diff --git a/cmake/modules/Findfast_obj.cmake b/cmake/modules/Findfast_obj.cmake deleted file mode 100644 index 1517cb9c..00000000 --- a/cmake/modules/Findfast_obj.cmake +++ /dev/null @@ -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) diff --git a/src/dusk/CMakeLists.txt b/src/dusk/CMakeLists.txt index f8b4daed..ff8c5acb 100644 --- a/src/dusk/CMakeLists.txt +++ b/src/dusk/CMakeLists.txt @@ -32,15 +32,6 @@ if(NOT yyjson_FOUND) 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) find_package(Lua REQUIRED) if(Lua_FOUND AND NOT TARGET Lua::Lua) diff --git a/src/dusk/assert/assert.h b/src/dusk/assert/assert.h index 2ea2ed51..d6de65a5 100644 --- a/src/dusk/assert/assert.h +++ b/src/dusk/assert/assert.h @@ -216,4 +216,9 @@ #define assertStrLenMax(str, len, message) ((void)0) #define assertStrLenMin(str, len, message) ((void)0) -#endif \ No newline at end of file +#endif + +#define assertStructSize(struct, size) \ + _Static_assert(sizeof(struct) == size, "Size of " #struct " must be " #size) + +// EOF diff --git a/src/dusk/asset/assetfile.c b/src/dusk/asset/assetfile.c index 76800469..3f9a9ec8 100644 --- a/src/dusk/asset/assetfile.c +++ b/src/dusk/asset/assetfile.c @@ -8,6 +8,7 @@ #include "asset/asset.h" #include "assert/assert.h" #include "util/memory.h" +#include "util/math.h" errorret_t assetFileInit( assetfile_t *file, @@ -70,6 +71,19 @@ errorret_t assetFileRead( ) { assertNotNull(file, "Asset file cannot be NULL."); 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? zip_int64_t bytesRead = zip_fread(file->zipFile, buffer, bufferSize); diff --git a/src/dusk/asset/loader/display/CMakeLists.txt b/src/dusk/asset/loader/display/CMakeLists.txt index 562a1298..391df145 100644 --- a/src/dusk/asset/loader/display/CMakeLists.txt +++ b/src/dusk/asset/loader/display/CMakeLists.txt @@ -6,6 +6,7 @@ # Sources target_sources(${DUSK_LIBRARY_TARGET_NAME} PUBLIC + assetmeshloader.c assettextureloader.c assettilesetloader.c ) \ No newline at end of file diff --git a/src/dusk/asset/loader/display/assetmeshloader.c b/src/dusk/asset/loader/display/assetmeshloader.c new file mode 100644 index 00000000..08eebc29 --- /dev/null +++ b/src/dusk/asset/loader/display/assetmeshloader.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); +} \ No newline at end of file diff --git a/src/dusk/asset/loader/display/assetmeshloader.h b/src/dusk/asset/loader/display/assetmeshloader.h new file mode 100644 index 00000000..6ea43e01 --- /dev/null +++ b/src/dusk/asset/loader/display/assetmeshloader.h @@ -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 +); \ No newline at end of file diff --git a/src/dusk/engine/engine.c b/src/dusk/engine/engine.c index 886143b7..8c2d0ec0 100644 --- a/src/dusk/engine/engine.c +++ b/src/dusk/engine/engine.c @@ -20,19 +20,17 @@ #include "game/game.h" #include "display/mesh/quad.h" +#include "asset/loader/display/assetmeshloader.h" + 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; componentid_t ent1Pos; componentid_t ent1Mesh; componentid_t ent1Mat; +mesh_t loadedMesh; +meshvertex_t *loadedVertices; + errorret_t engineInit(const int32_t argc, const char_t **argv) { memoryZero(&ENGINE, sizeof(engine_t)); ENGINE.running = true; @@ -57,27 +55,23 @@ errorret_t engineInit(const int32_t argc, const char_t **argv) { entityPositionLookAt( cam, camPos, - (vec3){ 0.0f, 0.0f, 0.0f }, + (vec3){ 0.0f, 20.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); - entityCameraSetZFar(cam, camCam, 100.0f); + entityCameraSetZFar(cam, camCam, 500.0f); ent1 = entityManagerAdd(); ent1Pos = entityAddComponent(ent1, COMPONENT_TYPE_POSITION); ent1Mesh = entityAddComponent(ent1, COMPONENT_TYPE_MESH); ent1Mat = entityAddComponent(ent1, COMPONENT_TYPE_MATERIAL); - // textureInit(&TEXTURE, 4, 4, TEXTURE_FORMAT_RGBA, (texturedata_t){ - // .rgbaColors = TEXTURE_COLORS - // }); - - entityMeshSetMesh(ent1, ent1Mesh, &QUAD_MESH_SIMPLE); + errorChain(assetMeshLoad("test/RosaTh.stl", &loadedMesh, &loadedVertices)); + entityMeshSetMesh(ent1, ent1Mesh, &loadedMesh); shadermaterial_t *mat = entityMaterialGetShaderMaterial(ent1, ent1Mat); - mat->unlit.color = COLOR_MAGENTA; - // mat->unlit.texture = &TEXTURE; + mat->unlit.color = COLOR_WHITE; // EOF