diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index ad391edd4..d102eb4cb 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -86,7 +86,9 @@ if (${PLATFORM} MATCHES "Android") list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/models/models_obj_viewer.c) list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/models/models_animation.c) list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/models/models_first_person_maze.c) + list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/models/models_magicavoxel_loading.c) + list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/shaders/shaders_custom_uniform.c) list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/shaders/shaders_model_shader.c) list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/shaders/shaders_postprocessing.c) diff --git a/examples/Makefile b/examples/Makefile index 5abf0c708..f5cb2175b 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -466,7 +466,8 @@ MODELS = \ models/models_skybox \ models/models_yaw_pitch_roll \ models/models_heightmap \ - models/models_waving_cubes + models/models_waving_cubes \ + models/models_magicavoxel_loading SHADERS = \ shaders/shaders_model_shader \ diff --git a/examples/models/models_magicavoxel_loading.c b/examples/models/models_magicavoxel_loading.c new file mode 100644 index 000000000..bb1389d6c --- /dev/null +++ b/examples/models/models_magicavoxel_loading.c @@ -0,0 +1,153 @@ +/******************************************************************************************* +* +* raylib [models] example - magicavoxel loader and viewer +* +* This example has been created using raylib 3.8 (www.raylib.com) +* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* +* Example contributed by Johann Nadalutti +* +* Copyright (c) 2021 Johann Nadalutti +* +********************************************************************************************/ + +#include "raylib.h" +#include "raymath.h" + +#include + + +// VOX Files to load and view + +#define NUM_VOX_FILES 3 + +const char* szVoxFiles[] = { + "resources/vox/chr_knight.vox", + "resources/vox/chr_sword.vox", + "resources/vox/monu9.vox" +}; + + +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [models] example - magicavoxel loading"); + + // Load MagicaVoxel files + Model models[NUM_VOX_FILES] = { 0 }; + + for (int i = 0; i < NUM_VOX_FILES; i++) + { + // Load MagicaVoxel File and build model + double t0, t1; + t0 = GetTime() * 1000.0; + + models[i] = LoadModel(szVoxFiles[i]); + + t1 = GetTime() * 1000.0; + TraceLog(LOG_INFO, TextFormat("Vox <%s> loaded in %f ms", GetFileName(szVoxFiles[i]), t1 - t0)); + + //Compute model matrix + BoundingBox bb = GetModelBoundingBox(models[i]); + Vector3 center; + center.x = -(((bb.max.x - bb.min.x) / 2)); + center.y = -(((bb.max.y - bb.min.y) / 2)); + center.z = -(((bb.max.z - bb.min.z) / 2)); + + Matrix matP = MatrixTranslate(center.x, center.z, 0); + Matrix matR = MatrixRotateX(90 * DEG2RAD); + models[i].transform = MatrixMultiply(matP, matR); + + + } + + + // Define the camera to look into our 3d world + Camera camera = { { 0.0f, 10.0f, 10.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 1.0f, 0.0f }, 45.0f, 0 }; + + // Model drawing position + Vector3 position = { 0.0f, 0.0f, 0.0f }; + + int currentModel = 0; + + + + SetCameraMode(camera, CAMERA_ORBITAL); // Set a orbital camera mode + + SetTargetFPS(60); // Set our game to run at 60 frames-per-second + + //-------------------------------------------------------------------------------------- + // Main game loop + //-------------------------------------------------------------------------------------- + while (!WindowShouldClose()) // Detect window close button or ESC key + { + //-------------------------------------------------------------------------------------- + // Update + //---------------------------------------------------------------------------------- + UpdateCamera(&camera); // Update internal camera and our camera + + if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) + { + currentModel = (currentModel + 1) % NUM_VOX_FILES; // Cycle between models + } + + if (IsKeyPressed(KEY_RIGHT)) + { + currentModel++; + if (currentModel >= NUM_VOX_FILES) currentModel = 0; + } + else if (IsKeyPressed(KEY_LEFT)) + { + currentModel--; + if (currentModel < 0) currentModel = NUM_VOX_FILES - 1; + } + + //---------------------------------------------------------------------------------- + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + //Display model + BeginMode3D(camera); + + Vector3 rotAxis = { 1,0,0 }; + Vector3 scale = { 1,1,1 }; + + + DrawModelEx(models[currentModel], position, rotAxis, 0, scale, WHITE); + //DrawModelWiresEx(models[currentModel], position, rotAxis, -90.0f, scale, BLACK); + + DrawGrid(10, 1.0); + + EndMode3D(); + + //Display debug infos + DrawRectangle(30, 400, 310, 30, Fade(SKYBLUE, 0.5f)); + DrawRectangleLines(30, 400, 310, 30, Fade(DARKBLUE, 0.5f)); + DrawText("MOUSE LEFT BUTTON to CYCLE VOX MODELS", 40, 410, 10, BLUE); + + DrawText(GetFileName(szVoxFiles[currentModel]), 100, 10, 20, DARKBLUE); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + + // Unload models data (GPU VRAM) + for (int i = 0; i < NUM_VOX_FILES; i++) UnloadModel(models[i]); + + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} + + diff --git a/examples/models/resources/vox/chr_knight.vox b/examples/models/resources/vox/chr_knight.vox new file mode 100644 index 000000000..c921bf5c7 Binary files /dev/null and b/examples/models/resources/vox/chr_knight.vox differ diff --git a/examples/models/resources/vox/chr_sword.vox b/examples/models/resources/vox/chr_sword.vox new file mode 100644 index 000000000..05fc4826b Binary files /dev/null and b/examples/models/resources/vox/chr_sword.vox differ diff --git a/examples/models/resources/vox/monu9.vox b/examples/models/resources/vox/monu9.vox new file mode 100644 index 000000000..fd7711125 Binary files /dev/null and b/examples/models/resources/vox/monu9.vox differ diff --git a/projects/VS2019/examples/models_mesh_magicavoxel_loading.vcxproj b/projects/VS2019/examples/models_mesh_magicavoxel_loading.vcxproj new file mode 100644 index 000000000..f2e86c8a6 --- /dev/null +++ b/projects/VS2019/examples/models_mesh_magicavoxel_loading.vcxproj @@ -0,0 +1,387 @@ + + + + + Debug.DLL + Win32 + + + Debug.DLL + x64 + + + Debug + Win32 + + + Debug + x64 + + + Release.DLL + Win32 + + + Release.DLL + x64 + + + Release + Win32 + + + Release + x64 + + + + {2F1B955B-275E-4D8E-8864-06FEC44D7912} + Win32Proj + models_mesh_magicavoxel_loading + 10.0 + models_mesh_magicavoxel_loading + + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + $(SolutionDir)..\..\examples\models + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\models + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\models + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\models + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\models + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\models + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\models + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\models + WindowsLocalDebugger + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + /FS %(AdditionalOptions) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + {e89d61ac-55de-4482-afd4-df7242ebc859} + + + + + + + + + \ No newline at end of file diff --git a/projects/VS2019/raylib.sln b/projects/VS2019/raylib.sln index 53d2a18ad..f5a4180f2 100644 --- a/projects/VS2019/raylib.sln +++ b/projects/VS2019/raylib.sln @@ -275,6 +275,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "rlgl_standalone", "examples EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_split_screen", "examples\core_split_screen.vcxproj", "{946A1700-C7AA-46F0-AEF2-67C98B5722AC}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_mesh_magicavoxel_loading", "examples\models_mesh_magicavoxel_loading.vcxproj", "{2F1B955B-275E-4D8E-8864-06FEC44D7912}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug.DLL|x64 = Debug.DLL|x64 @@ -2283,6 +2285,22 @@ Global {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Release|x64.Build.0 = Release|x64 {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Release|x86.ActiveCfg = Release|Win32 {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Release|x86.Build.0 = Release|Win32 + {2F1B955B-275E-4D8E-8864-06FEC44D7912}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {2F1B955B-275E-4D8E-8864-06FEC44D7912}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {2F1B955B-275E-4D8E-8864-06FEC44D7912}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {2F1B955B-275E-4D8E-8864-06FEC44D7912}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {2F1B955B-275E-4D8E-8864-06FEC44D7912}.Debug|x64.ActiveCfg = Debug|x64 + {2F1B955B-275E-4D8E-8864-06FEC44D7912}.Debug|x64.Build.0 = Debug|x64 + {2F1B955B-275E-4D8E-8864-06FEC44D7912}.Debug|x86.ActiveCfg = Debug|Win32 + {2F1B955B-275E-4D8E-8864-06FEC44D7912}.Debug|x86.Build.0 = Debug|Win32 + {2F1B955B-275E-4D8E-8864-06FEC44D7912}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {2F1B955B-275E-4D8E-8864-06FEC44D7912}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {2F1B955B-275E-4D8E-8864-06FEC44D7912}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {2F1B955B-275E-4D8E-8864-06FEC44D7912}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {2F1B955B-275E-4D8E-8864-06FEC44D7912}.Release|x64.ActiveCfg = Release|x64 + {2F1B955B-275E-4D8E-8864-06FEC44D7912}.Release|x64.Build.0 = Release|x64 + {2F1B955B-275E-4D8E-8864-06FEC44D7912}.Release|x86.ActiveCfg = Release|Win32 + {2F1B955B-275E-4D8E-8864-06FEC44D7912}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -2422,6 +2440,7 @@ Global {6237BEDE-BAAA-4A06-9C5E-8089BAA14C8B} = {E9D708A5-9C1F-4B84-A795-C5F191801762} {C8765523-58F8-4C8E-9914-693396F6F0FF} = {E9D708A5-9C1F-4B84-A795-C5F191801762} {946A1700-C7AA-46F0-AEF2-67C98B5722AC} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} + {2F1B955B-275E-4D8E-8864-06FEC44D7912} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {E926C768-6307-4423-A1EC-57E95B1FAB29} diff --git a/src/config.h b/src/config.h index b7358d7e7..b244add66 100644 --- a/src/config.h +++ b/src/config.h @@ -182,6 +182,7 @@ #define SUPPORT_FILEFORMAT_MTL 1 #define SUPPORT_FILEFORMAT_IQM 1 #define SUPPORT_FILEFORMAT_GLTF 1 +#define SUPPORT_FILEFORMAT_VOX 1 // Support procedural mesh generation functions, uses external par_shapes.h library // NOTE: Some generated meshes DO NOT include generated texture coordinates #define SUPPORT_MESH_GENERATION 1 diff --git a/src/external/vox_loader.h b/src/external/vox_loader.h new file mode 100644 index 000000000..63f2719f5 --- /dev/null +++ b/src/external/vox_loader.h @@ -0,0 +1,771 @@ +/* + The MIT License (MIT) + + Copyright (c) 2021 Johann Nadalutti. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + + + vox_loader - v1.00 + no warranty implied; use at your own risk + + Do this: + #define VOX_LOADER_INCLUDE__H + before you include this file in* one* C or C++ file to create the implementation. + + // i.e. it should look like this: + #include ... + #include ... + #include ... + #define VOX_LOADER_INCLUDE__H + #include "magicavoxel_loader.h" + +revision history: + 1.00 (2021-09-03) first released version + +*/ + + +#ifndef VOX_LOADER_H +#define VOX_LOADER_H + + +#include + + +#ifdef __cplusplus +extern "C" { +#endif + +#define VOX_SUCCESS (0) +#define VOX_ERROR_FILE_NOT_FOUND (-1) +#define VOX_ERROR_INVALID_FORMAT (-2) +#define VOX_ERROR_FILE_VERSION_TOO_OLD (-3) + + typedef struct + { + int* array; + int used, size; + } ArrayInt; + + typedef struct + { + Vector3* array; + int used, size; + } ArrayVector3; + + typedef struct + { + Color* array; + int used, size; + } ArrayColor; + + typedef struct + { + unsigned short* array; + int used, size; + } ArrayUShort; + + + // A chunk that contain voxels + typedef struct + { + unsigned char* m_array; //If Sparse != null + int arraySize; //Size for m_array in bytes (DEBUG ONLY) + } CubeChunk3D; + + // Array for voxels + // Array is divised into chunks of CHUNKSIZE*CHUNKSIZE*CHUNKSIZE voxels size + typedef struct + { + //Array size in voxels + int sizeX; + int sizeY; + int sizeZ; + + //Chunks size into array (array is divised into chunks) + int chunksSizeX; + int chunksSizeY; + int chunksSizeZ; + + //Chunks array + CubeChunk3D* m_arrayChunks; + int arrayChunksSize; //Size for m_arrayChunks in bytes (DEBUG ONLY) + + int ChunkFlattenOffset; + int chunksAllocated; + int chunksTotal; + + //Arrays for mesh build + ArrayVector3 vertices; + ArrayUShort indices; + ArrayColor colors; + + //Palette for voxels + Color palette[256]; + + } VoxArray3D; + + + // Functions + extern int Vox_LoadFileName(const char* pszfileName, VoxArray3D* voxarray); + extern void Vox_FreeArrays(VoxArray3D* voxarray); + + +#ifdef __cplusplus +} +#endif + + + +//// end header file ///////////////////////////////////////////////////// +#endif // VOX_LOADER_H + + + +///////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////// +// Implementation +///////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////// + +#ifdef VOX_LOADER_IMPLEMENTATION + + + +///////////////////////////////////////////////////////////////////////////////////////////// +// ArrayInt helper +///////////////////////////////////////////////////////////////////////////////////////////// + +void initArrayInt(ArrayInt* a, int initialSize) +{ + a->array = MemAlloc(initialSize * sizeof(int)); + a->used = 0; + a->size = initialSize; +} + +void insertArrayInt(ArrayInt* a, int element) +{ + if (a->used == a->size) + { + a->size *= 2; + a->array = MemRealloc(a->array, a->size * sizeof(int)); + } + a->array[a->used++] = element; +} + +void freeArrayInt(ArrayInt* a) +{ + MemFree(a->array); + a->array = NULL; + a->used = a->size = 0; +} + +///////////////////////////////////////////////////////////////////////////////////////////// +// ArrayUShort helper +///////////////////////////////////////////////////////////////////////////////////////////// + +void initArrayUShort(ArrayUShort* a, int initialSize) +{ + a->array = MemAlloc(initialSize * sizeof(unsigned short)); + a->used = 0; + a->size = initialSize; +} + +void insertArrayUShort(ArrayUShort* a, unsigned short element) +{ + if (a->used == a->size) + { + a->size *= 2; + a->array = MemRealloc(a->array, a->size * sizeof(unsigned short)); + } + a->array[a->used++] = element; +} + +void freeArrayUShort(ArrayUShort* a) +{ + MemFree(a->array); + a->array = NULL; + a->used = a->size = 0; +} + + +///////////////////////////////////////////////////////////////////////////////////////////// +// ArrayVector3 helper +///////////////////////////////////////////////////////////////////////////////////////////// + +void initArrayVector3(ArrayVector3* a, int initialSize) +{ + a->array = MemAlloc(initialSize * sizeof(Vector3)); + a->used = 0; + a->size = initialSize; +} + +void insertArrayVector3(ArrayVector3* a, Vector3 element) +{ + if (a->used == a->size) + { + a->size *= 2; + a->array = MemRealloc(a->array, a->size * sizeof(Vector3)); + } + a->array[a->used++] = element; +} + +void freeArrayVector3(ArrayVector3* a) +{ + MemFree(a->array); + a->array = NULL; + a->used = a->size = 0; +} + +///////////////////////////////////////////////////////////////////////////////////////////// +// ArrayColor helper +///////////////////////////////////////////////////////////////////////////////////////////// + +void initArrayColor(ArrayColor* a, int initialSize) +{ + a->array = MemAlloc(initialSize * sizeof(Color)); + a->used = 0; + a->size = initialSize; +} + +void insertArrayColor(ArrayColor* a, Color element) +{ + if (a->used == a->size) + { + a->size *= 2; + a->array = MemRealloc(a->array, a->size * sizeof(Color)); + } + a->array[a->used++] = element; +} + +void freeArrayColor(ArrayColor* a) +{ + MemFree(a->array); + a->array = NULL; + a->used = a->size = 0; +} + + +///////////////////////////////////////////////////////////////////////////////////////////// +// Vox Loader +///////////////////////////////////////////////////////////////////////////////////////////// + +#define CHUNKSIZE 16 // chunk size (CHUNKSIZE*CHUNKSIZE*CHUNKSIZE) in voxels +#define CHUNKSIZE_OPSHIFT 4 // 1<<4=16 -> Warning depend of CHUNKSIZE +#define CHUNK_FLATTENOFFSET_OPSHIFT 8 //Warning depend of CHUNKSIZE + +// +// used right handed system and CCW face +// +// indexes for voxelcoords, per face orientation +// + +//# Y +//# | +//# o----X +//# / +//# Z 2------------3 +//# /| /| +//# 6------------7 | +//# | | | | +//# |0 ----------|- 1 +//# |/ |/ +//# 4------------5 + +// +// CCW +const int fv[6][4] = { + {0, 2, 6, 4 }, //-X + {5, 7, 3, 1 }, //+X + {0, 4, 5, 1 }, //-y + {6, 2, 3, 7 }, //+y + {1, 3, 2, 0 }, //-Z + {4, 6, 7, 5 } };//+Z + + +const Vector3 SolidVertex[] = { + {0, 0, 0}, //0 + {1, 0, 0}, //1 + {0, 1, 0}, //2 + {1, 1, 0}, //3 + {0, 0, 1}, //4 + {1, 0, 1}, //5 + {0, 1, 1}, //6 + {1, 1, 1} }; //7 + + + + + +// Allocated VoxArray3D size +void Vox_AllocArray(VoxArray3D* voxarray, int _sx, int _sy, int _sz) +{ + int sx = _sx + ((CHUNKSIZE - (_sx % CHUNKSIZE)) % CHUNKSIZE); + int sy = _sy + ((CHUNKSIZE - (_sy % CHUNKSIZE)) % CHUNKSIZE); + int sz = _sz + ((CHUNKSIZE - (_sz % CHUNKSIZE)) % CHUNKSIZE); + + int chx = sx >> CHUNKSIZE_OPSHIFT; //Chunks Count in X + int chy = sy >> CHUNKSIZE_OPSHIFT; //Chunks Count in Y + int chz = sz >> CHUNKSIZE_OPSHIFT; //Chunks Count in Z + + //VoxArray3D* parray = (VoxArray3D*)MemAlloc(sizeof(VoxArray3D)); + voxarray->sizeX = sx; + voxarray->sizeY = sy; + voxarray->sizeZ = sz; + + voxarray->chunksSizeX = chx; + voxarray->chunksSizeY = chy; + voxarray->chunksSizeZ = chz; + + voxarray->ChunkFlattenOffset = (chy * chz); //m_arrayChunks[(x * (sy*sz)) + (z * sy) + y] + + //Alloc chunks array + int size = sizeof(CubeChunk3D) * chx * chy * chz; + voxarray->m_arrayChunks = MemAlloc(size); + voxarray->arrayChunksSize = size; + + + //Init chunks array + size = chx * chy * chz; + voxarray->chunksTotal = size; + voxarray->chunksAllocated = 0; + + for (int i = 0; i < size; i++) + { + voxarray->m_arrayChunks[i].m_array = 0; + voxarray->m_arrayChunks[i].arraySize = 0; + } + + return voxarray; +} + +// Set voxel ID from its position into VoxArray3D +void Vox_SetVoxel(VoxArray3D* voxarray, int x, int y, int z, unsigned char id) +{ + //Get chunk from array pos + int chX = x >> CHUNKSIZE_OPSHIFT; //x / CHUNKSIZE; + int chY = y >> CHUNKSIZE_OPSHIFT; //y / CHUNKSIZE; + int chZ = z >> CHUNKSIZE_OPSHIFT; //z / CHUNKSIZE; + int offset = (chX * voxarray->ChunkFlattenOffset) + (chZ * voxarray->chunksSizeY) + chY; + + //if (offset > voxarray->arrayChunksSize) + //{ + // TraceLog(LOG_ERROR, "Out of array"); + //} + + CubeChunk3D* chunk = &voxarray->m_arrayChunks[offset]; + + //Set Chunk + chX = x - (chX << CHUNKSIZE_OPSHIFT); //x - (bx * CHUNKSIZE); + chY = y - (chY << CHUNKSIZE_OPSHIFT); //y - (by * CHUNKSIZE); + chZ = z - (chZ << CHUNKSIZE_OPSHIFT); //z - (bz * CHUNKSIZE); + + if (chunk->m_array == 0) + { + int size = CHUNKSIZE * CHUNKSIZE * CHUNKSIZE; + chunk->m_array = MemAlloc(size); + chunk->arraySize = size; + //memset(chunk->m_array, 0, size); + + voxarray->chunksAllocated++; + } + + offset = (chX << CHUNK_FLATTENOFFSET_OPSHIFT) + (chZ << CHUNKSIZE_OPSHIFT) + chY; + + //if (offset > chunk->arraySize) + //{ + // TraceLog(LOG_ERROR, "Out of array"); + //} + + chunk->m_array[offset] = id; + +} + +// Get voxel ID from its position into VoxArray3D +unsigned char Vox_GetVoxel(VoxArray3D* voxarray, int x, int y, int z) +{ + if (x < 0 || y < 0 || z < 0) + return 0; + + if (x >= voxarray->sizeX || y >= voxarray->sizeY || z >= voxarray->sizeZ) + return 0; + + + //Get chunk from array pos + int chX = x >> CHUNKSIZE_OPSHIFT; //x / CHUNKSIZE; + int chY = y >> CHUNKSIZE_OPSHIFT; //y / CHUNKSIZE; + int chZ = z >> CHUNKSIZE_OPSHIFT; //z / CHUNKSIZE; + int offset = (chX * voxarray->ChunkFlattenOffset) + (chZ * voxarray->chunksSizeY) + chY; + + //if (offset > voxarray->arrayChunksSize) + //{ + // TraceLog(LOG_ERROR, "Out of array"); + //} + + CubeChunk3D* chunk = &voxarray->m_arrayChunks[offset]; + + //Set Chunk + chX = x - (chX << CHUNKSIZE_OPSHIFT); //x - (bx * CHUNKSIZE); + chY = y - (chY << CHUNKSIZE_OPSHIFT); //y - (by * CHUNKSIZE); + chZ = z - (chZ << CHUNKSIZE_OPSHIFT); //z - (bz * CHUNKSIZE); + + if (chunk->m_array == 0) + { + return 0; + } + + offset = (chX << CHUNK_FLATTENOFFSET_OPSHIFT) + (chZ << CHUNKSIZE_OPSHIFT) + chY; + + //if (offset > chunk->arraySize) + //{ + // TraceLog(LOG_ERROR, "Out of array"); + //} + return chunk->m_array[offset]; + +} + +// Calc visibles faces from a voxel position +unsigned char Vox_CalcFacesVisible(VoxArray3D* pvoxArray, int cx, int cy, int cz) +{ + unsigned char idXm1 = Vox_GetVoxel(pvoxArray, cx - 1, cy, cz); + unsigned char idXp1 = Vox_GetVoxel(pvoxArray, cx + 1, cy, cz); + + unsigned char idYm1 = Vox_GetVoxel(pvoxArray, cx, cy - 1, cz); + unsigned char idYp1 = Vox_GetVoxel(pvoxArray, cx, cy + 1, cz); + + unsigned char idZm1 = Vox_GetVoxel(pvoxArray, cx, cy, cz - 1); + unsigned char idZp1 = Vox_GetVoxel(pvoxArray, cx, cy, cz + 1); + + unsigned char byVFMask = 0; + + //#-x + if (idXm1 == 0) + byVFMask |= (1 << 0); + + //#+x + if (idXp1 == 0) + byVFMask |= (1 << 1); + + //#-y + if (idYm1 == 0) + byVFMask |= (1 << 2); + + //#+y + if (idYp1 == 0) + byVFMask |= (1 << 3); + + //#-z + if (idZm1 == 0) + byVFMask |= (1 << 4); + + //#+z + if (idZp1 == 0) + byVFMask |= (1 << 5); + + return byVFMask; +} + +// Get a vertex position from a voxel's corner +Vector3 Vox_GetVertexPosition(int _wcx, int _wcy, int _wcz, int _nNumVertex) +{ + float scale = 0.25; + Vector3 vtx = SolidVertex[_nNumVertex]; + vtx.x = (vtx.x + _wcx) * scale; + vtx.y = (vtx.y + _wcy) * scale; + vtx.z = (vtx.z + _wcz) * scale; + return vtx; +} + +// Build a voxel vertices/colors/indices +void Vox_Build_Voxel(VoxArray3D* pvoxArray, int x, int y, int z, int matID) +{ + + unsigned char byVFMask = Vox_CalcFacesVisible(pvoxArray, x, y, z); + + if (byVFMask == 0) + return; + + int i, j; + Vector3 vertComputed[8]; + int bVertexComputed[8]; + memset(vertComputed, 0, sizeof(vertComputed)); + memset(bVertexComputed, 0, sizeof(bVertexComputed)); + + + //For each Cube's faces + for (i = 0; i < 6; i++) // 6 faces + { + if ((byVFMask & (1 << i)) != 0) //If face is visible + { + for (j = 0; j < 4; j++) // 4 corners + { + int nNumVertex = fv[i][j]; //Face,Corner + if (bVertexComputed[nNumVertex] == 0) //if never calc + { + bVertexComputed[nNumVertex] = 1; + vertComputed[nNumVertex] = Vox_GetVertexPosition(x, y, z, nNumVertex); + } + } + } + } + + //Add face + for (i = 0; i < 6; i++)// 6 faces + { + if ((byVFMask & (1 << i)) == 0) + continue; //Face invisible + + int v0 = fv[i][0]; //Face, Corner + int v1 = fv[i][1]; //Face, Corner + int v2 = fv[i][2]; //Face, Corner + int v3 = fv[i][3]; //Face, Corner + + //Arrays + int idx = pvoxArray->vertices.used; + insertArrayVector3(&pvoxArray->vertices, vertComputed[v0]); + insertArrayVector3(&pvoxArray->vertices, vertComputed[v1]); + insertArrayVector3(&pvoxArray->vertices, vertComputed[v2]); + insertArrayVector3(&pvoxArray->vertices, vertComputed[v3]); + + Color col = pvoxArray->palette[matID]; + + insertArrayColor(&pvoxArray->colors, col); + insertArrayColor(&pvoxArray->colors, col); + insertArrayColor(&pvoxArray->colors, col); + insertArrayColor(&pvoxArray->colors, col); + + + //v0 - v1 - v2, v0 - v2 - v3 + insertArrayUShort(&pvoxArray->indices, idx + 0); + insertArrayUShort(&pvoxArray->indices, idx + 2); + insertArrayUShort(&pvoxArray->indices, idx + 1); + + insertArrayUShort(&pvoxArray->indices, idx + 0); + insertArrayUShort(&pvoxArray->indices, idx + 3); + insertArrayUShort(&pvoxArray->indices, idx + 2); + + + + } + +} + +// MagicaVoxel *.vox file format Loader +int Vox_LoadFileName(const char* pszfileName, VoxArray3D* voxarray) +{ + + ////////////////////////////////////////////////// + //Read VOX file + //4 bytes: magic number ('V' 'O' 'X' 'space' ) + //4 bytes: version number (current version is 150 ) + + unsigned long signature; + + unsigned long readed = 0; + unsigned char* fileData; + fileData = LoadFileData(pszfileName, &readed); + if (fileData == 0) + { + return VOX_ERROR_FILE_NOT_FOUND; + } + + unsigned char* fileDataPtr = fileData; + unsigned char* endfileDataPtr = fileData + readed; + + signature = *((unsigned long*)fileDataPtr); + fileDataPtr += sizeof(unsigned long); + + if (signature != 0x20584F56) //56 4F 58 20 + { + //TraceLog(LOG_ERROR, "Not an MagicaVoxel File format"); + return VOX_ERROR_INVALID_FORMAT; + } + + unsigned long version; + + version = *((unsigned long*)fileDataPtr); + fileDataPtr += sizeof(unsigned long); + + if (version < 150) + { + //TraceLog(LOG_ERROR, "MagicaVoxel version too old"); + return VOX_ERROR_FILE_VERSION_TOO_OLD; + } + + + // header + //4 bytes: chunk id + //4 bytes: size of chunk contents (n) + //4 bytes: total size of children chunks(m) + + //// chunk content + //n bytes: chunk contents + + //// children chunks : m bytes + //{ child chunk 0 } + //{ child chunk 1 } + unsigned long sizeX, sizeY, sizeZ; + sizeX = sizeY = sizeZ = 0; + unsigned long numVoxels = 0; + int offsetX, offsetY, offsetZ; + offsetX = offsetY = offsetZ = 0; + + while (fileDataPtr < endfileDataPtr) + { + char szChunkName[5]; + memcpy(szChunkName, fileDataPtr, 4); + szChunkName[4] = 0; + fileDataPtr += 4; + + unsigned long chunkSize = *((unsigned long*)fileDataPtr); + fileDataPtr += sizeof(unsigned long); + + unsigned long chunkTotalChildSize = *((unsigned long*)fileDataPtr); + fileDataPtr += sizeof(unsigned long); + + + if (strcmp(szChunkName, "SIZE") == 0) + { + //(4 bytes x 3 : x, y, z ) + sizeX = *((unsigned long*)fileDataPtr); + fileDataPtr += sizeof(unsigned long); + + sizeY = *((unsigned long*)fileDataPtr); + fileDataPtr += sizeof(unsigned long); + + sizeZ = *((unsigned long*)fileDataPtr); + fileDataPtr += sizeof(unsigned long); + + //Alloc vox array + Vox_AllocArray(voxarray, sizeX, sizeY, sizeZ); + } + else if (strcmp(szChunkName, "XYZI") == 0) + { + unsigned char vx, vy, vz, vi; + + //(numVoxels : 4 bytes ) + //(each voxel: 1 byte x 4 : x, y, z, colorIndex ) x numVoxels + numVoxels = *((unsigned long*)fileDataPtr); + fileDataPtr += sizeof(unsigned long); + + while (numVoxels > 0) + { + vx = *((unsigned char*)fileDataPtr++); + vy = *((unsigned char*)fileDataPtr++); + vz = *((unsigned char*)fileDataPtr++); + vi = *((unsigned char*)fileDataPtr++); + + Vox_SetVoxel(voxarray, vx, vy, vz, vi); + + numVoxels--; + } + } + else if (strcmp(szChunkName, "RGBA") == 0) + { + Color col; + + //(each pixel: 1 byte x 4 : r, g, b, a ) x 256 + for (int i = 0; i < 256 - 1; i++) + { + col.r = *((unsigned char*)fileDataPtr++); + col.g = *((unsigned char*)fileDataPtr++); + col.b = *((unsigned char*)fileDataPtr++); + col.a = *((unsigned char*)fileDataPtr++); + + voxarray->palette[i + 1] = col; + } + + } + else + { + fileDataPtr += chunkSize; + } + } + + //TraceLog(LOG_INFO, TextFormat("Vox Size : %dx%dx%d", sizeX, sizeY, sizeZ)); + + //TraceLog(LOG_INFO, TextFormat("Vox Chunks Count : %d/%d", pvoxArray->chunksAllocated, pvoxArray->chunksTotal)); + + + ////////////////////////////////////////////////////////// + // Building Mesh + // TODO compute globals indices array + + //TraceLog(LOG_INFO, TextFormat("Building VOX Mesh : %s", pszfileName)); + + // Init Arrays + initArrayVector3(&voxarray->vertices, 3 * 1024); + initArrayUShort(&voxarray->indices, 3 * 1024); + initArrayColor(&voxarray->colors, 3 * 1024); + + // Create vertices and indices buffers + int x, y, z; + + for (x = 0; x <= voxarray->sizeX; x++) + { + for (z = 0; z <= voxarray->sizeZ; z++) + { + for (y = 0; y <= voxarray->sizeY; y++) + { + unsigned char matID = Vox_GetVoxel(voxarray, x, y, z); + if (matID != 0) + Vox_Build_Voxel(voxarray, x, y, z, matID); + } + } + } + + + + return VOX_SUCCESS; +} + +void Vox_FreeArrays(VoxArray3D* voxarray) +{ + //Free chunks + if (voxarray->m_arrayChunks != 0) + { + for (int i = 0; i < voxarray->chunksTotal; i++) + { + CubeChunk3D* chunk = &voxarray->m_arrayChunks[i]; + if (chunk->m_array != 0) + { + chunk->arraySize = 0; + MemFree(chunk->m_array); + } + } + + MemFree(voxarray->m_arrayChunks); + voxarray->m_arrayChunks = 0; + voxarray->arrayChunksSize = 0; + + voxarray->chunksSizeX = voxarray->chunksSizeY = voxarray->chunksSizeZ = 0; + voxarray->chunksTotal = 0; + voxarray->chunksAllocated = 0; + voxarray->ChunkFlattenOffset = 0; + voxarray->sizeX = voxarray->sizeY = voxarray->sizeZ = 0; + } + + //Free arrays + freeArrayVector3(&voxarray->vertices); + freeArrayUShort(&voxarray->indices); + freeArrayColor(&voxarray->colors); +} + +#endif //VOX_LOADER_IMPLEMENTATION diff --git a/src/models.c b/src/models.c index b233cf2af..168ac2c31 100644 --- a/src/models.c +++ b/src/models.c @@ -8,6 +8,8 @@ * #define SUPPORT_FILEFORMAT_MTL * #define SUPPORT_FILEFORMAT_IQM * #define SUPPORT_FILEFORMAT_GLTF +* #define SUPPORT_FILEFORMAT_VOX +* * Selected desired fileformats to be supported for model data loading. * * #define SUPPORT_MESH_GENERATION @@ -71,6 +73,11 @@ #include "external/stb_image.h" // glTF texture images loading #endif +#if defined(SUPPORT_FILEFORMAT_VOX) + #define VOX_LOADER_IMPLEMENTATION + #include "external/vox_loader.h" // vox file format loading +#endif + #if defined(SUPPORT_MESH_GENERATION) #define PAR_MALLOC(T, N) ((T*)RL_MALLOC(N*sizeof(T))) #define PAR_CALLOC(T, N) ((T*)RL_CALLOC(N*sizeof(T), 1)) @@ -131,6 +138,9 @@ static void GetGLTFPrimitiveCount(cgltf_node *node, int *outCount); static bool ReadGLTFValue(cgltf_accessor *acc, unsigned int index, void *variable); static void *ReadGLTFValuesAs(cgltf_accessor *acc, cgltf_component_type type, bool adjustOnDownCasting); #endif +#if defined(SUPPORT_FILEFORMAT_VOX) +static Model LoadVOX(const char* filename); //Load VOX mesh data +#endif //---------------------------------------------------------------------------------- // Module Functions Definition @@ -718,6 +728,9 @@ Model LoadModel(const char *fileName) #if defined(SUPPORT_FILEFORMAT_GLTF) if (IsFileExtension(fileName, ".gltf;.glb")) model = LoadGLTF(fileName); #endif +#if defined(SUPPORT_FILEFORMAT_VOX) + if (IsFileExtension(fileName, ".vox")) model = LoadVOX(fileName); +#endif // Make sure model transform is set to identity matrix! model.transform = MatrixIdentity(); @@ -5469,3 +5482,100 @@ static void GetGLTFPrimitiveCount(cgltf_node *node, int *outCount) } #endif + +#if defined(SUPPORT_FILEFORMAT_VOX) +// Load OBJ mesh data +static Model LoadVOX(const char* fileName) +{ + Model model = { 0 }; + int nbvertices = 0; + int meshescount = 0; + + ////////////////////////////////// + // Load MagicaVoxel fileformat + + VoxArray3D voxarray; + int ret = Vox_LoadFileName(fileName, &voxarray); + + if (ret != VOX_SUCCESS) + { + TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to load VOX data", fileName); + return model; + } + else + { + // Compute meshes count + nbvertices = voxarray.vertices.used; + meshescount = 1 + (nbvertices / 65536); + + TRACELOG(LOG_INFO, "MODEL: [%s] VOX data loaded successfully : %i vertices/%i meshes", fileName, nbvertices, meshescount); + } + + ////////////////////////////////// + // Build model + + // Build Models from meshes + model.transform = MatrixIdentity(); + + model.meshCount = meshescount; + model.meshes = (Mesh*)MemAlloc(model.meshCount * sizeof(Mesh)); + + model.meshMaterial = (int*)MemAlloc(model.meshCount * sizeof(int)); + + model.materialCount = 1; + model.materials = (Material*)MemAlloc(model.materialCount * sizeof(Material)); + model.materials[0] = LoadMaterialDefault(); + + + // Init model's meshes + int verticesRemain = voxarray.vertices.used; + int verticesMax = 65532; //5461 voxels x 12 vertices per voxel -> 65532 (must be inf 65536) + + Vector3* pvertices = voxarray.vertices.array; //6*4=12 vertices per voxel + Color* pcolors = voxarray.colors.array; + unsigned short* pindices = voxarray.indices.array; //5461 * 6 * 6 -> 196596 indices max per mesh + + int size; + + for (int idxMesh = 0; idxMesh < meshescount; idxMesh++) + { + Mesh* pmesh = &model.meshes[idxMesh]; + memset(pmesh, 0, sizeof(Mesh)); + + // Copy Vertices + pmesh->vertexCount = (int)fmin(verticesMax, verticesRemain); + + size = pmesh->vertexCount * sizeof(float) * 3; + pmesh->vertices = MemAlloc(size); + memcpy(pmesh->vertices, pvertices, size); + + //Copy Indices TODO compute globals indices array + size = voxarray.indices.used * sizeof(unsigned short); + pmesh->indices = MemAlloc(size); + memcpy(pmesh->indices, pindices, size); + + pmesh->triangleCount = (pmesh->vertexCount / 4) * 2; + + // Copy Colors + size = pmesh->vertexCount * sizeof(Color); + pmesh->colors = MemAlloc(size); + memcpy(pmesh->colors, pcolors, size); + + // First material index + model.meshMaterial[idxMesh] = 0; + + // Build GPU mesh + UploadMesh(pmesh, false); + + //Next + verticesRemain -= verticesMax; + pvertices += verticesMax; + pcolors += verticesMax; + } + + //Free arrays + Vox_FreeArrays(&voxarray); + + return model; +} +#endif \ No newline at end of file