From dcf5f45f687f2a534286aecd5e6471a0440b0c21 Mon Sep 17 00:00:00 2001 From: raysan5 Date: Fri, 20 May 2016 12:28:07 +0200 Subject: [PATCH] Add lighting system -IN PROGRESS- Improved materials --- src/models.c | 12 ++++ src/raylib.h | 30 ++++++++- src/rlgl.c | 167 +++++++++++++++++++++++++++++++++++++++++++++++++-- src/rlgl.h | 25 ++++++++ 4 files changed, 228 insertions(+), 6 deletions(-) diff --git a/src/models.c b/src/models.c index 6d72d1e3..44e30390 100644 --- a/src/models.c +++ b/src/models.c @@ -732,6 +732,18 @@ Material LoadDefaultMaterial(void) return material; } +// Load standard material (uses standard models shader) +// NOTE: Standard shader supports multiple maps and lights +Material LoadStandardMaterial(void) +{ + Material material = LoadDefaultMaterial(); + + //material.shader = GetStandardShader(); + + return material; +} + +// Unload material from memory void UnloadMaterial(Material material) { rlDeleteTextures(material.texDiffuse.id); diff --git a/src/raylib.h b/src/raylib.h index fc1914bb..d98a0797 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -422,13 +422,38 @@ typedef struct Material { float normalDepth; // Normal map depth } Material; -// 3d Model type +// Model type typedef struct Model { Mesh mesh; // Vertex data buffers (RAM and VRAM) Matrix transform; // Local transform matrix Material material; // Shader and textures data } Model; +// Light type +// TODO: Review contained data to support different light types and features +typedef struct LightData { + int id; + int type; // LIGHT_POINT, LIGHT_DIRECTIONAL, LIGHT_SPOT + bool enabled; + + Vector3 position; + Vector3 direction; // Used on LIGHT_DIRECTIONAL and LIGHT_SPOT (cone direction) + float attenuation; // Lost of light intensity with distance (use radius?) + + Color diffuse; // Use Vector3 diffuse (including intensities)? + float intensity; + + Color specular; + //float specFactor; // Specular intensity ? + + //Color ambient; // Required? + + float coneAngle; // SpotLight +} LightData, *Light; + +// Light types +typedef enum { LIGHT_POINT, LIGHT_DIRECTIONAL, LIGHT_SPOT } LightType; + // Ray type (useful for raycast) typedef struct Ray { Vector3 position; @@ -849,6 +874,9 @@ void SetShaderValueMatrix(Shader shader, int uniformLoc, Matrix mat); // S void SetBlendMode(int mode); // Set blending mode (alpha, additive, multiplied) +Light CreateLight(int type, Vector3 position, Color diffuse); // Create a new light, initialize it and add to pool +void DestroyLight(Light light); // Destroy a light and take it out of the list + //---------------------------------------------------------------------------------- // Physics System Functions (Module: physac) //---------------------------------------------------------------------------------- diff --git a/src/rlgl.c b/src/rlgl.c index cc2b8942..e971d747 100644 --- a/src/rlgl.c +++ b/src/rlgl.c @@ -71,6 +71,8 @@ #define MAX_DRAWS_BY_TEXTURE 256 // Draws are organized by texture changes #define TEMP_VERTEX_BUFFER_SIZE 4096 // Temporal Vertex Buffer (required for vertex-transformations) // NOTE: Every vertex are 3 floats (12 bytes) + +#define MAX_LIGHTS 8 // Max lights supported by standard shader #ifndef GL_SHADING_LANGUAGE_VERSION #define GL_SHADING_LANGUAGE_VERSION 0x8B8C @@ -199,6 +201,10 @@ static bool texCompETC1Supported = false; // ETC1 texture compression support static bool texCompETC2Supported = false; // ETC2/EAC texture compression support static bool texCompPVRTSupported = false; // PVR texture compression support static bool texCompASTCSupported = false; // ASTC texture compression support + +// Lighting data +static Light lights[MAX_LIGHTS]; // Lights pool +static int lightsCount; // Counts current enabled physic objects #endif // Compressed textures support flags @@ -227,6 +233,7 @@ static void LoadCompressedTexture(unsigned char *data, int width, int height, in static unsigned int LoadShaderProgram(char *vShaderStr, char *fShaderStr); // Load custom shader strings and return program id static Shader LoadDefaultShader(void); // Load default shader (just vertex positioning and texture coloring) +static Shader LoadStandardShader(void); // Load standard shader (support materials and lighting) static void LoadDefaultShaderLocations(Shader *shader); // Bind default shader locations (attributes and uniforms) static void UnloadDefaultShader(void); // Unload default shader @@ -235,6 +242,8 @@ static void UpdateDefaultBuffers(void); // Update default internal buffers ( static void DrawDefaultBuffers(void); // Draw default internal buffers vertex data static void UnloadDefaultBuffers(void); // Unload default internal buffers vertex data from CPU and GPU +static void SetShaderLights(Shader shader); // Sets shader uniform values for lights array + static char *ReadTextFile(const char *fileName); #endif @@ -1749,11 +1758,19 @@ void rlglDrawMesh(Mesh mesh, Material material, Matrix transform) // Send combined model-view-projection matrix to shader glUniformMatrix4fv(material.shader.mvpLoc, 1, false, MatrixToFloat(matMVP)); - // Apply color tinting (material.colDiffuse) - // NOTE: Just update one uniform on fragment shader - float vColor[4] = { (float)material.colDiffuse.r/255, (float)material.colDiffuse.g/255, (float)material.colDiffuse.b/255, (float)material.colDiffuse.a/255 }; - glUniform4fv(material.shader.tintColorLoc, 1, vColor); - + // Setup shader uniforms for material related data + // TODO: Check if using standard shader to get location points + + // Upload to shader material.colDiffuse + float vColorDiffuse[4] = { (float)material.colDiffuse.r/255, (float)material.colDiffuse.g/255, (float)material.colDiffuse.b/255, (float)material.colDiffuse.a/255 }; + glUniform4fv(material.shader.tintColorLoc, 1, vColorDiffuse); + + // TODO: Upload to shader material.colAmbient + // glUniform4f(???, (float)material.colAmbient.r/255, (float)material.colAmbient.g/255, (float)material.colAmbient.b/255, (float)material.colAmbient.a/255); + + // TODO: Upload to shader material.colSpecular + // glUniform4f(???, (float)material.colSpecular.r/255, (float)material.colSpecular.g/255, (float)material.colSpecular.b/255, (float)material.colSpecular.a/255); + // Set shader textures (diffuse, normal, specular) glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, material.texDiffuse.id); @@ -1764,6 +1781,9 @@ void rlglDrawMesh(Mesh mesh, Material material, Matrix transform) glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, material.texNormal.id); glUniform1i(material.shader.mapNormalLoc, 1); // Texture fits in active texture unit 1 + + // TODO: Upload to shader normalDepth + //glUniform1f(???, material.normalDepth); } if ((material.texSpecular.id != 0) && (material.shader.mapSpecularLoc != -1)) @@ -1771,7 +1791,13 @@ void rlglDrawMesh(Mesh mesh, Material material, Matrix transform) glActiveTexture(GL_TEXTURE2); glBindTexture(GL_TEXTURE_2D, material.texSpecular.id); glUniform1i(material.shader.mapSpecularLoc, 2); // Texture fits in active texture unit 2 + + // TODO: Upload to shader glossiness + //glUniform1f(???, material.glossiness); } + + // Setup shader uniforms for lights + SetShaderLights(material.shader); if (vaoSupported) { @@ -2198,6 +2224,55 @@ void SetBlendMode(int mode) } } +// Create a new light, initialize it and add to pool +// TODO: Review creation parameters (only generic ones) +Light CreateLight(int type, Vector3 position, Color diffuse) +{ + // Allocate dynamic memory + Light light = (Light)malloc(sizeof(LightData)); + + // Initialize light values with generic values + light->id = lightsCount; + light->type = type; + light->enabled = true; + + light->position = position; + light->direction = (Vector3){ 0.0f, 0.0f, 0.0f }; + light->intensity = 1.0f; + light->diffuse = diffuse; + light->specular = WHITE; + + // Add new light to the array + lights[lightsCount] = light; + + // Increase enabled lights count + lightsCount++; + + return light; +} + +// Destroy a light and take it out of the list +void DestroyLight(Light light) +{ + // Free dynamic memory allocation + free(lights[light->id]); + + // Remove *obj from the pointers array + for (int i = light->id; i < lightsCount; i++) + { + // Resort all the following pointers of the array + if ((i + 1) < lightsCount) + { + lights[i] = lights[i + 1]; + lights[i]->id = lights[i + 1]->id; + } + else free(lights[i]); + } + + // Decrease enabled physic objects count + lightsCount--; +} + //---------------------------------------------------------------------------------- // Module specific Functions Definition //---------------------------------------------------------------------------------- @@ -2415,6 +2490,32 @@ static Shader LoadDefaultShader(void) return shader; } +// Load standard shader +// NOTE: This shader supports: +// - Up to 3 different maps: diffuse, normal, specular +// - Material properties: colDiffuse, colAmbient, colSpecular, glossiness, normalDepth +// - Up to 8 lights: Point, Directional or Spot +static Shader LoadStandardShader(void) +{ + Shader shader; + + char *vShaderStr; + char *fShaderStr; + + // TODO: Implement standard uber-shader, supporting all features (GLSL 100 / GLSL 330) + + // NOTE: Shader could be quite extensive so it could be implemented in external files (standard.vs/standard.fs) + + shader.id = LoadShaderProgram(vShaderStr, fShaderStr); + + if (shader.id != 0) TraceLog(INFO, "[SHDR ID %i] Standard shader loaded successfully", shader.id); + else TraceLog(WARNING, "[SHDR ID %i] Standard shader could not be loaded", shader.id); + + if (shader.id != 0) LoadDefaultShaderLocations(&shader); // TODO: Review locations fetching + + return shader; +} + // Get location handlers to for shader attributes and uniforms // NOTE: If any location is not found, loc point becomes -1 static void LoadDefaultShaderLocations(Shader *shader) @@ -2900,6 +3001,62 @@ static void UnloadDefaultBuffers(void) free(quads.indices); } +// Sets shader uniform values for lights array +// NOTE: It would be far easier with shader UBOs but are not supported on OpenGL ES 2.0f +// TODO: Review memcpy() and parameters pass +static void SetShaderLights(Shader shader) +{ + /* + // NOTE: Standard Shader must include the following data: + + // Shader Light struct + struct Light { + vec3 position; + vec3 direction; + + vec3 diffuse; + float intensity; + } + + const int maxLights = 8; + uniform int lightsCount; // Number of lights + uniform Light lights[maxLights]; + */ + + int locPoint; + char locName[32] = "lights[x].position\0"; + + glUseProgram(shader.id); + + locPoint = glGetUniformLocation(shader.id, "lightsCount"); + glUniform1i(locPoint, lightsCount); + + for (int i = 0; i < lightsCount; i++) + { + locName[7] = '0' + i; + + memcpy(&locName[10], "position\0", strlen("position\0")); + locPoint = glGetUniformLocation(shader.id, locName); + glUniform3f(locPoint, lights[i]->position.x, lights[i]->position.y, lights[i]->position.z); + + memcpy(&locName[10], "direction\0", strlen("direction\0")); + locPoint = glGetUniformLocation(shader.id, locName); + glUniform3f(locPoint, lights[i]->direction.x, lights[i]->direction.y, lights[i]->direction.z); + + memcpy(&locName[10], "diffuse\0", strlen("diffuse\0")); + locPoint = glGetUniformLocation(shader.id, locName); + glUniform4f(locPoint, (float)lights[i]->diffuse.r/255, (float)lights[i]->diffuse.g/255, (float)lights[i]->diffuse.b/255, (float)lights[i]->diffuse.a/255 ); + + memcpy(&locName[10], "intensity\0", strlen("intensity\0")); + locPoint = glGetUniformLocation(shader.id, locName); + glUniform1f(locPoint, lights[i]->intensity); + + // TODO: Pass to the shader any other required data from LightData struct + } + + glUseProgram(0); +} + // Read text data from file // NOTE: text chars array should be freed manually static char *ReadTextFile(const char *fileName) diff --git a/src/rlgl.h b/src/rlgl.h index a557ffa2..39941b33 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -209,6 +209,28 @@ typedef enum { OPENGL_11 = 1, OPENGL_33, OPENGL_ES_20 } GlVersion; float glossiness; float normalDepth; } Material; + + // Light type + // TODO: Review contained data to support different light types and features + typedef struct LightData { + int id; + int type; // LIGHT_POINT, LIGHT_DIRECTIONAL, LIGHT_SPOT + bool enabled; + + Vector3 position; + Vector3 direction; // Used on LIGHT_DIRECTIONAL and LIGHT_SPOT (cone direction) + float attenuation; // Lost of light intensity with distance (use radius?) + + Color diffuse; // Use Vector3 diffuse (including intensities)? + float intensity; + + Color specular; + //float specFactor; // Specular intensity ? + + //Color ambient; // Required? + + float coneAngle; // SpotLight + } LightData, *Light; // Color blending modes (pre-defined) typedef enum { BLEND_ALPHA = 0, BLEND_ADDITIVE, BLEND_MULTIPLIED } BlendMode; @@ -311,6 +333,9 @@ void SetShaderValuei(Shader shader, int uniformLoc, int *value, int size); // S void SetShaderValueMatrix(Shader shader, int uniformLoc, Matrix mat); // Set shader uniform value (matrix 4x4) void SetBlendMode(int mode); // Set blending mode (alpha, additive, multiplied) + +Light CreateLight(int type, Vector3 position, Color diffuse); // Create a new light, initialize it and add to pool +void DestroyLight(Light light); // Destroy a light and take it out of the list #endif #ifdef __cplusplus