Browse Source

feat: vox_loader normals and new voxels shader (#3843)

johann nadalutti 1 year ago
parent
commit
53221eb799

+ 177 - 105
examples/models/models_loading_vox.c

@@ -17,117 +17,189 @@
 
 
 #include "raymath.h"        // Required for: MatrixTranslate()
 #include "raymath.h"        // Required for: MatrixTranslate()
 
 
-#define MAX_VOX_FILES  3
+#define MAX_VOX_FILES  4
+
+#define RLIGHTS_IMPLEMENTATION
+#include "rlights.h"
+
+#if defined(PLATFORM_DESKTOP)
+#define GLSL_VERSION            330
+#else   // PLATFORM_ANDROID, PLATFORM_WEB
+#define GLSL_VERSION            100
+#endif
 
 
 //------------------------------------------------------------------------------------
 //------------------------------------------------------------------------------------
 // Program main entry point
 // Program main entry point
 //------------------------------------------------------------------------------------
 //------------------------------------------------------------------------------------
 int main(void)
 int main(void)
 {
 {
-    // Initialization
-    //--------------------------------------------------------------------------------------
-    const int screenWidth = 800;
-    const int screenHeight = 450;
-
-    const char *voxFileNames[] = {
-        "resources/models/vox/chr_knight.vox",
-        "resources/models/vox/chr_sword.vox",
-        "resources/models/vox/monu9.vox"
-    };
-
-    InitWindow(screenWidth, screenHeight, "raylib [models] example - magicavoxel loading");
-
-    // Define the camera to look into our 3d world
-    Camera camera = { 0 };
-    camera.position = (Vector3){ 10.0f, 10.0f, 10.0f }; // Camera position
-    camera.target = (Vector3){ 0.0f, 0.0f, 0.0f };      // Camera looking at point
-    camera.up = (Vector3){ 0.0f, 1.0f, 0.0f };          // Camera up vector (rotation towards target)
-    camera.fovy = 45.0f;                                // Camera field-of-view Y
-    camera.projection = CAMERA_PERSPECTIVE;             // Camera projection type
-
-    // Load MagicaVoxel files
-    Model models[MAX_VOX_FILES] = { 0 };
-
-    for (int i = 0; i < MAX_VOX_FILES; i++)
-    {
-        // Load VOX file and measure time
-        double t0 = GetTime()*1000.0;
-        models[i] = LoadModel(voxFileNames[i]);
-        double t1 = GetTime()*1000.0;
-
-        TraceLog(LOG_WARNING, TextFormat("[%s] File loaded in %.3f ms", voxFileNames[i], t1 - t0));
-
-        // Compute model translation matrix to center model on draw position (0, 0 , 0)
-        BoundingBox bb = GetModelBoundingBox(models[i]);
-        Vector3 center = { 0 };
-        center.x = bb.min.x  + (((bb.max.x - bb.min.x)/2));
-        center.z = bb.min.z  + (((bb.max.z - bb.min.z)/2));
-
-        Matrix matTranslate = MatrixTranslate(-center.x, 0, -center.z);
-        models[i].transform = matTranslate;
-    }
-
-    int currentModel = 0;
-
-    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, CAMERA_ORBITAL);
-
-        // Cycle between models on mouse click
-        if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) currentModel = (currentModel + 1)%MAX_VOX_FILES;
-
-        // Cycle between models on key pressed
-        if (IsKeyPressed(KEY_RIGHT))
-        {
-            currentModel++;
-            if (currentModel >= MAX_VOX_FILES) currentModel = 0;
-        }
-        else if (IsKeyPressed(KEY_LEFT))
-        {
-            currentModel--;
-            if (currentModel < 0) currentModel = MAX_VOX_FILES - 1;
-        }
-        //----------------------------------------------------------------------------------
-
-        // Draw
-        //----------------------------------------------------------------------------------
-        BeginDrawing();
-
-            ClearBackground(RAYWHITE);
-
-            // Draw 3D model
-            BeginMode3D(camera);
-
-                DrawModel(models[currentModel], (Vector3){ 0, 0, 0 }, 1.0f, WHITE);
-                DrawGrid(10, 1.0);
-
-            EndMode3D();
-
-            // Display info
-            DrawRectangle(10, 400, 310, 30, Fade(SKYBLUE, 0.5f));
-            DrawRectangleLines(10, 400, 310, 30, Fade(DARKBLUE, 0.5f));
-            DrawText("MOUSE LEFT BUTTON to CYCLE VOX MODELS", 40, 410, 10, BLUE);
-            DrawText(TextFormat("File: %s", GetFileName(voxFileNames[currentModel])), 10, 10, 20, GRAY);
-
-        EndDrawing();
-        //----------------------------------------------------------------------------------
-    }
-
-    // De-Initialization
-    //--------------------------------------------------------------------------------------
-    // Unload models data (GPU VRAM)
-    for (int i = 0; i < MAX_VOX_FILES; i++) UnloadModel(models[i]);
-
-    CloseWindow();          // Close window and OpenGL context
-    //--------------------------------------------------------------------------------------
-
-    return 0;
+	// Initialization
+	//--------------------------------------------------------------------------------------
+	const int screenWidth = 800;
+	const int screenHeight = 450;
+
+	const char* voxFileNames[] = {
+		"resources/models/vox/chr_knight.vox",
+		"resources/models/vox/chr_sword.vox",
+		"resources/models/vox/monu9.vox",
+		"resources/models/vox/fez.vox"
+	};
+
+	InitWindow(screenWidth, screenHeight, "raylib [models] example - magicavoxel loading");
+
+	// Define the camera to look into our 3d world
+	Camera camera = { 0 };
+	camera.position = (Vector3){ 10.0f, 10.0f, 10.0f }; // Camera position
+	camera.target = (Vector3){ 0.0f, 0.0f, 0.0f };      // Camera looking at point
+	camera.up = (Vector3){ 0.0f, 1.0f, 0.0f };          // Camera up vector (rotation towards target)
+	camera.fovy = 45.0f;                                // Camera field-of-view Y
+	camera.projection = CAMERA_PERSPECTIVE;             // Camera projection type
+
+	//--------------------------------------------------------------------------------------
+	// Load MagicaVoxel files
+	Model models[MAX_VOX_FILES] = { 0 };
+
+	for (int i = 0; i < MAX_VOX_FILES; i++)
+	{
+		// Load VOX file and measure time
+		double t0 = GetTime() * 1000.0;
+		models[i] = LoadModel(voxFileNames[i]);
+		double t1 = GetTime() * 1000.0;
+
+		TraceLog(LOG_WARNING, TextFormat("[%s] File loaded in %.3f ms", voxFileNames[i], t1 - t0));
+
+		// Compute model translation matrix to center model on draw position (0, 0 , 0)
+		BoundingBox bb = GetModelBoundingBox(models[i]);
+		Vector3 center = { 0 };
+		center.x = bb.min.x + (((bb.max.x - bb.min.x) / 2));
+		center.z = bb.min.z + (((bb.max.z - bb.min.z) / 2));
+
+		Matrix matTranslate = MatrixTranslate(-center.x, 0, -center.z);
+		models[i].transform = matTranslate;
+	}
+
+	int currentModel = 0;
+
+	//--------------------------------------------------------------------------------------
+	// Load voxel shader
+	Shader shader = LoadShader(TextFormat("resources/shaders/glsl%i/voxel_lighting.vs", GLSL_VERSION),
+		TextFormat("resources/shaders/glsl%i/voxel_lighting.fs", GLSL_VERSION));
+
+	// Get some required shader locations
+	shader.locs[SHADER_LOC_VECTOR_VIEW] = GetShaderLocation(shader, "viewPos");
+	// NOTE: "matModel" location name is automatically assigned on shader loading, 
+	// no need to get the location again if using that uniform name
+	//shader.locs[SHADER_LOC_MATRIX_MODEL] = GetShaderLocation(shader, "matModel");
+
+	// Ambient light level (some basic lighting)
+	int ambientLoc = GetShaderLocation(shader, "ambient");
+	SetShaderValue(shader, ambientLoc, (float[4]) { 0.1f, 0.1f, 0.1f, 1.0f }, SHADER_UNIFORM_VEC4);
+
+	// Assign out lighting shader to model
+	for (int i = 0; i < MAX_VOX_FILES; i++)
+	{
+		Model m = models[i];
+		for (int j = 0; j < m.materialCount; j++)
+		{
+			m.materials[j].shader = shader;
+		}
+	}
+
+	// Create lights
+	Light lights[MAX_LIGHTS] = { 0 };
+	lights[0] = CreateLight(LIGHT_POINT, (Vector3) { -20, 20, -20 }, Vector3Zero(), GRAY, shader);
+	lights[1] = CreateLight(LIGHT_POINT, (Vector3) { 20, -20, 20 }, Vector3Zero(), GRAY, shader);
+	lights[2] = CreateLight(LIGHT_POINT, (Vector3) { -20, 20, 20 }, Vector3Zero(), GRAY, shader);
+	lights[3] = CreateLight(LIGHT_POINT, (Vector3) { 20, -20, -20 }, Vector3Zero(), GRAY, shader);
+
+
+	SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
+
+	//--------------------------------------------------------------------------------------
+	Vector3 modelpos = { 0 };
+	Vector3 camerarot = { 0 };
+
+	// Main game loop
+	while (!WindowShouldClose())    // Detect window close button or ESC key
+	{
+		// Update
+		//----------------------------------------------------------------------------------
+		if (IsMouseButtonDown(MOUSE_BUTTON_MIDDLE))
+		{
+			const Vector2 mouseDelta = GetMouseDelta();
+			camerarot.x = mouseDelta.x * 0.05f;
+			camerarot.y = mouseDelta.y * 0.05f;
+		}
+		else
+		{
+			camerarot.x = 0;
+			camerarot.y = 0;
+		}
+
+		UpdateCameraPro(&camera,
+			(Vector3) {
+			(IsKeyDown(KEY_W) || IsKeyDown(KEY_UP)) * 0.1f -      // Move forward-backward
+				(IsKeyDown(KEY_S) || IsKeyDown(KEY_DOWN)) * 0.1f,
+				(IsKeyDown(KEY_D) || IsKeyDown(KEY_RIGHT)) * 0.1f -   // Move right-left
+				(IsKeyDown(KEY_A) || IsKeyDown(KEY_LEFT)) * 0.1f,
+				0.0f                                                // Move up-down
+		},
+			camerarot,
+			GetMouseWheelMove() * -2.0f);                              // Move to target (zoom)
+
+		// Cycle between models on mouse click
+		if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) currentModel = (currentModel + 1) % MAX_VOX_FILES;
+
+		// Update the shader with the camera view vector (points towards { 0.0f, 0.0f, 0.0f })
+		float cameraPos[3] = { camera.position.x, camera.position.y, camera.position.z };
+		SetShaderValue(shader, shader.locs[SHADER_LOC_VECTOR_VIEW], cameraPos, SHADER_UNIFORM_VEC3);
+
+		// Update light values (actually, only enable/disable them)
+		for (int i = 0; i < MAX_LIGHTS; i++) UpdateLightValues(shader, lights[i]);
+
+		//----------------------------------------------------------------------------------
+		// Draw
+		//----------------------------------------------------------------------------------
+		BeginDrawing();
+
+		ClearBackground(RAYWHITE);
+
+		// Draw 3D model
+		BeginMode3D(camera);
+
+		DrawModel(models[currentModel], modelpos, 1.0f, WHITE);
+		DrawGrid(10, 1.0);
+
+		// Draw spheres to show where the lights are
+		for (int i = 0; i < MAX_LIGHTS; i++)
+		{
+			if (lights[i].enabled) DrawSphereEx(lights[i].position, 0.2f, 8, 8, lights[i].color);
+			else DrawSphereWires(lights[i].position, 0.2f, 8, 8, ColorAlpha(lights[i].color, 0.3f));
+		}
+
+		EndMode3D();
+
+		// Display info
+		DrawRectangle(10, 400, 340, 60, Fade(SKYBLUE, 0.5f));
+		DrawRectangleLines(10, 400, 340, 60, Fade(DARKBLUE, 0.5f));
+		DrawText("MOUSE LEFT BUTTON to CYCLE VOX MODELS", 40, 410, 10, BLUE);
+		DrawText("MOUSE MIDDLE BUTTON to ZOOM OR ROTATE CAMERA", 40, 420, 10, BLUE);
+		DrawText("UP-DOWN-LEFT-RIGHT KEYS to MOVE CAMERA", 40, 430, 10, BLUE);
+		DrawText(TextFormat("File: %s", GetFileName(voxFileNames[currentModel])), 10, 10, 20, GRAY);
+
+		EndDrawing();
+		//----------------------------------------------------------------------------------
+	}
+
+	// De-Initialization
+	//--------------------------------------------------------------------------------------
+	// Unload models data (GPU VRAM)
+	for (int i = 0; i < MAX_VOX_FILES; i++) UnloadModel(models[i]);
+
+	CloseWindow();          // Close window and OpenGL context
+	//--------------------------------------------------------------------------------------
+
+	return 0;
 }
 }
 
 
 
 

