Huge changes for 3d

This commit is contained in:
2026-09-30 18:53:16 -07:00
parent cd2e80cfca
commit b5b0eb9497
53 changed files with 1313 additions and 457 deletions
-2
View File
@@ -3,7 +3,6 @@
#include <cstdlib>
#include <iostream>
#include <memory>
#include <sophia/camera_2d.hpp>
#include <sophia/canvas.hpp>
#include <sophia/console/console_command_registry.hpp>
#include <sophia/frame_info.hpp>
@@ -53,7 +52,6 @@ class Application {
DebugUI& debugUI() { return this->_debugUI; }
Canvas canvas;
Camera2D camera;
std::vector<std::string> args;
ConsoleCommandRegistry consoleCommandRegistry;
-28
View File
@@ -1,28 +0,0 @@
#pragma once
#include <glm/gtc/matrix_transform.hpp>
#include <sophia/math.hpp>
namespace sophia {
class Camera2D {
public:
glm::vec2 position{0.f, 0.f};
void setProjection(float left, float right, float bottom, float top) {
projection = glm::ortho(left, right, bottom, top);
}
const glm::mat4& getProjection() const { return projection; }
glm::mat4 getView() const {
return glm::translate(glm::mat4(1.f), glm::vec3(-position, 0.f));
}
glm::mat4 getViewProjection() const { return projection * getView(); }
private:
glm::mat4 projection{1.f};
};
} // namespace sophia
+40 -17
View File
@@ -43,8 +43,15 @@ struct TransformComponent {
}
};
/**
* Draws a mesh, placed by the entity's TransformComponent. Opaque and unlit
* for now.
*/
struct MeshComponent {
MeshHandle handle;
// Multiplied with the mesh's vertex colors. Alpha is ignored. Stands in for
// a material until there are materials.
glm::vec4 color{1.f, 1.f, 1.f, 1.f};
};
/**
@@ -58,13 +65,17 @@ struct SpriteComponent {
// Size in world units, before the transform's scale.
glm::vec2 size{1.f, 1.f};
// Point on the sprite, from (0, 0) to (1, 1) across its size, that sits at
// the transform's position and that it rotates around.
// Point on the sprite, from (0, 0) at its bottom-left to (1, 1) at its
// top-right, that sits at the transform's position and that it rotates
// around.
glm::vec2 pivot{0.5f, 0.5f};
// Region of the texture to draw, as normalized (u0, v0, u1, v1).
// Region of the texture to draw, as normalized (u0, v0, u1, v1) with
// (0, 0) at the texture's top-left.
glm::vec4 uvRect{0.f, 0.f, 1.f, 1.f};
// Higher draws on top. Ties are broken by the transform's position.z.
// Higher draws on top, whatever the depth. Sprites with the same sortOrder
// draw back to front from the camera; the transform's position.z is depth
// like any other axis, not a layer.
s32 sortOrder = 0;
// Maps the unit quad (0, 0)-(1, 1) to world space: to the sprite's
@@ -79,8 +90,8 @@ struct SpriteComponent {
struct CameraComponent {
struct Orthographic {
float height = 10.f;
float nearPlane = -1.f;
float farPlane = 1.f;
float nearPlane = -1000.f;
float farPlane = 1000.f;
};
struct Perspective {
@@ -95,25 +106,37 @@ struct CameraComponent {
Orthographic ortho;
Perspective perspective;
// View -> clip space. World +Y is up on screen: Vulkan's clip space has +Y
// pointing down, so the projection flips it.
glm::mat4 getProjection(float aspect) const {
glm::mat4 result;
if (projection == Projection::Perspective) {
return glm::perspective(perspective.fov, aspect, perspective.nearPlane,
perspective.farPlane);
result = glm::perspective(perspective.fov, aspect, perspective.nearPlane,
perspective.farPlane);
} else {
float halfHeight = ortho.height / 2.f;
float halfWidth = halfHeight * aspect;
result = glm::ortho(-halfWidth, halfWidth, -halfHeight, halfHeight,
ortho.nearPlane, ortho.farPlane);
}
float halfHeight = ortho.height / 2.f;
float halfWidth = halfHeight * aspect;
return glm::ortho(-halfWidth, halfWidth, -halfHeight, halfHeight,
ortho.nearPlane, ortho.farPlane);
result[1][1] *= -1.f;
return result;
}
// World -> clip space for this camera placed by `transform`. The view
// ignores the transform's scale; zoom is the projection's job.
glm::mat4 getViewProjection(const TransformComponent& transform,
float aspect) const {
// World -> view space for this camera placed by `transform`. Ignores the
// transform's scale; zoom is the projection's job. The camera looks down
// view-space -Z.
static glm::mat4 getView(const TransformComponent& transform) {
glm::mat4 world = glm::translate(glm::mat4{1.f}, transform.position) *
glm::mat4_cast(transform.rotation);
return getProjection(aspect) * glm::inverse(world);
return glm::inverse(world);
}
// World -> clip space for this camera placed by `transform`.
glm::mat4 getViewProjection(const TransformComponent& transform,
float aspect) const {
return getProjection(aspect) * getView(transform);
}
};
-2
View File
@@ -1,6 +1,5 @@
#pragma once
#include <sophia/camera_2d.hpp>
#include <sophia/math.hpp>
#include <sophia/types.hpp>
@@ -11,7 +10,6 @@ struct FrameInfo {
double elapsedTime;
double deltaTime;
glm::vec2 currentFramebufferExtent;
const Camera2D& camera;
};
} // namespace sophia
+29
View File
@@ -0,0 +1,29 @@
#pragma once
#include <sophia/math.hpp>
#include <sophia/types.hpp>
#include <vector>
namespace sophia {
// Layout must match the per-vertex inputs in mesh.vert.
struct MeshVertex {
vec3 position{0.f, 0.f, 0.f};
vec3 normal{0.f, 0.f, 1.f};
// Texture coordinates, with (0, 0) at the texture's top-left.
vec2 uv{0.f, 0.f};
vec4 color{1.f, 1.f, 1.f, 1.f};
};
/**
* Geometry on the CPU, uploaded with `resources().create<Mesh>(data)`.
* Triangles wind counter-clockwise when seen from the front; back faces are
* culled. See primitives.hpp for ready-made shapes.
*/
struct MeshData {
std::vector<MeshVertex> vertices;
// Empty draws the vertices in order, three per triangle.
std::vector<u32> indices;
};
} // namespace sophia
+18
View File
@@ -0,0 +1,18 @@
#pragma once
#include <sophia/math.hpp>
#include <sophia/mesh_data.hpp>
/**
* Ready-made shapes, centered on the origin. They return MeshData rather than
* a mesh so they can be adjusted before uploading:
*
* MeshHandle cube = resources().create<Mesh>(primitives::cube());
*/
namespace sophia::primitives {
// An axis-aligned cube `size` across. Each face has its own four vertices so
// it gets its own normal and UVs.
MeshData cube(float size = 1.f, vec4 color = vec4{1.f});
} // namespace sophia::primitives
@@ -1,8 +1,8 @@
#pragma once
#include <sophia/canvas.hpp>
#include <sophia/handles.hpp>
#include <sophia/math.hpp>
#include <sophia/mesh_data.hpp>
#include <sophia/types.hpp>
#include <string>
#include <type_traits>
@@ -40,9 +40,7 @@ class ResourceRegistryRef {
explicit ResourceRegistryRef(ResourceRegistry& registry)
: registry{&registry} {}
MeshHandle createResource(std::type_identity<Mesh>,
std::vector<CanvasVertex> vertices,
std::vector<u32> indices = {});
MeshHandle createResource(std::type_identity<Mesh>, const MeshData& data);
RenderTargetHandle createResource(std::type_identity<RenderTarget>,
u32 width = 1, u32 height = 1);
+1
View File
@@ -21,6 +21,7 @@ struct SceneView {
INVALID_HANDLE; // null implies render to swapchain
Entity camera; // null implies use scene's active camera
// TODO(#76): not used yet; every view renders to its whole target.
vec4 viewport{0.0f, 0.0f, 1.0f, 1.0f};
vec4 clearColor{0.f, 0.f, 0.f, 1.f};