STL Loader

This commit is contained in:
2026-04-13 11:41:51 -05:00
parent 2b9ee8f721
commit c8abd374fe
8 changed files with 198 additions and 47 deletions
-20
View File
@@ -1,20 +0,0 @@
# Copyright (c) 2026 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
include(FetchContent)
if(NOT TARGET fast_obj)
FetchContent_Declare(
fast_obj
GIT_REPOSITORY https://github.com/thisistherk/fast_obj.git
GIT_TAG master
)
FetchContent_MakeAvailable(fast_obj)
endif()
set(fast_obj_FOUND TRUE)
set(FAST_OBJ_INCLUDE_DIRS "${fast_obj_SOURCE_DIR}")
set(FAST_OBJ_LIBRARIES fast_obj)
mark_as_advanced(FAST_OBJ_INCLUDE_DIRS FAST_OBJ_LIBRARIES fast_obj_FOUND)
-9
View File
@@ -32,15 +32,6 @@ if(NOT yyjson_FOUND)
endif() endif()
endif() endif()
if(NOT fast_obj_FOUND)
find_package(fast_obj REQUIRED)
if(fast_obj_FOUND)
target_link_libraries(${DUSK_LIBRARY_TARGET_NAME} PUBLIC fast_obj)
else()
message(FATAL_ERROR "fast_obj not found. Please ensure fast_obj is correctly fetched.")
endif()
endif()
if(NOT Lua_FOUND) if(NOT Lua_FOUND)
find_package(Lua REQUIRED) find_package(Lua REQUIRED)
if(Lua_FOUND AND NOT TARGET Lua::Lua) if(Lua_FOUND AND NOT TARGET Lua::Lua)
+5
View File
@@ -217,3 +217,8 @@
#define assertStrLenMin(str, len, message) ((void)0) #define assertStrLenMin(str, len, message) ((void)0)
#endif #endif
#define assertStructSize(struct, size) \
_Static_assert(sizeof(struct) == size, "Size of " #struct " must be " #size)
// EOF
+14
View File
@@ -8,6 +8,7 @@
#include "asset/asset.h" #include "asset/asset.h"
#include "assert/assert.h" #include "assert/assert.h"
#include "util/memory.h" #include "util/memory.h"
#include "util/math.h"
errorret_t assetFileInit( errorret_t assetFileInit(
assetfile_t *file, assetfile_t *file,
@@ -71,6 +72,19 @@ errorret_t assetFileRead(
assertNotNull(file, "Asset file cannot be NULL."); assertNotNull(file, "Asset file cannot be NULL.");
assertNotNull(file->zipFile, "Asset file must be opened before reading."); assertNotNull(file->zipFile, "Asset file must be opened before reading.");
if(buffer == NULL) {
size_t bytesRemaining = bufferSize;
uint8_t tempBuffer[256];
while(bytesRemaining > 0) {
size_t chunkSize = mathMin(bytesRemaining, sizeof(tempBuffer));
errorChain(assetFileRead(file, tempBuffer, chunkSize));
file->position += chunkSize;
bytesRemaining -= chunkSize;
}
file->lastRead = bufferSize;
errorOk();
}
// I assume zip_fread takes buffer NULL for skipping? // I assume zip_fread takes buffer NULL for skipping?
zip_int64_t bytesRead = zip_fread(file->zipFile, buffer, bufferSize); zip_int64_t bytesRead = zip_fread(file->zipFile, buffer, bufferSize);
if(bytesRead < 0) { if(bytesRead < 0) {
@@ -6,6 +6,7 @@
# 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
) )
@@ -0,0 +1,120 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "asset/loader/display/assetmeshloader.h"
#include "asset/asset.h"
#include "assert/assert.h"
#include "util/endian.h"
#include "util/memory.h"
errorret_t assetMeshLoader(assetfile_t *file) {
assertNotNull(file, "Asset file cannot be null");
assetmeshoutput_t *output = (assetmeshoutput_t *)file->output;
assertNotNull(output, "Output cannot be null");
assertNotNull(output->outMesh, "Output mesh cannot be null");
assertNotNull(output->outVertices, "Output vertices cannot be null");
// STL file loading
errorChain(assetFileOpen(file));
// Skip the 80 byte header
errorChain(assetFileRead(file, NULL, 80));
if(file->lastRead != 80) errorThrow("Failed to skip STL header");
uint32_t triangleCount;
errorChain(assetFileRead(file, &triangleCount, sizeof(uint32_t)));
if(file->lastRead != sizeof(uint32_t)) errorThrow("Failed read tri count");
// normalize
triangleCount = endianLittleToHost32(triangleCount);
// Allocate mesh and vertex data
errorret_t ret;
meshvertex_t *verts = memoryAllocate(
sizeof(meshvertex_t) * triangleCount * 3
);
*output->outVertices = verts;
// Read triangle data
for(uint32_t i = 0; i < triangleCount; i++) {
assetmeshstltriangle_t triData;
ret = assetFileRead(file, &triData, sizeof(triData));
if(ret.code != ERROR_OK) {
memoryFree(verts);
errorChain(ret);
}
if(file->lastRead != sizeof(triData)) {
memoryFree(verts);
errorThrow("Failed to read triangle data");
}
// Skip normals, we don't use them
// Fix endianess of of data
for(uint8_t j = 0; j < 3; j++) {
verts[i * 3 + j].color.r = (uint8_t)(endianLittleToHostFloat(
triData.normal[0]
) * 255.0f);
verts[i * 3 + j].color.g = (uint8_t)(endianLittleToHostFloat(
triData.normal[1]
) * 255.0f);
verts[i * 3 + j].color.b = (uint8_t)(endianLittleToHostFloat(
triData.normal[2]
) * 255.0f);
verts[i * 3 + j].color.a = 0xFF;
verts[i * 3 + j].uv[0] = 0.0f; // No UV data in STL, just set to 0
verts[i * 3 + j].uv[1] = 0.0f;
for(uint8_t k = 0; k < 3; k++) {
verts[i * 3 + j].pos[k] = endianLittleToHostFloat(
triData.positions[j][k]
);
}
// Convert Z-Up to Y-Up
float_t temp = verts[i * 3 + j].pos[1];
verts[i * 3 + j].pos[1] = verts[i * 3 + j].pos[2];
verts[i * 3 + j].pos[2] = temp;
}
}
// Finally, init mesh
ret = meshInit(
output->outMesh,
MESH_PRIMITIVE_TYPE_TRIANGLES,
triangleCount * 3,
verts
);
if(ret.code != ERROR_OK) {
memoryFree(verts);
errorChain(ret);
}
ret = assetFileClose(file);
if(ret.code != ERROR_OK) {
errorCatch(errorPrint(meshDispose(output->outMesh)));
memoryFree(verts);
errorChain(ret);
}
errorOk();
}
errorret_t assetMeshLoad(
const char_t *path,
mesh_t *outMesh,
meshvertex_t **outVertices
) {
assertNotNull(path, "Path cannot be null");
assertNotNull(outMesh, "Output mesh cannot be null");
assertNotNull(outVertices, "Output vertices cannot be null");
assetmeshoutput_t output = { outMesh, outVertices };
return assetLoad(path, &assetMeshLoader, NULL, &output);
}
@@ -0,0 +1,46 @@
/**
* 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"
#include "assert/assert.h"
typedef struct {
mesh_t *outMesh;
meshvertex_t **outVertices;
} assetmeshoutput_t;
#pragma pack(push, 1)
typedef struct {
vec3 normal;
float_t positions[3][3];
uint16_t attributeByteCount;
} assetmeshstltriangle_t;
#pragma pack(pop)
assertStructSize(assetmeshstltriangle_t, 50);
/**
* Loader for mesh assets.
*
* @param file Asset file to load the mesh from.
* @return Any error that occurs during loading.
*/
errorret_t assetMeshLoader(assetfile_t *file);
/**
* Handler for mesh assets.
*
* @param file Asset file to load the mesh from.
* @return Any error that occurs during loading.
*/
errorret_t assetMeshLoad(
const char_t *path,
mesh_t *outMesh,
meshvertex_t **outVertices
);
+11 -17
View File
@@ -20,19 +20,17 @@
#include "game/game.h" #include "game/game.h"
#include "display/mesh/quad.h" #include "display/mesh/quad.h"
#include "asset/loader/display/assetmeshloader.h"
engine_t ENGINE; engine_t ENGINE;
// texture_t TEXTURE;
// color_t TEXTURE_COLORS[] = {
// COLOR_RED, COLOR_GREEN, COLOR_MAGENTA, COLOR_CYAN,
// COLOR_BLUE, COLOR_WHITE, COLOR_YELLOW, COLOR_BLACK,
// COLOR_CYAN, COLOR_MAGENTA, COLOR_GREEN, COLOR_RED,
// 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;
componentid_t ent1Mat; componentid_t ent1Mat;
mesh_t loadedMesh;
meshvertex_t *loadedVertices;
errorret_t engineInit(const int32_t argc, const char_t **argv) { errorret_t engineInit(const int32_t argc, const char_t **argv) {
memoryZero(&ENGINE, sizeof(engine_t)); memoryZero(&ENGINE, sizeof(engine_t));
ENGINE.running = true; ENGINE.running = true;
@@ -57,27 +55,23 @@ errorret_t engineInit(const int32_t argc, const char_t **argv) {
entityPositionLookAt( entityPositionLookAt(
cam, cam,
camPos, camPos,
(vec3){ 0.0f, 0.0f, 0.0f }, (vec3){ 0.0f, 20.0f, 0.0f },
(vec3){ 0.0f, 1.0f, 0.0f }, (vec3){ 0.0f, 1.0f, 0.0f },
(vec3){ 3.0f, 3.0f, 3.0f } (vec3){ 50.0f, 70.0f, 50.0f }
); );
componentid_t camCam = entityAddComponent(cam, COMPONENT_TYPE_CAMERA); componentid_t camCam = entityAddComponent(cam, COMPONENT_TYPE_CAMERA);
entityCameraSetZFar(cam, camCam, 100.0f); entityCameraSetZFar(cam, camCam, 500.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){ errorChain(assetMeshLoad("test/RosaTh.stl", &loadedMesh, &loadedVertices));
// .rgbaColors = TEXTURE_COLORS entityMeshSetMesh(ent1, ent1Mesh, &loadedMesh);
// });
entityMeshSetMesh(ent1, ent1Mesh, &QUAD_MESH_SIMPLE);
shadermaterial_t *mat = entityMaterialGetShaderMaterial(ent1, ent1Mat); shadermaterial_t *mat = entityMaterialGetShaderMaterial(ent1, ent1Mat);
mat->unlit.color = COLOR_MAGENTA; mat->unlit.color = COLOR_WHITE;
// mat->unlit.texture = &TEXTURE;
// EOF // EOF