BIN
examples/models/resources/models/vox/fez.vox


+ 76 - 0
examples/models/resources/shaders/glsl330/voxel_lighting.fs

@@ -0,0 +1,76 @@
+#version 330
+
+// Input vertex attributes (from vertex shader)
+in vec3 fragPosition;
+//in vec2 fragTexCoord;
+in vec4 fragColor;
+in vec3 fragNormal;
+
+// Input uniform values
+//uniform sampler2D texture0;
+uniform vec4 colDiffuse;
+
+// Output fragment color
+out vec4 finalColor;
+
+// NOTE: Add here your custom variables
+
+#define     MAX_LIGHTS              4
+#define     LIGHT_DIRECTIONAL       0
+#define     LIGHT_POINT             1
+
+struct Light {
+    int enabled;
+    int type;
+    vec3 position;
+    vec3 target;
+    vec4 color;
+};
+
+// Input lighting values
+uniform Light lights[MAX_LIGHTS];
+uniform vec4 ambient;
+uniform vec3 viewPos;
+
+void main()
+{
+    // Texel color fetching from texture sampler
+    //vec4 texelColor = texture(texture0, fragTexCoord);
+    vec3 lightDot = vec3(0.0);
+    vec3 normal = normalize(fragNormal);
+    vec3 viewD = normalize(viewPos - fragPosition);
+    vec3 specular = vec3(0.0);
+
+    // NOTE: Implement here your fragment shader code
+
+    for (int i = 0; i < MAX_LIGHTS; i++)
+    {
+        if (lights[i].enabled == 1)
+        {
+            vec3 light = vec3(0.0);
+
+            if (lights[i].type == LIGHT_DIRECTIONAL)
+            {
+                light = -normalize(lights[i].target - lights[i].position);
+            }
+
+            if (lights[i].type == LIGHT_POINT)
+            {
+                light = normalize(lights[i].position - fragPosition);
+            }
+
+            float NdotL = max(dot(normal, light), 0.0);
+            lightDot += lights[i].color.rgb*NdotL;
+
+            float specCo = 0.0;
+            if (NdotL > 0.0) specCo = pow(max(0.0, dot(viewD, reflect(-(light), normal))), 16.0); // 16 refers to shine
+            specular += specCo;
+        }
+    }
+
+    finalColor = (fragColor*((colDiffuse + vec4(specular, 1.0))*vec4(lightDot, 1.0)));
+    finalColor += fragColor*(ambient/10.0)*colDiffuse;
+
+    // Gamma correction
+    finalColor = pow(finalColor, vec4(1.0/2.2));
+}

