Skip to content

Commit 9f2a8dc

Browse files
committed
feat(model): enhance model handling with combined mesh generation and draw command struct
1 parent eb35a92 commit 9f2a8dc

6 files changed

Lines changed: 86 additions & 45 deletions

File tree

src/common/mesh/mesh.hpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ namespace our {
2222
// We need to remember the number of elements that will be draw by glDrawElements
2323
GLsizei elementCount;
2424
GLsizei vertexCount;
25+
std::vector<Vertex> vertices; // Store vertices for potential future use (e.g., collision, CPU-side processing)
26+
std::vector<unsigned int> indices; // Store indices for potential future use
2527

2628
public:
2729
GLsizei getVertexCount() const {
@@ -90,6 +92,8 @@ namespace our {
9092

9193
elementCount = static_cast<GLsizei>(elements.size());
9294
vertexCount = static_cast<GLsizei>(vertices.size());
95+
this->vertices = vertices;
96+
this->indices = elements;
9397
}
9498

9599
// this function should render the mesh
@@ -105,6 +109,14 @@ namespace our {
105109
glDeleteVertexArrays(1, &VAO);
106110
}
107111

112+
std::vector<Vertex> getVertices() const {
113+
return vertices;
114+
}
115+
116+
std::vector<unsigned int> getIndices() const {
117+
return indices;
118+
}
119+
108120
Mesh(Mesh const&) = delete;
109121
Mesh& operator=(Mesh const&) = delete;
110122
};

src/common/model/model.cpp

Lines changed: 45 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -13,23 +13,20 @@
1313
namespace our {
1414
int Model::ID_COUNTER = 0;
1515

16-
void Model::draw(const glm::mat4& VP, const glm::mat4& modelMatrix, const std::vector<our::LightRenderData>& lights,
17-
const glm::vec3& cameraPosition) const {
18-
glm::mat4 MVP = VP * modelMatrix;
16+
void Model::generateDrawCommands(std::vector<RenderCommand>& modelCommands,
17+
std::vector<RenderCommand>& transparentCommands,
18+
const glm::mat4& modelMatrix) const {
1919
for (const auto& submesh : submeshes) {
20-
// this dynamic cast will always pass
21-
// as we only make LitMaterials when loading the model
22-
if (LitMaterial* litMaterial = dynamic_cast<LitMaterial*>(submesh->material)) {
23-
litMaterial->setup(lights);
24-
litMaterial->shader->set("cameraPos", cameraPosition);
25-
litMaterial->shader->set("model", modelMatrix * submesh->transform);
26-
litMaterial->shader->set("transform", MVP * submesh->transform);
27-
} else if (submesh->material) {
28-
submesh->material->setup();
29-
submesh->material->shader->set("model", modelMatrix * submesh->transform);
30-
submesh->material->shader->set("transform", MVP * submesh->transform);
20+
RenderCommand cmd;
21+
cmd.localToWorld = modelMatrix * submesh->transform;
22+
cmd.center = glm::vec3(cmd.localToWorld * glm::vec4(0, 0, 0, 1));
23+
cmd.mesh = submesh->mesh;
24+
cmd.material = submesh->material;
25+
if (submesh->material->transparent) {
26+
transparentCommands.push_back(cmd);
27+
} else {
28+
modelCommands.push_back(cmd);
3129
}
32-
submesh->mesh->draw();
3330
}
3431
}
3532

@@ -69,6 +66,7 @@ namespace our {
6966
loadMaterialsFromScene(scene);
7067
glm::mat4 identity(1.0f);
7168
processNode(scene->mRootNode, scene, identity);
69+
generateCombinedMesh();
7270
}
7371

7472
void Model::processNode(aiNode* node, const aiScene* scene, glm::mat4& parentTransform) {
@@ -263,7 +261,8 @@ namespace our {
263261
material->transparent = material->tint.a < 0.999f;
264262

265263
if (material->transparent) {
266-
material->pipelineState.depthTesting.enabled = GL_FALSE; // disable depth writing for transparent materials
264+
material->pipelineState.depthTesting.enabled = GL_TRUE; // disable depth writing for transparent materials
265+
material->pipelineState.depthMask = false;
267266
material->pipelineState.blending.enabled = true;
268267
material->pipelineState.blending.sourceFactor = GL_SRC_ALPHA;
269268
int blendFunc = 0;
@@ -364,6 +363,36 @@ namespace our {
364363
}
365364
}
366365

366+
void Model::generateCombinedMesh() {
367+
std::vector<Vertex> combinedVertices;
368+
std::vector<unsigned int> combinedIndices;
369+
370+
for (const auto& submesh : submeshes) {
371+
unsigned int indexOffset = static_cast<unsigned int>(combinedVertices.size());
372+
373+
// Transform each vertex into model space using the submesh's local transform
374+
for (const Vertex& v : submesh->mesh->getVertices()) {
375+
Vertex transformed = v;
376+
377+
// Apply the submesh transform to position
378+
glm::vec4 worldPos = submesh->transform * glm::vec4(v.position, 1.0f);
379+
transformed.position = glm::vec3(worldPos);
380+
381+
// Transform normal using the normal matrix (inverse transpose)
382+
glm::mat3 normalMatrix = glm::mat3(glm::transpose(glm::inverse(submesh->transform)));
383+
transformed.normal = glm::normalize(normalMatrix * v.normal);
384+
385+
combinedVertices.push_back(transformed);
386+
}
387+
388+
// Re-base indices so they point into the combined vertex buffer
389+
for (unsigned int idx : submesh->mesh->getIndices()) {
390+
combinedIndices.push_back(idx + indexOffset);
391+
}
392+
}
393+
combinedMesh = new Mesh(combinedVertices, combinedIndices);
394+
}
395+
367396
Model::~Model() {
368397
for (auto& submesh : submeshes) {
369398
delete submesh->mesh;

src/common/model/model.hpp

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,28 +12,33 @@
1212

1313
#include "components/mesh-renderer.hpp"
1414
#include "material/material.hpp"
15+
#include "systems/render-command.hpp"
1516

1617
namespace our {
17-
1818
class Model {
1919
static int ID_COUNTER; // this is used to give each model a unique ID for caching purposes
2020
int id; // the unique ID of this model
2121
std::string modelDirectory;
22-
std::vector<MeshRendererComponent*> submeshes;
22+
std::vector<MeshRendererComponent*> submeshes; // submeshes with opaque materials (rendered in the first pass)
2323

2424
void processNode(aiNode* node, const aiScene* scene, glm::mat4& parentTransform);
2525
MeshRendererComponent* processMesh(aiMesh* mesh, const aiScene* scene);
2626
void processVertexBoneData(aiMesh* mesh, std::vector<Vertex>& vertices);
2727
void loadMaterialsFromScene(const aiScene* scene);
2828
LitMaterial* loadMaterial(const aiScene* scene, const aiMaterial* mat);
2929
Texture2D* loadTextureFromMaterial(const aiScene* scene, const aiMaterial* mat, aiTextureType type);
30-
30+
void generateCombinedMesh(); // will be used for collision detection and other non-rendering purposes
31+
Mesh* combinedMesh; // a single mesh that combines all the submeshes of this model (used for collision
32+
// detection and other non-rendering purposes)
3133
public:
3234
Model() {
3335
id = ID_COUNTER++;
3436
};
35-
void draw(const glm::mat4& VP, const glm::mat4& modelMatrix, const std::vector<our::LightRenderData>& lights,
36-
const glm::vec3& cameraPosition) const;
37+
void generateDrawCommands(std::vector<RenderCommand>& modelCommands,
38+
std::vector<RenderCommand>& transparentCommands, const glm::mat4& modelMatrix) const;
39+
Mesh* getCombinedMesh() const {
40+
return combinedMesh;
41+
}
3742
~Model();
3843
void loadFromFile(const std::string& path);
3944
};

src/common/systems/forward-renderer.cpp

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include "forward-renderer.hpp"
22

3+
#include "../components/model-renderer.hpp"
34
#include "../mesh/mesh-utils.hpp"
45
#include "../texture/texture-utils.hpp"
56

@@ -131,7 +132,6 @@ namespace our {
131132
opaqueCommands.clear();
132133
transparentCommands.clear();
133134
sceneLights.clear();
134-
modelCommands.clear();
135135
for (auto entity : world->getEntities()) {
136136
// If we hadn't found a camera yet, we look for a camera in this entity
137137
if (!camera) camera = entity->getComponent<CameraComponent>();
@@ -169,11 +169,8 @@ namespace our {
169169
}
170170

171171
if (auto modelRenderer = entity->getComponent<ModelRendererComponent>(); modelRenderer) {
172-
RenderCommand command;
173-
command.localToWorld = modelRenderer->getOwner()->getLocalToWorldMatrix();
174-
command.center = glm::vec3(command.localToWorld * glm::vec4(0, 0, 0, 1));
175-
command.model = modelRenderer->model;
176-
modelCommands.push_back(command);
172+
glm::mat4 localToWorld = modelRenderer->getOwner()->getLocalToWorldMatrix();
173+
modelRenderer->model->generateDrawCommands(opaqueCommands, transparentCommands, localToWorld);
177174
}
178175
}
179176

@@ -241,10 +238,6 @@ namespace our {
241238
skySphere->draw();
242239
}
243240

244-
for (const RenderCommand& command : modelCommands) {
245-
command.model->draw(VP, command.localToWorld, sceneLights, cameraPosition);
246-
}
247-
248241
// draw all the transparent commands
249242
for (const RenderCommand& command : transparentCommands) {
250243
command.material->setup();

src/common/systems/forward-renderer.hpp

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,22 +8,10 @@
88
#include "../asset-loader.hpp"
99
#include "../components/camera.hpp"
1010
#include "../components/mesh-renderer.hpp"
11-
#include "../components/model-renderer.hpp"
1211
#include "../ecs/world.hpp"
12+
#include "render-command.hpp"
1313

1414
namespace our {
15-
16-
// The render command stores command that tells the renderer that it should draw
17-
// the given mesh at the given localToWorld matrix using the given material
18-
// The renderer will fill this struct using the mesh renderer components
19-
struct RenderCommand {
20-
glm::mat4 localToWorld;
21-
glm::vec3 center;
22-
Mesh* mesh;
23-
Material* material;
24-
Model* model;
25-
};
26-
2715
// A forward renderer is a renderer that draw the object final color directly to the framebuffer
2816
// In other words, the fragment shader in the material should output the color that we should see on the screen
2917
// This is different from more complex renderers that could draw intermediate data to a framebuffer before computing
@@ -36,7 +24,6 @@ namespace our {
3624
// reallocating them every frame
3725
std::vector<RenderCommand> opaqueCommands;
3826
std::vector<RenderCommand> transparentCommands;
39-
std::vector<RenderCommand> modelCommands;
4027
// Objects used for rendering a skybox
4128
Mesh* skySphere;
4229
TexturedMaterial* skyMaterial;
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#pragma once
2+
#include "material/material.hpp"
3+
#include "mesh/mesh.hpp"
4+
5+
namespace our {
6+
// The render command stores command that tells the renderer that it should draw
7+
// the given mesh at the given localToWorld matrix using the given material
8+
// The renderer will fill this struct using the mesh renderer components
9+
struct RenderCommand {
10+
glm::mat4 localToWorld;
11+
glm::vec3 center;
12+
Mesh* mesh;
13+
Material* material;
14+
};
15+
}; // namespace our

0 commit comments

Comments
 (0)