From f9bab14fdb61ec7b6d407a4d3f742414c2842d50 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 26 Mar 2021 00:12:29 +0100 Subject: [PATCH] REVIEWED: DrawMeshInstanced() --- .../shaders/shaders_rlgl_mesh_instanced.c | 27 ++-- src/models.c | 120 +++++++++++++----- src/raylib.h | 4 +- src/rlgl.h | 26 +++- 4 files changed, 132 insertions(+), 45 deletions(-) diff --git a/examples/shaders/shaders_rlgl_mesh_instanced.c b/examples/shaders/shaders_rlgl_mesh_instanced.c index 3477b0cf..e786b8a0 100644 --- a/examples/shaders/shaders_rlgl_mesh_instanced.c +++ b/examples/shaders/shaders_rlgl_mesh_instanced.c @@ -59,15 +59,15 @@ int main(void) camera.fovy = 45.0f; camera.projection = CAMERA_PERSPECTIVE; - const int count = 10000; // Number of instances to display + const int instances = 10000; // Number of instances to display Mesh cube = GenMeshCube(1.0f, 1.0f, 1.0f); - Matrix *rotations = RL_MALLOC(count*sizeof(Matrix)); // Rotation state of instances - Matrix *rotationsInc = RL_MALLOC(count*sizeof(Matrix)); // Per-frame rotation animation of instances - Matrix *translations = RL_MALLOC(count*sizeof(Matrix)); // Locations of instances + Matrix *rotations = RL_MALLOC(instances*sizeof(Matrix)); // Rotation state of instances + Matrix *rotationsInc = RL_MALLOC(instances*sizeof(Matrix)); // Per-frame rotation animation of instances + Matrix *translations = RL_MALLOC(instances*sizeof(Matrix)); // Locations of instances // Scatter random cubes around - for (int i = 0; i < count; i++) + for (int i = 0; i < instances; i++) { x = GetRandomValue(-50, 50); y = GetRandomValue(-50, 50); @@ -84,7 +84,7 @@ int main(void) rotations[i] = MatrixIdentity(); } - Matrix *transforms = RL_MALLOC(count*sizeof(Matrix)); // Pre-multiplied transformations passed to rlgl + Matrix *transforms = RL_MALLOC(instances*sizeof(Matrix)); // Pre-multiplied transformations passed to rlgl Shader shader = LoadShader(TextFormat("resources/shaders/glsl%i/base_lighting_instanced.vs", GLSL_VERSION), TextFormat("resources/shaders/glsl%i/lighting.fs", GLSL_VERSION)); @@ -107,19 +107,18 @@ int main(void) SetCameraMode(camera, CAMERA_ORBITAL); // Set an orbital camera mode int textPositionY = 300; - - int framesCounter = 0; // Simple frames counter to manage animation + int framesCounter = 0; // Simple frames counter to manage animation - SetTargetFPS(fps); // Set our game to run at 60 frames-per-second + SetTargetFPS(fps); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- // Main game loop - while (!WindowShouldClose()) // Detect window close button or ESC key + while (!WindowShouldClose()) // Detect window close button or ESC key { // Update //---------------------------------------------------------------------------------- - UpdateCamera(&camera); + textPositionY = 300; framesCounter++; if (IsKeyDown(KEY_UP)) amp += 0.5f; @@ -148,7 +147,7 @@ int main(void) SetShaderValue(shader, shader.locs[SHADER_LOC_VECTOR_VIEW], cameraPos, SHADER_UNIFORM_VEC3); // Apply per-instance transformations - for (int i = 0; i < count; i++) + for (int i = 0; i < instances; i++) { rotations[i] = MatrixMultiply(rotations[i], rotationsInc[i]); transforms[i] = MatrixMultiply(rotations[i], translations[i]); @@ -164,6 +163,8 @@ int main(void) transforms[i] = MatrixMultiply(transforms[i], MatrixTranslate(0.0f, y, 0.0f)); } + + UpdateCamera(&camera); //---------------------------------------------------------------------------------- // Draw @@ -173,7 +174,7 @@ int main(void) ClearBackground(RAYWHITE); BeginMode3D(camera); - DrawMeshInstanced(cube, material, transforms, count); + DrawMeshInstanced(cube, material, transforms, instances); EndMode3D(); DrawText("A CUBE OF DANCING CUBES!", 490, 10, 20, MAROON); diff --git a/src/models.c b/src/models.c index 4c5a0833..09a2b2f0 100644 --- a/src/models.c +++ b/src/models.c @@ -924,6 +924,12 @@ void UploadMesh(Mesh *mesh, bool dynamic) // Draw a 3d mesh with material and transform void DrawMesh(Mesh mesh, Material material, Matrix transform) +{ + DrawMeshInstanced(mesh, material, &transform, 1); +} + +// Draw multiple mesh instances with material and different transforms +void DrawMeshInstanced(Mesh mesh, Material material, Matrix *transforms, int instances) { #if defined(GRAPHICS_API_OPENGL_11) #define GL_VERTEX_ARRAY 0x8074 @@ -939,7 +945,7 @@ void DrawMesh(Mesh mesh, Material material, Matrix transform) rlEnableStatePointer(GL_COLOR_ARRAY, mesh.colors); rlPushMatrix(); - rlMultMatrixf(MatrixToFloat(transform)); + rlMultMatrixf(MatrixToFloat(transforms[0])); rlColor4ub(material.maps[MATERIAL_MAP_DIFFUSE].color.r, material.maps[MATERIAL_MAP_DIFFUSE].color.g, material.maps[MATERIAL_MAP_DIFFUSE].color.b, @@ -961,14 +967,8 @@ void DrawMesh(Mesh mesh, Material material, Matrix transform) // Bind shader program rlEnableShader(material.shader.id); - // Matrices and other values required by shader + // Send required data to shader (matrices, values) //----------------------------------------------------- - // Calculate and send to shader model matrix - if (material.shader.locs[SHADER_LOC_MATRIX_MODEL] != -1) - { - rlSetUniformMatrix(material.shader.locs[SHADER_LOC_MATRIX_MODEL], transform); - } - // Upload to shader material.colDiffuse if (material.shader.locs[SHADER_LOC_COLOR_DIFFUSE] != -1) { @@ -998,16 +998,63 @@ void DrawMesh(Mesh mesh, Material material, Matrix transform) if (material.shader.locs[SHADER_LOC_MATRIX_VIEW] != -1) rlSetUniformMatrix(material.shader.locs[SHADER_LOC_MATRIX_VIEW], rlGetMatrixModelview()); if (material.shader.locs[SHADER_LOC_MATRIX_PROJECTION] != -1) rlSetUniformMatrix(material.shader.locs[SHADER_LOC_MATRIX_PROJECTION], rlGetMatrixProjection()); - // At this point the modelview matrix just contains the view matrix (camera) - // That's because BeginMode3D() sets it an no model-drawing function modifies it, all use rlPushMatrix() and rlPopMatrix() - Matrix matView = rlGetMatrixModelview(); // View matrix (camera) - Matrix matProjection = rlGetMatrixProjection(); // Projection matrix (perspective) + bool instancing = false; + if (instances < 1) return; + else if (instances > 1) instancing = true; + + float16 *instanceTransforms = NULL; + unsigned int instancesVboId = 0; + + Matrix matView = MatrixIdentity(); + Matrix matModelView = MatrixIdentity(); + Matrix matProjection = MatrixIdentity(); + + if (instancing) + { + // Create instances buffer + instanceTransforms = RL_MALLOC(instances*sizeof(float16)); - // Accumulate several transformations: - // matView: rlgl internal modelview matrix (actually, just view matrix) - // rlGetMatrixTransform(): rlgl internal transform matrix due to push/pop matrix stack - // transform: function parameter transformation - Matrix matModelView = MatrixMultiply(transform, MatrixMultiply(rlGetMatrixTransform(), matView)); + for (int i = 0; i < instances; i++) instanceTransforms[i] = MatrixToFloatV(transforms[i]); + + // Enable mesh VAO to attach new buffer + rlEnableVertexArray(mesh.vaoId); + + // This could alternatively use a static VBO and either glMapBuffer() or glBufferSubData(). + // It isn't clear which would be reliably faster in all cases and on all platforms, + // anecdotally glMapBuffer() seems very slow (syncs) while glBufferSubData() seems + // no faster, since we're transferring all the transform matrices anyway + instancesVboId = rlLoadVertexBuffer(instanceTransforms, instances*sizeof(float16), false); + + // Instances are send to attribute location: SHADER_LOC_MATRIX_MODEL + unsigned int instanceLoc = material.shader.locs[SHADER_LOC_MATRIX_MODEL]; + + for (unsigned int i = 0; i < 4; i++) + { + rlEnableVertexAttribute(instanceLoc + i); + rlSetVertexAttribute(instanceLoc + i, 4, RL_FLOAT, 0, sizeof(Matrix), (void *)(i*sizeof(Vector4))); + + rlSetVertexAttributeDivisor(instanceLoc + i, 1); + } + + rlDisableVertexBuffer(); + rlDisableVertexArray(); + } + else + { + // Calculate and send to shader model matrix + if (material.shader.locs[SHADER_LOC_MATRIX_MODEL] != -1) rlSetUniformMatrix(material.shader.locs[SHADER_LOC_MATRIX_MODEL], transforms[0]); + + // At this point the modelview matrix just contains the view matrix (camera) + // That's because BeginMode3D() sets it an no model-drawing function modifies it, all use rlPushMatrix() and rlPopMatrix() + matView = rlGetMatrixModelview(); // View matrix (camera) + matProjection = rlGetMatrixProjection(); // Projection matrix (perspective) + + // Accumulate several transformations: + // matView: rlgl internal modelview matrix (actually, just view matrix) + // rlGetMatrixTransform(): rlgl internal transform matrix due to push/pop matrix stack + // transform: function parameter transformation + matModelView = MatrixMultiply(transforms[0], MatrixMultiply(rlGetMatrixTransform(), matView)); + } //----------------------------------------------------- // Bind active texture maps (if available) @@ -1092,16 +1139,15 @@ void DrawMesh(Mesh mesh, Material material, Matrix transform) if (mesh.indices != NULL) rlEnableVertexBufferElement(mesh.vboId[6]); } - //rlDrawVertexData(int vertexCount, Matrix matModelView, bool stereo) - int eyesCount = 1; - //if (RLGL.State.stereoRender) eyesCount = 2; + // TODO: if (RLGL.State.stereoRender) eyesCount = 2; for (int eye = 0; eye < eyesCount; eye++) { if (eyesCount == 1) rlSetMatrixModelview(matModelView); else { + // TODO. // Setup current eye viewport (half screen width) //rlViewport(eye*rlGetFramebufferWidth()/2, 0, rlGetFramebufferWidth()/2, rlGetFramebufferHeight()); @@ -1111,16 +1157,23 @@ void DrawMesh(Mesh mesh, Material material, Matrix transform) // Set current eye projection matrix //rlSetMatrixProjection(RLGL.State.projectionStereo[eye]); } - + // Calculate model-view-projection matrix (MVP) - Matrix matMVP = MatrixMultiply(rlGetMatrixModelview(), rlGetMatrixProjection()); // Transform to screen-space coordinates - + Matrix matMVP = MatrixMultiply(rlGetMatrixModelview(), rlGetMatrixProjection()); + // Send combined model-view-projection matrix to shader rlSetUniformMatrix(material.shader.locs[SHADER_LOC_MATRIX_MVP], matMVP); - // Draw calls - if (mesh.indices != NULL) rlDrawVertexArrayElements(0, mesh.triangleCount*3, 0); - else rlDrawVertexArray(0, mesh.vertexCount); + if (instancing) // Draw mesh instanced + { + if (mesh.indices != NULL) rlDrawVertexArrayElementsInstanced(0, mesh.triangleCount*3, 0, instances); + else rlDrawVertexArrayInstanced(0, mesh.vertexCount, instances); + } + else // Draw mesh + { + if (mesh.indices != NULL) rlDrawVertexArrayElements(0, mesh.triangleCount*3, 0); + else rlDrawVertexArray(0, mesh.vertexCount); + } } // Unbind all binded texture maps @@ -1144,9 +1197,18 @@ void DrawMesh(Mesh mesh, Material material, Matrix transform) // Disable shader program rlDisableShader(); - // Restore rlgl internal modelview and projection matrices - rlSetMatrixModelview(matView); - rlSetMatrixProjection(matProjection); + if (instancing) + { + // Remove instance transforms buffer + rlUnloadVertexBuffer(instancesVboId); + RL_FREE(instanceTransforms); + } + else + { + // Restore rlgl internal modelview and projection matrices + rlSetMatrixModelview(matView); + rlSetMatrixProjection(matProjection); + } #endif } diff --git a/src/raylib.h b/src/raylib.h index 3ca9819a..472e1751 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1377,8 +1377,8 @@ RLAPI void UnloadModelKeepMeshes(Model model); // Mesh loading/unloading functions RLAPI void UploadMesh(Mesh *mesh, bool dynamic); // Upload vertex data into GPU and provided VAO/VBO ids -RLAPI void DrawMesh(Mesh mesh, Material material, Matrix transform); // Draw a 3d mesh with material and transform -RLAPI void DrawMeshInstanced(Mesh mesh, Material material, Matrix *transforms, int count); // Draw a 3d mesh with material and transform +RLAPI void DrawMesh(Mesh mesh, Material material, Matrix transform); // Draw a 3d mesh with material and transform +RLAPI void DrawMeshInstanced(Mesh mesh, Material material, Matrix *transforms, int instances); // Draw multiple mesh instances with material and different transforms RLAPI void UnloadMesh(Mesh mesh); // Unload mesh data from CPU and GPU RLAPI bool ExportMesh(Mesh mesh, const char *fileName); // Export mesh data to file, returns true on success diff --git a/src/rlgl.h b/src/rlgl.h index 206b8194..f2283743 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -600,9 +600,12 @@ RLAPI void rlUpdateVertexBuffer(int bufferId, void *data, int dataSize, int offs RLAPI void rlUnloadVertexArray(unsigned int vaoId); RLAPI void rlUnloadVertexBuffer(unsigned int vboId); RLAPI void rlSetVertexAttribute(unsigned int index, int compSize, int type, bool normalized, int stride, void *pointer); +RLAPI void rlSetVertexAttributeDivisor(unsigned int index, int divisor); RLAPI void rlSetVertexAttributeDefault(int locIndex, const void *value, int attribType, int count); // Set vertex attribute default value RLAPI void rlDrawVertexArray(int offset, int count); RLAPI void rlDrawVertexArrayElements(int offset, int count, void *buffer); +RLAPI void rlDrawVertexArrayInstanced(int offset, int count, int instances); +RLAPI void rlDrawVertexArrayElementsInstanced(int offset, int count, void *buffer, int instances); // Textures management RLAPI unsigned int rlLoadTexture(void *data, int width, int height, int format, int mipmapCount); // Load texture in GPU @@ -3118,6 +3121,20 @@ void rlDrawVertexArrayElements(int offset, int count, void *buffer) glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_SHORT, buffer + offset); } +void rlDrawVertexArrayInstanced(int offset, int count, int instances) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + glDrawArraysInstanced(GL_TRIANGLES, 0, count, instances); +#endif +} + +void rlDrawVertexArrayElementsInstanced(int offset, int count, void *buffer, int instances) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + glDrawElementsInstanced(GL_TRIANGLES, count, GL_UNSIGNED_SHORT, buffer + offset, instances); +#endif +} + #if defined(GRAPHICS_API_OPENGL_11) void rlEnableStatePointer(int vertexAttribType, void *buffer) { @@ -3155,6 +3172,13 @@ void rlSetVertexAttribute(unsigned int index, int compSize, int type, bool norma #endif } +void rlSetVertexAttributeDivisor(unsigned int index, int divisor) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + glVertexAttribDivisor(index, divisor); +#endif +} + void rlUnloadVertexArray(unsigned int vaoId) { #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) @@ -3171,7 +3195,7 @@ void rlUnloadVertexBuffer(unsigned int vboId) { #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) glDeleteBuffers(1, &vboId); - TRACELOG(LOG_INFO, "VBO: Unloaded vertex data from VRAM (GPU)"); + //TRACELOG(LOG_INFO, "VBO: Unloaded vertex data from VRAM (GPU)"); #endif }