+ 32 - 0
examples/models/resources/shaders/glsl330/voxel_lighting.vs

@@ -0,0 +1,32 @@
+#version 330
+
+// Input vertex attributes
+in vec3 vertexPosition;
+//in vec2 vertexTexCoord;
+in vec3 vertexNormal;
+in vec4 vertexColor;
+
+// Input uniform values
+uniform mat4 mvp;
+uniform mat4 matModel;
+uniform mat4 matNormal;
+
+// Output vertex attributes (to fragment shader)
+out vec3 fragPosition;
+//out vec2 fragTexCoord;
+out vec4 fragColor;
+out vec3 fragNormal;
+
+// NOTE: Add here your custom variables
+
+void main()
+{
+    // Send vertex attributes to fragment shader
+    fragPosition = vec3(matModel*vec4(vertexPosition, 1.0));
+    //fragTexCoord = vertexTexCoord;
+    fragColor = vertexColor;
+    fragNormal = normalize(vec3(matNormal*vec4(vertexNormal, 1.0)));
+
+    // Calculate final vertex position
+    gl_Position = mvp*vec4(vertexPosition, 1.0);
+}

+ 170 - 0
examples/models/rlights.h

@@ -0,0 +1,170 @@
+/**********************************************************************************************
+*
+*   raylib.lights - Some useful functions to deal with lights data
+*
+*   CONFIGURATION:
+*
+*   #define RLIGHTS_IMPLEMENTATION
+*       Generates the implementation of the library into the included file.
+*       If not defined, the library is in header only mode and can be included in other headers 
+*       or source files without problems. But only ONE file should hold the implementation.
+*
+*   LICENSE: zlib/libpng
+*
+*   Copyright (c) 2017-2024 Victor Fisac (@victorfisac) and Ramon Santamaria (@raysan5)
+*
+*   This software is provided "as-is", without any express or implied warranty. In no event
+*   will the authors be held liable for any damages arising from the use of this software.
+*
+*   Permission is granted to anyone to use this software for any purpose, including commercial
+*   applications, and to alter it and redistribute it freely, subject to the following restrictions:
+*
+*     1. The origin of this software must not be misrepresented; you must not claim that you
+*     wrote the original software. If you use this software in a product, an acknowledgment
+*     in the product documentation would be appreciated but is not required.
+*
+*     2. Altered source versions must be plainly marked as such, and must not be misrepresented
+*     as being the original software.
+*
+*     3. This notice may not be removed or altered from any source distribution.
+*
+**********************************************************************************************/
+
+#ifndef RLIGHTS_H
+#define RLIGHTS_H
+
+//----------------------------------------------------------------------------------
+// Defines and Macros
+//----------------------------------------------------------------------------------
+#define MAX_LIGHTS  4         // Max dynamic lights supported by shader
+
+//----------------------------------------------------------------------------------
+// Types and Structures Definition
+//----------------------------------------------------------------------------------
+
+// Light data
+typedef struct {   
+    int type;
+    bool enabled;
+    Vector3 position;
+    Vector3 target;
+    Color color;
+    float attenuation;
+    
+    // Shader locations
+    int enabledLoc;
+    int typeLoc;
+    int positionLoc;
+    int targetLoc;
+    int colorLoc;
+    int attenuationLoc;
+} Light;
+
+// Light type
+typedef enum {
+    LIGHT_DIRECTIONAL = 0,
+    LIGHT_POINT
+} LightType;
+
+#ifdef __cplusplus
+extern "C" {            // Prevents name mangling of functions
+#endif
+
+//----------------------------------------------------------------------------------
+// Module Functions Declaration
+//----------------------------------------------------------------------------------
+Light CreateLight(int type, Vector3 position, Vector3 target, Color color, Shader shader);   // Create a light and get shader locations
+void UpdateLightValues(Shader shader, Light light);         // Send light properties to shader
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // RLIGHTS_H
+
+
+/***********************************************************************************
+*
+*   RLIGHTS IMPLEMENTATION
+*
+************************************************************************************/
+
+#if defined(RLIGHTS_IMPLEMENTATION)
+
+#include "raylib.h"
+
+//----------------------------------------------------------------------------------
+// Defines and Macros
+//----------------------------------------------------------------------------------
+// ...
+
+//----------------------------------------------------------------------------------
+// Types and Structures Definition
+//----------------------------------------------------------------------------------
+// ...
+
+//----------------------------------------------------------------------------------
+// Global Variables Definition
+//----------------------------------------------------------------------------------
+static int lightsCount = 0;    // Current amount of created lights
+
+//----------------------------------------------------------------------------------
+// Module specific Functions Declaration
+//----------------------------------------------------------------------------------
+// ...
+
+//----------------------------------------------------------------------------------
+// Module Functions Definition
+//----------------------------------------------------------------------------------
+
+// Create a light and get shader locations
+Light CreateLight(int type, Vector3 position, Vector3 target, Color color, Shader shader)
+{
+    Light light = { 0 };
+
+    if (lightsCount < MAX_LIGHTS)
+    {
+        light.enabled = true;
+        light.type = type;
+        light.position = position;
+        light.target = target;
+        light.color = color;
+
+        // NOTE: Lighting shader naming must be the provided ones
+        light.enabledLoc = GetShaderLocation(shader, TextFormat("lights[%i].enabled", lightsCount));
+        light.typeLoc = GetShaderLocation(shader, TextFormat("lights[%i].type", lightsCount));
+        light.positionLoc = GetShaderLocation(shader, TextFormat("lights[%i].position", lightsCount));
+        light.targetLoc = GetShaderLocation(shader, TextFormat("lights[%i].target", lightsCount));
+        light.colorLoc = GetShaderLocation(shader, TextFormat("lights[%i].color", lightsCount));
+
+        UpdateLightValues(shader, light);
+        
+        lightsCount++;
+    }
+
+    return light;
+}
+
+// Send light properties to shader
+// NOTE: Light shader locations should be available 
+void UpdateLightValues(Shader shader, Light light)
+{
+    // Send to shader light enabled state and type
+    SetShaderValue(shader, light.enabledLoc, &light.enabled, SHADER_UNIFORM_INT);
+    SetShaderValue(shader, light.typeLoc, &light.type, SHADER_UNIFORM_INT);
+
+    // Send to shader light position values
+    float position[3] = { light.position.x, light.position.y, light.position.z };
+    SetShaderValue(shader, light.positionLoc, position, SHADER_UNIFORM_VEC3);
+
+    // Send to shader light target position values
+    float target[3] = { light.target.x, light.target.y, light.target.z };
+    SetShaderValue(shader, light.targetLoc, target, SHADER_UNIFORM_VEC3);
+
+    // Send to shader light color values
+    float color[4] = { (float)light.color.r/(float)255, (float)light.color.g/(float)255, 
+                       (float)light.color.b/(float)255, (float)light.color.a/(float)255 };
+    SetShaderValue(shader, light.colorLoc, color, SHADER_UNIFORM_VEC4);
+}
+
+#endif // RLIGHTS_IMPLEMENTATION

+ 17 - 0
src/external/vox_loader.h

@@ -123,6 +123,7 @@ typedef struct {
 
 
     // Arrays for mesh build
     // Arrays for mesh build
     ArrayVector3 vertices;
     ArrayVector3 vertices;
+	ArrayVector3 normals;
     ArrayUShort indices;
     ArrayUShort indices;
     ArrayColor colors;
     ArrayColor colors;
 
 
@@ -292,6 +293,16 @@ const VoxVector3 SolidVertex[] = {
 	{1, 1, 1}    //7
 	{1, 1, 1}    //7
  };
  };
 
 
+const VoxVector3 FacesPerSideNormal[] = {
+	{ -1, 0, 0 }, //-X
+	{1, 0, 0 },   //+X
+	{0,-1, 0},    //-Y
+	{0, 1, 0},    //+Y
+	{0, 0, -1},   //-Z
+	{0, 0,  1},  //+Z
+};
+
+
 // Allocated VoxArray3D size
 // Allocated VoxArray3D size
 static void Vox_AllocArray(VoxArray3D* pvoxarray, int _sx, int _sy, int _sz)
 static void Vox_AllocArray(VoxArray3D* pvoxarray, int _sx, int _sy, int _sz)
 {
 {
@@ -508,6 +519,11 @@ static void Vox_Build_Voxel(VoxArray3D* pvoxArray, int x, int y, int z, int matI
 		insertArrayVector3(&pvoxArray->vertices, vertComputed[v2]);
 		insertArrayVector3(&pvoxArray->vertices, vertComputed[v2]);
 		insertArrayVector3(&pvoxArray->vertices, vertComputed[v3]);
 		insertArrayVector3(&pvoxArray->vertices, vertComputed[v3]);
 
 
+		insertArrayVector3(&pvoxArray->normals, FacesPerSideNormal[i]);
+		insertArrayVector3(&pvoxArray->normals, FacesPerSideNormal[i]);
+		insertArrayVector3(&pvoxArray->normals, FacesPerSideNormal[i]);
+		insertArrayVector3(&pvoxArray->normals, FacesPerSideNormal[i]);
+
 		VoxColor col = pvoxArray->palette[matID];
 		VoxColor col = pvoxArray->palette[matID];
 
 
 		insertArrayColor(&pvoxArray->colors, col);
 		insertArrayColor(&pvoxArray->colors, col);
@@ -653,6 +669,7 @@ int Vox_LoadFromMemory(unsigned char* pvoxData, unsigned int voxDataSize, VoxArr
 
 
 	// Init Arrays
 	// Init Arrays
 	initArrayVector3(&pvoxarray->vertices, 3 * 1024);
 	initArrayVector3(&pvoxarray->vertices, 3 * 1024);
+	initArrayVector3(&pvoxarray->normals, 3 * 1024);
 	initArrayUShort(&pvoxarray->indices, 3 * 1024);
 	initArrayUShort(&pvoxarray->indices, 3 * 1024);
 	initArrayColor(&pvoxarray->colors, 3 * 1024);
 	initArrayColor(&pvoxarray->colors, 3 * 1024);
 
 

+ 6 - 0
src/rmodels.c

@@ -5641,6 +5641,7 @@ static Model LoadVOX(const char *fileName)
 
 
     // 6*4 = 12 vertices per voxel
     // 6*4 = 12 vertices per voxel
     Vector3 *pvertices = (Vector3 *)voxarray.vertices.array;
     Vector3 *pvertices = (Vector3 *)voxarray.vertices.array;
+    Vector3* pnormals = (Vector3*)voxarray.normals.array;
     Color *pcolors = (Color *)voxarray.colors.array;
     Color *pcolors = (Color *)voxarray.colors.array;
 
 
     unsigned short *pindices = voxarray.indices.array;    // 5461*6*6 = 196596 indices max per mesh
     unsigned short *pindices = voxarray.indices.array;    // 5461*6*6 = 196596 indices max per mesh
@@ -5659,6 +5660,10 @@ static Model LoadVOX(const char *fileName)
         pmesh->vertices = RL_MALLOC(size);
         pmesh->vertices = RL_MALLOC(size);
         memcpy(pmesh->vertices, pvertices, size);
         memcpy(pmesh->vertices, pvertices, size);
 
 
+        // Copy normals
+        pmesh->normals = RL_MALLOC(size); //Rk. size as vertices
+        memcpy(pmesh->normals, pnormals, size);
+
         // Copy indices
         // Copy indices
         size = voxarray.indices.used*sizeof(unsigned short);
         size = voxarray.indices.used*sizeof(unsigned short);
         pmesh->indices = RL_MALLOC(size);
         pmesh->indices = RL_MALLOC(size);
@@ -5676,6 +5681,7 @@ static Model LoadVOX(const char *fileName)
 
 
         verticesRemain -= verticesMax;
         verticesRemain -= verticesMax;
         pvertices += verticesMax;
         pvertices += verticesMax;
+        pnormals += verticesMax;
         pcolors += verticesMax;
         pcolors += verticesMax;
     }
     }