Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
182 changes: 87 additions & 95 deletions docs/fase5/mesh-loading.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
> **Fase:** 5 — Transição Dimensional
> **Namespace:** `Caffeine::Assets`
> **Arquivo:** `src/assets/MeshLoader.hpp`
> **Status:** 📅 Planejado
> **Status:** ✅ Implementado
> **RFs:** RF5.2, RF5.3

---
Expand All @@ -14,114 +14,103 @@ Carregamento de malhas 3D nos formatos `.obj` e `.gltf`, convertendo para o form

---

## API Planejada
## API Implementada

```cpp
namespace Caffeine::Assets {

// ============================================================================
// @brief Formato de vértice 3D (48 bytes — SIMD-aligned a 32 bytes com padding).
//
// offset 0: Vec3 position (12 bytes)
// offset 12: Vec3 normal (12 bytes)
// offset 24: Vec2 texcoord ( 8 bytes)
// offset 32: Vec4 tangent (16 bytes) — para normal mapping
// TOTAL: 48 bytes (align 16)
// ============================================================================
struct Color {
f32 r, g, b, a = 1.0f;
static Color white();
static Color black();
};

struct Rect3D {
Vec3 min, max;
Vec3 center() const;
Vec3 extents() const;
};

struct Vertex3D {
Vec3 position;
Vec3 normal;
Vec2 texcoord;
Vec4 tangent; // xyz = tangent, w = bitangent sign
Vec4 tangent;
};

// ============================================================================
// @brief Sub-mesh — parte de uma mesh com material próprio.
// ============================================================================
struct SubMesh {
u32 indexOffset; // primeiro índice no index buffer
u32 indexCount;
u32 materialIndex;
u32 indexOffset = 0;
u32 indexCount = 0;
u32 materialIndex = 0;
FixedString<64> name;
};

// ============================================================================
// @brief Mesh 3D carregada.
// ============================================================================
struct Mesh3D {
std::vector<Vertex3D> vertices;
std::vector<u32> indices;
std::vector<SubMesh> subMeshes;
Rect3D bounds; // AABB para frustum culling
u32 lodCount = 1; // LODs futuros

// GPU buffers (criados após upload)
std::vector<u32> indices;
std::vector<SubMesh> subMeshes;
Rect3D bounds;
u32 lodCount = 1;
#ifdef CF_HAS_SDL3
RHI::Buffer* vertexBuffer = nullptr;
RHI::Buffer* indexBuffer = nullptr;
RHI::Buffer* indexBuffer = nullptr;
#endif
};

// ============================================================================
// @brief Material 3D.
// ============================================================================
struct Material3D {
FixedString<64> name;
RHI::Texture* albedoTexture = nullptr;
RHI::Texture* normalTexture = nullptr;
RHI::Texture* roughnessTexture = nullptr;
Color albedoColor = Color::WHITE;
f32 roughness = 0.5f;
f32 metallic = 0.0f;
RHI::Shader* shader = nullptr;
FixedString<64> name;
Color albedoColor = Color::white();
f32 roughness = 0.5f;
f32 metallic = 0.0f;

#ifdef CF_HAS_SDL3
RHI::Texture* albedoTexture = nullptr;
RHI::Texture* normalTexture = nullptr;
RHI::Texture* roughnessTexture = nullptr;
RHI::Shader* shader = nullptr;
#endif
};

struct MeshRenderer {
FixedString<128> meshPath;
Mesh3D* mesh = nullptr;
Material3D* material = nullptr;
bool castShadows = true;
bool receiveShadows = true;
};

// ============================================================================
// @brief Loader de meshes .obj e .gltf.
//
// Fluxo:
// 1. Carregar .obj/.gltf → Mesh3D (vertices, indices)
// 2. Converter para .caf (AssetType::Mesh) via Asset Pipeline (Fase 6)
// 3. Em runtime: BlobLoader carrega .caf → upload GPU
// ============================================================================
class MeshLoader {
public:
explicit MeshLoader(RHI::RenderDevice* device,
AssetManager* assets);

// Carregamento assíncrono (preferido em runtime)
AssetHandle<Mesh3D> loadAsync(const char* cafPath);

// Carregamento síncrono (para ferramentas, não para runtime)
Mesh3D* loadOBJ(const char* objPath);
Mesh3D* loadGLTF(const char* gltfPath);

// Upload da mesh para GPU (chama após loadOBJ/loadGLTF)
MeshLoader() = default;
#ifdef CF_HAS_SDL3
explicit MeshLoader(RHI::RenderDevice* device);
#endif

static Mesh3D* fromMemory(const Vertex3D* verts, u32 vertCount,
const u32* indices, u32 indexCount);
static Mesh3D* parseOBJ(const char* src, usize srcLen);
Mesh3D* loadOBJ(const char* path);
#ifdef CF_HAS_SDL3
void uploadToGPU(Mesh3D* mesh);
#endif
};

private:
RHI::RenderDevice* m_device;
AssetManager* m_assets;
class MeshSystem : public ECS::ISystem {
public:
void onUpdate(ECS::World& world, f32 dt) override;
};

} // namespace Caffeine::Assets

// ============================================================================
// @brief Sistema de renderização de meshes 3D.
// ============================================================================
namespace Caffeine::Render {
namespace Caffeine::ECS {

class MeshSystem : public ECS::ISystem {
public:
void update(ECS::World& world, f64 dt) override;
i32 priority() const override { return 900; } // antes do render flush
const char* name() const override { return "MeshSystem"; }

private:
void submitMesh(const Assets::Mesh3D& mesh,
const Math::Mat4& worldMatrix,
RHI::CommandBuffer* cmd);
};
struct Position3D { Vec3 position; };
struct Rotation3D { Vec4 quaternion = Vec4(0.0f, 0.0f, 0.0f, 1.0f); };
struct Scale3D { Vec3 scale = Vec3(1.0f, 1.0f, 1.0f); };

} // namespace Caffeine::Render
} // namespace Caffeine::ECS
```

