Entity does not own mesh.
This commit is contained in:
@@ -6,7 +6,6 @@
|
|||||||
# Sources
|
# Sources
|
||||||
target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
||||||
PUBLIC
|
PUBLIC
|
||||||
assetmeshloader.c
|
|
||||||
assettextureloader.c
|
assettextureloader.c
|
||||||
assettilesetloader.c
|
assettilesetloader.c
|
||||||
)
|
)
|
||||||
@@ -1,122 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2026 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "assetmeshloader.h"
|
|
||||||
#include "assert/assert.h"
|
|
||||||
#include "util/memory.h"
|
|
||||||
#define FAST_OBJ_IMPLEMENTATION
|
|
||||||
#include "fast_obj.h"
|
|
||||||
|
|
||||||
// Wraps assetfile_t for fast_obj callbacks. Only the primary OBJ file is
|
|
||||||
// opened through the ZIP; returning NULL for any secondary path (e.g. .mtl)
|
|
||||||
// causes fast_obj to skip material loading, which is fine for our use case.
|
|
||||||
static void* meshFileOpen(const char *path, void *user_data) {
|
|
||||||
assetfile_t *file = (assetfile_t *)user_data;
|
|
||||||
if(file->zipFile != NULL) return NULL;
|
|
||||||
errorret_t ret = assetFileOpen(file);
|
|
||||||
if(ret.code != ERROR_OK) return NULL;
|
|
||||||
return file;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void meshFileClose(void *handle, void *user_data) {
|
|
||||||
if(handle == NULL) return;
|
|
||||||
assetfile_t *file = (assetfile_t *)handle;
|
|
||||||
errorCatch(assetFileClose(file));
|
|
||||||
}
|
|
||||||
|
|
||||||
static size_t meshFileRead(void *handle, void *dst, size_t bytes, void *user_data) {
|
|
||||||
if(handle == NULL) return 0;
|
|
||||||
assetfile_t *file = (assetfile_t *)handle;
|
|
||||||
errorret_t ret = assetFileRead(file, dst, bytes);
|
|
||||||
if(ret.code != ERROR_OK) return 0;
|
|
||||||
return (size_t)file->lastRead;
|
|
||||||
}
|
|
||||||
|
|
||||||
static unsigned long meshFileSize(void *handle, void *user_data) {
|
|
||||||
if(handle == NULL) return 0;
|
|
||||||
assetfile_t *file = (assetfile_t *)handle;
|
|
||||||
return (unsigned long)file->size;
|
|
||||||
}
|
|
||||||
|
|
||||||
errorret_t assetMeshLoader(assetfile_t *file) {
|
|
||||||
assertNotNull(file, "Asset file cannot be NULL.");
|
|
||||||
assertNotNull(file->output, "Asset file output cannot be NULL.");
|
|
||||||
|
|
||||||
fastObjCallbacks callbacks = {
|
|
||||||
.file_open = meshFileOpen,
|
|
||||||
.file_close = meshFileClose,
|
|
||||||
.file_read = meshFileRead,
|
|
||||||
.file_size = meshFileSize,
|
|
||||||
};
|
|
||||||
|
|
||||||
fastObjMesh *obj = fast_obj_read_with_callbacks(file->filename, &callbacks, file);
|
|
||||||
if(obj == NULL) {
|
|
||||||
errorThrow("Failed to parse OBJ: %s", file->filename);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Count output vertices, triangulating any polygons via fan decomposition.
|
|
||||||
int32_t vertexCount = 0;
|
|
||||||
for(unsigned int i = 0; i < obj->face_count; i++) {
|
|
||||||
if(obj->face_vertices[i] >= 3) {
|
|
||||||
vertexCount += (int32_t)(obj->face_vertices[i] - 2) * 3;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(vertexCount == 0) {
|
|
||||||
fast_obj_destroy(obj);
|
|
||||||
errorThrow("OBJ has no valid faces: %s", file->filename);
|
|
||||||
}
|
|
||||||
|
|
||||||
meshvertex_t *vertices = (meshvertex_t *)memoryAllocate(
|
|
||||||
sizeof(meshvertex_t) * vertexCount
|
|
||||||
);
|
|
||||||
memoryZero(vertices, sizeof(meshvertex_t) * vertexCount);
|
|
||||||
|
|
||||||
int32_t vi = 0;
|
|
||||||
fastObjIndex *idx = obj->indices;
|
|
||||||
for(unsigned int fi = 0; fi < obj->face_count; fi++) {
|
|
||||||
unsigned int fv = obj->face_vertices[fi];
|
|
||||||
// Fan triangulation: anchor at idx[0], triangle (0, j, j+1).
|
|
||||||
for(unsigned int j = 1; j + 1 < fv; j++) {
|
|
||||||
fastObjIndex corners[3] = { idx[0], idx[j], idx[j + 1] };
|
|
||||||
for(int c = 0; c < 3; c++) {
|
|
||||||
vertices[vi].color = COLOR_WHITE_4B;
|
|
||||||
|
|
||||||
unsigned int p = corners[c].p;
|
|
||||||
if(p > 0 && p < obj->position_count) {
|
|
||||||
vertices[vi].pos[0] = obj->positions[p * 3 + 0];
|
|
||||||
vertices[vi].pos[1] = obj->positions[p * 3 + 1];
|
|
||||||
vertices[vi].pos[2] = obj->positions[p * 3 + 2];
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int t = corners[c].t;
|
|
||||||
if(t > 0 && t < obj->texcoord_count) {
|
|
||||||
vertices[vi].uv[0] = obj->texcoords[t * 2 + 0];
|
|
||||||
vertices[vi].uv[1] = obj->texcoords[t * 2 + 1];
|
|
||||||
}
|
|
||||||
|
|
||||||
vi++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
idx += fv;
|
|
||||||
}
|
|
||||||
|
|
||||||
fast_obj_destroy(obj);
|
|
||||||
|
|
||||||
errorret_t ret = meshInit(
|
|
||||||
(mesh_t *)file->output,
|
|
||||||
MESH_PRIMITIVE_TYPE_TRIANGLES,
|
|
||||||
vertexCount,
|
|
||||||
vertices
|
|
||||||
);
|
|
||||||
memoryFree(vertices);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
errorret_t assetMeshLoad(const char_t *path, mesh_t *out) {
|
|
||||||
return assetLoad(path, assetMeshLoader, NULL, out);
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2026 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "asset/asset.h"
|
|
||||||
#include "display/mesh/mesh.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads a mesh from an OBJ asset file.
|
|
||||||
*
|
|
||||||
* @param file Asset file to load from.
|
|
||||||
* @return Any error that occurs during loading.
|
|
||||||
*/
|
|
||||||
errorret_t assetMeshLoader(assetfile_t *file);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads a mesh from the specified OBJ asset path.
|
|
||||||
*
|
|
||||||
* @param path Path to the OBJ asset.
|
|
||||||
* @param out Output mesh to load into.
|
|
||||||
* @return Any error that occurs during loading.
|
|
||||||
*/
|
|
||||||
errorret_t assetMeshLoad(const char_t *path, mesh_t *out);
|
|
||||||
+16
-18
@@ -18,17 +18,16 @@
|
|||||||
#include "assert/assert.h"
|
#include "assert/assert.h"
|
||||||
#include "entity/entitymanager.h"
|
#include "entity/entitymanager.h"
|
||||||
#include "game/game.h"
|
#include "game/game.h"
|
||||||
|
#include "display/mesh/quad.h"
|
||||||
#include "asset/loader/display/assetmeshloader.h"
|
|
||||||
|
|
||||||
engine_t ENGINE;
|
engine_t ENGINE;
|
||||||
texture_t TEXTURE;
|
// texture_t TEXTURE;
|
||||||
color_t TEXTURE_COLORS[] = {
|
// color_t TEXTURE_COLORS[] = {
|
||||||
COLOR_RED, COLOR_GREEN, COLOR_MAGENTA, COLOR_CYAN,
|
// COLOR_RED, COLOR_GREEN, COLOR_MAGENTA, COLOR_CYAN,
|
||||||
COLOR_BLUE, COLOR_WHITE, COLOR_YELLOW, COLOR_BLACK,
|
// COLOR_BLUE, COLOR_WHITE, COLOR_YELLOW, COLOR_BLACK,
|
||||||
COLOR_CYAN, COLOR_MAGENTA, COLOR_GREEN, COLOR_RED,
|
// COLOR_CYAN, COLOR_MAGENTA, COLOR_GREEN, COLOR_RED,
|
||||||
COLOR_WHITE, COLOR_BLUE, COLOR_BLACK, COLOR_YELLOW
|
// COLOR_WHITE, COLOR_BLUE, COLOR_BLACK, COLOR_YELLOW
|
||||||
};
|
// };
|
||||||
entityid_t ent1;
|
entityid_t ent1;
|
||||||
componentid_t ent1Pos;
|
componentid_t ent1Pos;
|
||||||
componentid_t ent1Mesh;
|
componentid_t ent1Mesh;
|
||||||
@@ -60,26 +59,25 @@ errorret_t engineInit(const int32_t argc, const char_t **argv) {
|
|||||||
camPos,
|
camPos,
|
||||||
(vec3){ 0.0f, 0.0f, 0.0f },
|
(vec3){ 0.0f, 0.0f, 0.0f },
|
||||||
(vec3){ 0.0f, 1.0f, 0.0f },
|
(vec3){ 0.0f, 1.0f, 0.0f },
|
||||||
(vec3){ 300.0f, 300.0f, 300.0f }
|
(vec3){ 3.0f, 3.0f, 3.0f }
|
||||||
);
|
);
|
||||||
componentid_t camCam = entityAddComponent(cam, COMPONENT_TYPE_CAMERA);
|
componentid_t camCam = entityAddComponent(cam, COMPONENT_TYPE_CAMERA);
|
||||||
entityCameraSetZFar(cam, camCam, 5000.0f);
|
entityCameraSetZFar(cam, camCam, 100.0f);
|
||||||
|
|
||||||
ent1 = entityManagerAdd();
|
ent1 = entityManagerAdd();
|
||||||
ent1Pos = entityAddComponent(ent1, COMPONENT_TYPE_POSITION);
|
ent1Pos = entityAddComponent(ent1, COMPONENT_TYPE_POSITION);
|
||||||
ent1Mesh = entityAddComponent(ent1, COMPONENT_TYPE_MESH);
|
ent1Mesh = entityAddComponent(ent1, COMPONENT_TYPE_MESH);
|
||||||
ent1Mat = entityAddComponent(ent1, COMPONENT_TYPE_MATERIAL);
|
ent1Mat = entityAddComponent(ent1, COMPONENT_TYPE_MATERIAL);
|
||||||
|
|
||||||
textureInit(&TEXTURE, 4, 4, TEXTURE_FORMAT_RGBA, (texturedata_t){
|
// textureInit(&TEXTURE, 4, 4, TEXTURE_FORMAT_RGBA, (texturedata_t){
|
||||||
.rgbaColors = TEXTURE_COLORS
|
// .rgbaColors = TEXTURE_COLORS
|
||||||
});
|
// });
|
||||||
|
|
||||||
mesh_t *mesh = entityMeshGetMesh(ent1, ent1Mesh);
|
entityMeshSetMesh(ent1, ent1Mesh, &QUAD_MESH_SIMPLE);
|
||||||
errorChain(assetMeshLoad("test/test.obj", mesh));
|
|
||||||
|
|
||||||
shadermaterial_t *mat = entityMaterialGetShaderMaterial(ent1, ent1Mat);
|
shadermaterial_t *mat = entityMaterialGetShaderMaterial(ent1, ent1Mat);
|
||||||
mat->unlit.color = COLOR_BLACK;
|
mat->unlit.color = COLOR_MAGENTA;
|
||||||
mat->unlit.texture = &TEXTURE;
|
// mat->unlit.texture = &TEXTURE;
|
||||||
|
|
||||||
// EOF
|
// EOF
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ void entityMeshInit(
|
|||||||
entitymesh_t *comp = componentGetData(
|
entitymesh_t *comp = componentGetData(
|
||||||
entityId, componentId, COMPONENT_TYPE_MESH
|
entityId, componentId, COMPONENT_TYPE_MESH
|
||||||
);
|
);
|
||||||
comp->mesh.vertexCount = 0;
|
comp->mesh = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
mesh_t * entityMeshGetMesh(
|
mesh_t * entityMeshGetMesh(
|
||||||
@@ -24,18 +24,16 @@ mesh_t * entityMeshGetMesh(
|
|||||||
entitymesh_t *comp = componentGetData(
|
entitymesh_t *comp = componentGetData(
|
||||||
entityId, componentId, COMPONENT_TYPE_MESH
|
entityId, componentId, COMPONENT_TYPE_MESH
|
||||||
);
|
);
|
||||||
return &comp->mesh;
|
return comp->mesh;
|
||||||
}
|
}
|
||||||
|
|
||||||
void entityMeshDispose(
|
void entityMeshSetMesh(
|
||||||
const entityid_t entityId,
|
const entityid_t entityId,
|
||||||
const componentid_t componentId
|
const componentid_t componentId,
|
||||||
|
mesh_t *mesh
|
||||||
) {
|
) {
|
||||||
entitymesh_t *comp = componentGetData(
|
entitymesh_t *comp = componentGetData(
|
||||||
entityId, componentId, COMPONENT_TYPE_MESH
|
entityId, componentId, COMPONENT_TYPE_MESH
|
||||||
);
|
);
|
||||||
if(comp->mesh.vertexCount > 0) {
|
comp->mesh = mesh;
|
||||||
meshDispose(&comp->mesh);
|
|
||||||
comp->mesh.vertexCount = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
#include "display/mesh/mesh.h"
|
#include "display/mesh/mesh.h"
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
mesh_t mesh;
|
mesh_t *mesh;
|
||||||
} entitymesh_t;
|
} entitymesh_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -37,13 +37,14 @@ mesh_t * entityMeshGetMesh(
|
|||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Disposes of the entity mesh component, freeing GPU resources if the mesh
|
* Sets the mesh associated with the entity mesh component.
|
||||||
* was initialized.
|
|
||||||
*
|
*
|
||||||
* @param entityId The entity ID.
|
* @param entityId The entity ID.
|
||||||
* @param componentId The component ID.
|
* @param componentId The component ID.
|
||||||
|
* @param mesh A pointer to the mesh to associate with the entity mesh component.
|
||||||
*/
|
*/
|
||||||
void entityMeshDispose(
|
void entityMeshSetMesh(
|
||||||
const entityid_t entityId,
|
const entityid_t entityId,
|
||||||
const componentid_t componentId
|
const componentid_t componentId,
|
||||||
|
mesh_t *mesh
|
||||||
);
|
);
|
||||||
@@ -12,5 +12,5 @@
|
|||||||
|
|
||||||
X(POSITION, entityposition_t, position, entityPositionInit, NULL)
|
X(POSITION, entityposition_t, position, entityPositionInit, NULL)
|
||||||
X(CAMERA, entitycamera_t, camera, entityCameraInit, NULL)
|
X(CAMERA, entitycamera_t, camera, entityCameraInit, NULL)
|
||||||
X(MESH, entitymesh_t, mesh, entityMeshInit, entityMeshDispose)
|
X(MESH, entitymesh_t, mesh, entityMeshInit, NULL)
|
||||||
X(MATERIAL, entitymaterial_t, material, entityMaterialInit, NULL)
|
X(MATERIAL, entitymaterial_t, material, entityMaterialInit, NULL)
|
||||||
@@ -64,12 +64,19 @@ errorret_t sceneRender(void) {
|
|||||||
entityCameraGetProjection(camEnt, camComp, proj);
|
entityCameraGetProjection(camEnt, camComp, proj);
|
||||||
entityPositionGetTransform(camEnt, camPos, view);
|
entityPositionGetTransform(camEnt, camPos, view);
|
||||||
|
|
||||||
|
|
||||||
// For each mesh.
|
// For each mesh.
|
||||||
for(entityid_t meshIndex = 0; meshIndex < meshCount; meshIndex++) {
|
for(entityid_t meshIndex = 0; meshIndex < meshCount; meshIndex++) {
|
||||||
entityid_t meshEnt = meshEnts[meshIndex];
|
entityid_t meshEnt = meshEnts[meshIndex];
|
||||||
|
|
||||||
componentid_t meshComp = meshComps[meshIndex];
|
componentid_t meshComp = meshComps[meshIndex];
|
||||||
componentid_t meshPos = entityGetComponent(meshEnt, COMPONENT_TYPE_POSITION);
|
mesh_t *mesh = entityMeshGetMesh(meshEnt, meshComp);
|
||||||
|
if(mesh == NULL) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
componentid_t meshPos = entityGetComponent(
|
||||||
|
meshEnt, COMPONENT_TYPE_POSITION
|
||||||
|
);
|
||||||
if(meshPos == 0xFF) {
|
if(meshPos == 0xFF) {
|
||||||
logError("Mesh entity without entity position found\n");
|
logError("Mesh entity without entity position found\n");
|
||||||
continue;
|
continue;
|
||||||
@@ -99,9 +106,6 @@ errorret_t sceneRender(void) {
|
|||||||
errorChain(shaderSetMatrix(shader, SHADER_UNLIT_VIEW, view));
|
errorChain(shaderSetMatrix(shader, SHADER_UNLIT_VIEW, view));
|
||||||
errorChain(shaderSetMatrix(shader, SHADER_UNLIT_MODEL, model));
|
errorChain(shaderSetMatrix(shader, SHADER_UNLIT_MODEL, model));
|
||||||
errorChain(shaderSetMaterial(shader, material));
|
errorChain(shaderSetMaterial(shader, material));
|
||||||
|
|
||||||
// Mesh
|
|
||||||
mesh_t *mesh = entityMeshGetMesh(meshEnt, meshComp);
|
|
||||||
errorChain(meshDraw(mesh, 0, -1));
|
errorChain(meshDraw(mesh, 0, -1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user