Files
sophia/engine/include/sophia/components.hpp
T
2026-09-26 02:03:11 -07:00

121 lines
3.7 KiB
C++

#pragma once
#include <glm/gtc/matrix_transform.hpp>
#include <iostream>
#include <ostream>
#include <sophia/handles.hpp>
#include <sophia/math.hpp>
#include <string>
namespace sophia {
struct TagComponent {
std::string tag; // entt::hashed_string ?
uint32_t mask = 0;
};
struct TransformComponent {
glm::vec3 position{0.f, 0.f, 0.f};
glm::quat rotation{1.f, 0.f, 0.f, 0.f};
glm::vec3 scale{1.f, 1.f, 1.f};
glm::mat4 mat4() const {
return glm::translate(glm::mat4{1.f}, position) * glm::mat4_cast(rotation) *
glm::scale(glm::mat4{1.f}, scale);
}
glm::mat3 normalMatrix() const {
return glm::mat3{glm::transpose(glm::inverse(mat4()))};
}
friend std::ostream& operator<<(std::ostream& os,
const TransformComponent& transform) {
os << "TransformComponent {\n"
<< " Position: (" << transform.position.x << ", "
<< transform.position.y << ", " << transform.position.z << ")\n"
<< " Rotation (w, x, y, z): (" << transform.rotation.w << ", "
<< transform.rotation.x << ", " << transform.rotation.y << ", "
<< transform.rotation.z << ")\n"
<< " Scale: (" << transform.scale.x << ", " << transform.scale.y << ", "
<< transform.scale.z << ")\n"
<< "}";
return os;
}
};
struct MeshComponent {
MeshHandle handle;
};
/**
* A textured quad in the XY plane, placed by the entity's TransformComponent.
* Only rotation around Z is meaningful.
*/
struct SpriteComponent {
// Invalid draws a solid quad in `color`.
TextureHandle texture = INVALID_HANDLE;
glm::vec4 color{1.f, 1.f, 1.f, 1.f};
// 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.
glm::vec2 pivot{0.5f, 0.5f};
// Region of the texture to draw, as normalized (u0, v0, u1, v1).
glm::vec4 uvRect{0.f, 0.f, 1.f, 1.f};
// Higher draws on top. Ties are broken by the transform's position.z.
s32 sortOrder = 0;
// Maps the unit quad (0, 0)-(1, 1) to world space: to the sprite's
// rectangle around its pivot, then by `transform`.
glm::mat4 getModel(const TransformComponent& transform) const {
return transform.mat4() *
glm::translate(glm::mat4{1.f}, glm::vec3{-pivot * size, 0.f}) *
glm::scale(glm::mat4{1.f}, glm::vec3{size, 1.f});
}
};
struct CameraComponent {
struct Orthographic {
float height = 10.f;
float nearPlane = -1.f;
float farPlane = 1.f;
};
struct Perspective {
float fov = glm::radians(60.f);
float nearPlane = 0.1f;
float farPlane = 100.f;
};
enum class Projection { Orthographic, Perspective };
Projection projection = Projection::Orthographic;
Orthographic ortho;
Perspective perspective;
glm::mat4 getProjection(float aspect) const {
if (projection == Projection::Perspective) {
return glm::perspective(perspective.fov, aspect, perspective.nearPlane,
perspective.farPlane);
}
float halfHeight = ortho.height / 2.f;
float halfWidth = halfHeight * aspect;
return glm::ortho(-halfWidth, halfWidth, -halfHeight, halfHeight,
ortho.nearPlane, ortho.farPlane);
}
// 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 {
glm::mat4 world = glm::translate(glm::mat4{1.f}, transform.position) *
glm::mat4_cast(transform.rotation);
return getProjection(aspect) * glm::inverse(world);
}
};
} // namespace sophia