---
Expand Down Expand Up @@ -177,33 +166,36 @@ struct MeshRenderer {
## Exemplos de Uso

```cpp
// ── Carregar mesh ─────────────────────────────────────────────
Caffeine::Assets::MeshLoader meshLoader(&device, &assets);
auto meshHandle = meshLoader.loadAsync("meshes/player.caf");
// ── Carregar mesh OBJ ─────────────────────────────────────────
Caffeine::Assets::MeshLoader meshLoader;
Mesh3D* mesh = meshLoader.loadOBJ("models/player.obj");

// ── Criar entidade 3D ─────────────────────────────────────────
Entity player3D = world.create("Player3D");
world.add<Position3D>(player3D, {0, 0, 0});
Entity player3D = world.create();
world.add<Position3D>(player3D, {Vec3(0.0f, 0.0f, 0.0f)});
world.add<Rotation3D>(player3D);
world.add<Scale3D>(player3D, {1, 1, 1});
world.add<MeshRenderer>(player3D, { .meshPath = "meshes/player.caf" });

// ── Shader customizado ────────────────────────────────────────
auto* shaderSys = world.getSystem<ShaderSystem>();
auto* vert = shaderSys->loadVertex("shaders/pbr.vert.spv");
auto* frag = shaderSys->loadFragment("shaders/pbr.frag.spv");
auto* pipeline = shaderSys->createPipeline(vert, frag, {});
// Assign ao material da mesh
world.add<Scale3D>(player3D, {Vec3(1.0f, 1.0f, 1.0f)});
world.add<MeshRenderer>(player3D, MeshRenderer{});

// ── Sistema de renderização ───────────────────────────────────
MeshSystem meshSystem;
meshSystem.onUpdate(world, 0.016f);
```

---

## Critério de Aceitação

- [ ] Mesh .obj com 10K triângulos carregada e renderizada corretamente
- [ ] Mesh .gltf com materiais PBR básicos funcional
- [ ] 60fps com mesh + shader customizado
- [ ] Vertex buffer alinhado a 32 bytes para SIMD
- [x] Vertex3D struct definido (position, normal, texcoord, tangent)
- [x] Mesh3D struct com vertices, indices, subMeshes, bounds
- [x] MeshLoader::fromMemory cria mesh de vértices e índices
- [x] MeshLoader::parseOBJ lê formato .obj (v, vt, vn, f)
- [x] MeshLoader::loadOBJ carrega arquivo do disco
- [x] MeshSystem::onUpdate integrado com ECS
- [x] Componentes 3D (Position3D, Rotation3D, Scale3D)
- [x] 22+ testes cobrindo todos os componentes
- [x] Compila sem SDL3 (CPU-only path)
- [x] GPU upload guardado com #ifdef CF_HAS_SDL3

---

Expand Down
7 changes: 6 additions & 1 deletion src/Caffeine.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,4 +75,9 @@

// Animation
#include "animation/AnimationComponents.hpp"
#include "animation/AnimationSystem.hpp"
#include "animation/AnimationSystem.hpp"

// Mesh (3D)
#include "ecs/Components3D.hpp"
#include "assets/MeshTypes.hpp"
#include "assets/MeshLoader.hpp"
Loading
Loading