From 97e40ced57489f4d66d4b9bc9fe213c388e1a827 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 11 Dec 2018 18:54:48 +0100 Subject: [PATCH] WARNING: BIG rewrite of rlgl module This commit implements a big update of rlgl module, intended to optimize some parts. This change could break some code bases... hopefully not, but it could. The BIG changes to the module are: - Replaced LINES-TRIANGLES-QUADS buffers by a single one, now all vertex data is accumulated on a single buffer and managed with registered draw calls. LINES-TRIANGLES-QUADS could be used the same way as before, rlgl will manage them carefully. That's a big improvement of the system. - Support multi-buffering if required. Just define MAX_BATCH_BUFFERING desired size (currently set to 1 batch). Should be enough for most of the situations. - Removed temporal accumulative buffers for matrix transformations, now transformations are directly applied to vertex when on rlVertex3f() - Reviewed rlPushMatrix()/rlPopMatrix() to be consistent with OpenGL 1.1, probably I should remove that ancient behaviour but... well, it was not consistent and now it is. - Minor tweaks: LoadText(), I broke it in last update... also multiple comments reviewed. - TODO: MAX_BATCH_ELEMENTS checking should probably be reviewed... done some tests and it works but... --- examples/models/models_billboard.c | 7 +- examples/models/models_rlgl_solar_system.c | 156 ++++ examples/models/models_rlgl_solar_system.png | Bin 0 -> 30443 bytes examples/shapes/shapes_basic_shapes.c | 17 +- src/config.h | 2 +- src/rlgl.h | 935 ++++++------------- 6 files changed, 463 insertions(+), 654 deletions(-) create mode 100644 examples/models/models_rlgl_solar_system.c create mode 100644 examples/models/models_rlgl_solar_system.png diff --git a/examples/models/models_billboard.c b/examples/models/models_billboard.c index 8ce6a44fd..596557146 100644 --- a/examples/models/models_billboard.c +++ b/examples/models/models_billboard.c @@ -28,7 +28,6 @@ int main() camera.fovy = 45.0f; camera.type = CAMERA_PERSPECTIVE; - Texture2D bill = LoadTexture("resources/billboard.png"); // Our texture billboard Vector3 billPosition = { 0.0f, 2.0f, 0.0f }; // Position where draw billboard @@ -52,11 +51,11 @@ int main() ClearBackground(RAYWHITE); BeginMode3D(camera); - - DrawBillboard(camera, bill, billPosition, 2.0f, WHITE); DrawGrid(10, 1.0f); // Draw a grid - + + DrawBillboard(camera, bill, billPosition, 2.0f, WHITE); + EndMode3D(); DrawFPS(10, 10); diff --git a/examples/models/models_rlgl_solar_system.c b/examples/models/models_rlgl_solar_system.c new file mode 100644 index 000000000..21fd30d54 --- /dev/null +++ b/examples/models/models_rlgl_solar_system.c @@ -0,0 +1,156 @@ +/******************************************************************************************* +* +* raylib [models] example - rlgl module usage with push/pop matrix transformations +* +* This example has been created using raylib 2.2 (www.raylib.com) +* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* +* Copyright (c) 2018 Ramon Santamaria (@raysan5) +* +********************************************************************************************/ + +#include "raylib.h" +#include "rlgl.h" + + +void DrawSphereBasic(Color color); // Draw sphere without any matrix transformation + +int main() +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + const float sunRadius = 4.0f; + const float earthRadius = 0.6f; + const float earthOrbitRadius = 8.0f; + const float moonRadius = 0.16f; + const float moonOrbitRadius = 1.5f; + + InitWindow(screenWidth, screenHeight, "raylib [models] example - rlgl module usage with push/pop matrix transformations"); + + // Define the camera to look into our 3d world + Camera camera = { 0 }; + camera.position = (Vector3){ 16.0f, 16.0f, 16.0f }; + camera.target = (Vector3){ 0.0f, 0.0f, 0.0f }; + camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; + camera.fovy = 45.0f; + camera.type = CAMERA_PERSPECTIVE; + + SetCameraMode(camera, CAMERA_FREE); + + float rotationSpeed = 0.2f; // General system rotation speed + + float earthRotation = 0.0f; // Rotation of earth around itself (days) in degrees + float earthOrbitRotation = 0.0f; // Rotation of earth around the Sun (years) in degrees + float moonRotation = 0.0f; // Rotation of moon around itself + float moonOrbitRotation = 0.0f; // Rotation of moon around earth in degrees + + 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); + + earthRotation += (5.0f*rotationSpeed); + earthOrbitRotation += (365/360.0f*(5.0f*rotationSpeed)*rotationSpeed); + moonRotation += (2.0f*rotationSpeed); + moonOrbitRotation += (8.0f*rotationSpeed); + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + BeginMode3D(camera); + + rlPushMatrix(); + rlScalef(sunRadius, sunRadius, sunRadius); // Scale Sun + DrawSphereBasic(GOLD); // Draw the Sun + rlPopMatrix(); + + rlPushMatrix(); + rlRotatef(earthOrbitRotation, 0.0f, 1.0f, 0.0f); // Rotation for Earth orbit around Sun + rlTranslatef(earthOrbitRadius, 0.0f, 0.0f); // Translation for Earth orbit + rlRotatef(-earthOrbitRotation, 0.0f, 1.0f, 0.0f); // Rotation for Earth orbit around Sun inverted + + rlPushMatrix(); + rlRotatef(earthRotation, 0.25, 1.0, 0.0); // Rotation for Earth itself + rlScalef(earthRadius, earthRadius, earthRadius);// Scale Earth + + DrawSphereBasic(BLUE); // Draw the Earth + rlPopMatrix(); + + rlRotatef(moonOrbitRotation, 0.0f, 1.0f, 0.0f); // Rotation for Moon orbit around Earth + rlTranslatef(moonOrbitRadius, 0.0f, 0.0f); // Translation for Moon orbit + rlRotatef(-moonOrbitRotation, 0.0f, 1.0f, 0.0f); // Rotation for Moon orbit around Earth inverted + rlRotatef(moonRotation, 0.0f, 1.0f, 0.0f); // Rotation for Moon itself + rlScalef(moonRadius, moonRadius, moonRadius); // Scale Moon + + DrawSphereBasic(LIGHTGRAY); // Draw the Moon + rlPopMatrix(); + + // Some reference elements (not affected by previous matrix transformations) + DrawCircle3D((Vector3){ 0.0f, 0.0f, 0.0f }, earthOrbitRadius, (Vector3){ 1, 0, 0 }, 90.0f, LIME); + DrawGrid(20, 1.0f); + + EndMode3D(); + + DrawFPS(10, 10); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} + +// Draw sphere without any matrix transformation +// NOTE: Sphere is drawn in world position ( 0, 0, 0 ) with radius 1.0f +void DrawSphereBasic(Color color) +{ + int rings = 16; + int slices = 16; + + rlBegin(RL_TRIANGLES); + rlColor4ub(color.r, color.g, color.b, color.a); + + for (int i = 0; i < (rings + 2); i++) + { + for (int j = 0; j < slices; j++) + { + rlVertex3f(cosf(DEG2RAD*(270+(180/(rings + 1))*i))*sinf(DEG2RAD*(j*360/slices)), + sinf(DEG2RAD*(270+(180/(rings + 1))*i)), + cosf(DEG2RAD*(270+(180/(rings + 1))*i))*cosf(DEG2RAD*(j*360/slices))); + rlVertex3f(cosf(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*sinf(DEG2RAD*((j+1)*360/slices)), + sinf(DEG2RAD*(270+(180/(rings + 1))*(i+1))), + cosf(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*cosf(DEG2RAD*((j+1)*360/slices))); + rlVertex3f(cosf(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*sinf(DEG2RAD*(j*360/slices)), + sinf(DEG2RAD*(270+(180/(rings + 1))*(i+1))), + cosf(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*cosf(DEG2RAD*(j*360/slices))); + + rlVertex3f(cosf(DEG2RAD*(270+(180/(rings + 1))*i))*sinf(DEG2RAD*(j*360/slices)), + sinf(DEG2RAD*(270+(180/(rings + 1))*i)), + cosf(DEG2RAD*(270+(180/(rings + 1))*i))*cosf(DEG2RAD*(j*360/slices))); + rlVertex3f(cosf(DEG2RAD*(270+(180/(rings + 1))*(i)))*sinf(DEG2RAD*((j+1)*360/slices)), + sinf(DEG2RAD*(270+(180/(rings + 1))*(i))), + cosf(DEG2RAD*(270+(180/(rings + 1))*(i)))*cosf(DEG2RAD*((j+1)*360/slices))); + rlVertex3f(cosf(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*sinf(DEG2RAD*((j+1)*360/slices)), + sinf(DEG2RAD*(270+(180/(rings + 1))*(i+1))), + cosf(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*cosf(DEG2RAD*((j+1)*360/slices))); + } + } + rlEnd(); +} \ No newline at end of file diff --git a/examples/models/models_rlgl_solar_system.png b/examples/models/models_rlgl_solar_system.png new file mode 100644 index 0000000000000000000000000000000000000000..576510c45927c8953e87e3df2451216e9b05a0b3 GIT binary patch literal 30443 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
`cm*xsm<)3#{-$+tBJCVlqke-0|*UzJ(3GGs_z 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 MAX_MATRIX_STACK_SIZE) TraceLog(LOG_ERROR, "Matrix stack overflow"); + + if (currentMatrixMode == RL_MODELVIEW) { - TraceLog(LOG_ERROR, "Stack Buffer Overflow (MAX %i Matrix)", MATRIX_STACK_SIZE); + useTransformMatrix = true; + currentMatrix = &transformMatrix; } stack[stackCounter] = *currentMatrix; - rlLoadIdentity(); // TODO: Review matrix stack logic! stackCounter++; - - if (currentMatrixMode == RL_MODELVIEW) useTempBuffer = true; } // Pop lattest inserted matrix from stack void rlPopMatrix(void) -{ +{ if (stackCounter > 0) { Matrix mat = stack[stackCounter - 1]; *currentMatrix = mat; stackCounter--; } + + if ((stackCounter == 0) && (currentMatrixMode == RL_MODELVIEW)) + { + currentMatrix = &modelview; + useTransformMatrix = false; + } } // Reset current matrix to identity matrix @@ -1095,107 +1089,62 @@ void rlColor4f(float x, float y, float z, float w) { glColor4f(x, y, z, w); } // Initialize drawing mode (how to organize vertex) void rlBegin(int mode) { - // Draw mode can only be RL_LINES, RL_TRIANGLES and RL_QUADS - currentDrawMode = mode; + // Draw mode can be RL_LINES, RL_TRIANGLES and RL_QUADS + // NOTE: In all three cases, vertex are accumulated over default internal vertex buffer + if (draws[drawsCounter - 1].mode != mode) + { + if (draws[drawsCounter - 1].vertexCount > 0) drawsCounter++; + if (drawsCounter >= MAX_DRAWCALL_REGISTERED) rlglDraw(); + + draws[drawsCounter - 1].mode = mode; + draws[drawsCounter - 1].vertexCount = 0; + draws[drawsCounter - 1].textureId = whiteTexture; + } } // Finish vertex providing void rlEnd(void) { - if (useTempBuffer) + // Make sure current draws[i].vertexCount is multiple of 4, to align with index processing + // NOTE: It implies adding some extra vertex at the end of the draw, those vertex will be + // processed but are placed in a single point to not result in a fragment output... + // TODO: System could be improved (a bit) just storing every draw alignment value + // and adding it to vertexOffset on drawing... maybe in a future... + int vertexToAlign = draws[drawsCounter - 1].vertexCount%4; + for (int i = 0; i < vertexToAlign; i++) rlVertex3f(-1, -1, -1); + + // Make sure vertexCount is the same for vertices, texcoords, colors and normals + // NOTE: In OpenGL 1.1, one glColor call can be made for all the subsequent glVertex calls + + // Make sure colors count match vertex count + if (vertexData[currentBuffer].vCounter != vertexData[currentBuffer].cCounter) { - // NOTE: In this case, *currentMatrix is already transposed because transposing has been applied - // independently to translation-scale-rotation matrices -> t(M1 x M2) = t(M2) x t(M1) - // This way, rlTranslatef(), rlRotatef()... behaviour is the same than OpenGL 1.1 + int addColors = vertexData[currentBuffer].vCounter - vertexData[currentBuffer].cCounter; - // Apply transformation matrix to all temp vertices - for (int i = 0; i < tempBufferCount; i++) tempBuffer[i] = Vector3Transform(tempBuffer[i], *currentMatrix); + for (int i = 0; i < addColors; i++) + { + vertexData[currentBuffer].colors[4*vertexData[currentBuffer].cCounter] = vertexData[currentBuffer].colors[4*vertexData[currentBuffer].cCounter - 4]; + vertexData[currentBuffer].colors[4*vertexData[currentBuffer].cCounter + 1] = vertexData[currentBuffer].colors[4*vertexData[currentBuffer].cCounter - 3]; + vertexData[currentBuffer].colors[4*vertexData[currentBuffer].cCounter + 2] = vertexData[currentBuffer].colors[4*vertexData[currentBuffer].cCounter - 2]; + vertexData[currentBuffer].colors[4*vertexData[currentBuffer].cCounter + 3] = vertexData[currentBuffer].colors[4*vertexData[currentBuffer].cCounter - 1]; + vertexData[currentBuffer].cCounter++; + } + } + + // Make sure texcoords count match vertex count + if (vertexData[currentBuffer].vCounter != vertexData[currentBuffer].tcCounter) + { + int addTexCoords = vertexData[currentBuffer].vCounter - vertexData[currentBuffer].tcCounter; - // Deactivate tempBuffer usage to allow rlVertex3f do its job - useTempBuffer = false; - - // Copy all transformed vertices to right VAO - for (int i = 0; i < tempBufferCount; i++) rlVertex3f(tempBuffer[i].x, tempBuffer[i].y, tempBuffer[i].z); - - // Reset temp buffer - tempBufferCount = 0; + for (int i = 0; i < addTexCoords; i++) + { + vertexData[currentBuffer].texcoords[2*vertexData[currentBuffer].tcCounter] = 0.0f; + vertexData[currentBuffer].texcoords[2*vertexData[currentBuffer].tcCounter + 1] = 0.0f; + vertexData[currentBuffer].tcCounter++; + } } - // Make sure vertexCount is the same for vertices-texcoords-normals-colors - // NOTE: In OpenGL 1.1, one glColor call can be made for all the subsequent glVertex calls. - switch (currentDrawMode) - { - case RL_LINES: - { - if (lines.vCounter != lines.cCounter) - { - int addColors = lines.vCounter - lines.cCounter; - - for (int i = 0; i < addColors; i++) - { - lines.colors[4*lines.cCounter] = lines.colors[4*lines.cCounter - 4]; - lines.colors[4*lines.cCounter + 1] = lines.colors[4*lines.cCounter - 3]; - lines.colors[4*lines.cCounter + 2] = lines.colors[4*lines.cCounter - 2]; - lines.colors[4*lines.cCounter + 3] = lines.colors[4*lines.cCounter - 1]; - - lines.cCounter++; - } - } - } break; - case RL_TRIANGLES: - { - if (triangles.vCounter != triangles.cCounter) - { - int addColors = triangles.vCounter - triangles.cCounter; - - for (int i = 0; i < addColors; i++) - { - triangles.colors[4*triangles.cCounter] = triangles.colors[4*triangles.cCounter - 4]; - triangles.colors[4*triangles.cCounter + 1] = triangles.colors[4*triangles.cCounter - 3]; - triangles.colors[4*triangles.cCounter + 2] = triangles.colors[4*triangles.cCounter - 2]; - triangles.colors[4*triangles.cCounter + 3] = triangles.colors[4*triangles.cCounter - 1]; - - triangles.cCounter++; - } - } - } break; - case RL_QUADS: - { - // Make sure colors count match vertex count - if (quads.vCounter != quads.cCounter) - { - int addColors = quads.vCounter - quads.cCounter; - - for (int i = 0; i < addColors; i++) - { - quads.colors[4*quads.cCounter] = quads.colors[4*quads.cCounter - 4]; - quads.colors[4*quads.cCounter + 1] = quads.colors[4*quads.cCounter - 3]; - quads.colors[4*quads.cCounter + 2] = quads.colors[4*quads.cCounter - 2]; - quads.colors[4*quads.cCounter + 3] = quads.colors[4*quads.cCounter - 1]; - - quads.cCounter++; - } - } - - // Make sure texcoords count match vertex count - if (quads.vCounter != quads.tcCounter) - { - int addTexCoords = quads.vCounter - quads.tcCounter; - - for (int i = 0; i < addTexCoords; i++) - { - quads.texcoords[2*quads.tcCounter] = 0.0f; - quads.texcoords[2*quads.tcCounter + 1] = 0.0f; - - quads.tcCounter++; - } - } - - // TODO: Make sure normals count match vertex count... if normals support is added in a future... :P - - } break; - default: break; - } + // TODO: Make sure normals count match vertex count... if normals support is added in a future... :P // NOTE: Depth increment is dependant on rlOrtho(): z-near and z-far values, // as well as depth buffer bit-depth (16bit or 24bit or 32bit) @@ -1204,84 +1153,36 @@ void rlEnd(void) // Verify internal buffers limits // NOTE: This check is combined with usage of rlCheckBufferLimit() - if ((lines.vCounter/2 >= (MAX_LINES_BATCH - 2)) || - (triangles.vCounter/3 >= (MAX_TRIANGLES_BATCH - 3)) || - (quads.vCounter/4 >= (MAX_QUADS_BATCH - 4))) + if ((vertexData[currentBuffer].vCounter/4) >= (MAX_BATCH_ELEMENTS - 4)) { // WARNING: If we are between rlPushMatrix() and rlPopMatrix() and we need to force a rlglDraw(), // we need to call rlPopMatrix() before to recover *currentMatrix (modelview) for the next forced draw call! // Also noted that if we had multiple matrix pushed, it will require "stackCounter" pops before launching the draw - - // TODO: Undoubtely, current rlPushMatrix/rlPopMatrix should be redesigned... or removed... it's not working properly - rlPopMatrix(); rlglDraw(); } } // Define one vertex (position) +// NOTE: Vertex position data is the basic information required for drawing void rlVertex3f(float x, float y, float z) { - // NOTE: Temp buffer is processed and resetted at rlEnd() - // Between rlBegin() and rlEnd() can not be more than TEMP_VERTEX_BUFFER_SIZE rlVertex3f() calls - if (useTempBuffer && (tempBufferCount < TEMP_VERTEX_BUFFER_SIZE)) + Vector3 vec = { x, y, z }; + + // Transform provided vector if required + if (useTransformMatrix) vec = Vector3Transform(vec, transformMatrix); + + // Verify that MAX_BATCH_ELEMENTS limit not reached + if (vertexData[currentBuffer].vCounter/4 < MAX_BATCH_ELEMENTS) { - tempBuffer[tempBufferCount].x = x; - tempBuffer[tempBufferCount].y = y; - tempBuffer[tempBufferCount].z = z; - tempBufferCount++; - } - else - { - switch (currentDrawMode) - { - case RL_LINES: - { - // Verify that MAX_LINES_BATCH limit not reached - if (lines.vCounter/2 < MAX_LINES_BATCH) - { - lines.vertices[3*lines.vCounter] = x; - lines.vertices[3*lines.vCounter + 1] = y; - lines.vertices[3*lines.vCounter + 2] = z; - - lines.vCounter++; - } - else TraceLog(LOG_ERROR, "MAX_LINES_BATCH overflow"); - - } break; - case RL_TRIANGLES: - { - // Verify that MAX_TRIANGLES_BATCH limit not reached - if (triangles.vCounter/3 < MAX_TRIANGLES_BATCH) - { - triangles.vertices[3*triangles.vCounter] = x; - triangles.vertices[3*triangles.vCounter + 1] = y; - triangles.vertices[3*triangles.vCounter + 2] = z; - - triangles.vCounter++; - } - else TraceLog(LOG_ERROR, "MAX_TRIANGLES_BATCH overflow"); - - } break; - case RL_QUADS: - { - // Verify that MAX_QUADS_BATCH limit not reached - if (quads.vCounter/4 < MAX_QUADS_BATCH) - { - quads.vertices[3*quads.vCounter] = x; - quads.vertices[3*quads.vCounter + 1] = y; - quads.vertices[3*quads.vCounter + 2] = z; - - quads.vCounter++; - - draws[drawsCounter - 1].vertexCount++; - } - else TraceLog(LOG_ERROR, "MAX_QUADS_BATCH overflow"); - - } break; - default: break; - } + vertexData[currentBuffer].vertices[3*vertexData[currentBuffer].vCounter] = vec.x; + vertexData[currentBuffer].vertices[3*vertexData[currentBuffer].vCounter + 1] = vec.y; + vertexData[currentBuffer].vertices[3*vertexData[currentBuffer].vCounter + 2] = vec.z; + vertexData[currentBuffer].vCounter++; + + draws[drawsCounter - 1].vertexCount++; } + else TraceLog(LOG_ERROR, "MAX_BATCH_ELEMENTS overflow"); } // Define one vertex (position) @@ -1300,13 +1201,9 @@ void rlVertex2i(int x, int y) // NOTE: Texture coordinates are limited to QUADS only void rlTexCoord2f(float x, float y) { - if (currentDrawMode == RL_QUADS) - { - quads.texcoords[2*quads.tcCounter] = x; - quads.texcoords[2*quads.tcCounter + 1] = y; - - quads.tcCounter++; - } + vertexData[currentBuffer].texcoords[2*vertexData[currentBuffer].tcCounter] = x; + vertexData[currentBuffer].texcoords[2*vertexData[currentBuffer].tcCounter + 1] = y; + vertexData[currentBuffer].tcCounter++; } // Define one vertex (normal) @@ -1319,40 +1216,11 @@ void rlNormal3f(float x, float y, float z) // Define one vertex (color) void rlColor4ub(byte x, byte y, byte z, byte w) { - switch (currentDrawMode) - { - case RL_LINES: - { - lines.colors[4*lines.cCounter] = x; - lines.colors[4*lines.cCounter + 1] = y; - lines.colors[4*lines.cCounter + 2] = z; - lines.colors[4*lines.cCounter + 3] = w; - - lines.cCounter++; - - } break; - case RL_TRIANGLES: - { - triangles.colors[4*triangles.cCounter] = x; - triangles.colors[4*triangles.cCounter + 1] = y; - triangles.colors[4*triangles.cCounter + 2] = z; - triangles.colors[4*triangles.cCounter + 3] = w; - - triangles.cCounter++; - - } break; - case RL_QUADS: - { - quads.colors[4*quads.cCounter] = x; - quads.colors[4*quads.cCounter + 1] = y; - quads.colors[4*quads.cCounter + 2] = z; - quads.colors[4*quads.cCounter + 3] = w; - - quads.cCounter++; - - } break; - default: break; - } + vertexData[currentBuffer].colors[4*vertexData[currentBuffer].cCounter] = x; + vertexData[currentBuffer].colors[4*vertexData[currentBuffer].cCounter + 1] = y; + vertexData[currentBuffer].colors[4*vertexData[currentBuffer].cCounter + 2] = z; + vertexData[currentBuffer].colors[4*vertexData[currentBuffer].cCounter + 3] = w; + vertexData[currentBuffer].cCounter++; } // Define one vertex (color) @@ -1385,8 +1253,7 @@ void rlEnableTexture(unsigned int id) if (draws[drawsCounter - 1].textureId != id) { if (draws[drawsCounter - 1].vertexCount > 0) drawsCounter++; - - if (drawsCounter >= MAX_DRAWS_BY_TEXTURE) rlglDraw(); + if (drawsCounter >= MAX_DRAWCALL_REGISTERED) rlglDraw(); draws[drawsCounter - 1].textureId = id; draws[drawsCounter - 1].vertexCount = 0; @@ -1403,7 +1270,7 @@ void rlDisableTexture(void) #else // NOTE: If quads batch limit is reached, // we force a draw call and next batch starts - if (quads.vCounter/4 >= MAX_QUADS_BATCH) rlglDraw(); + if (vertexData[currentBuffer].vCounter/4 >= MAX_BATCH_ELEMENTS) rlglDraw(); #endif } @@ -1747,7 +1614,6 @@ void rlglInit(int width, int height) // Init default white texture unsigned char pixels[4] = { 255, 255, 255, 255 }; // 1 pixel RGBA (4 bytes) - whiteTexture = rlLoadTexture(pixels, 1, 1, UNCOMPRESSED_R8G8B8A8, 1); if (whiteTexture != 0) TraceLog(LOG_INFO, "[TEX ID %i] Base white texture loaded successfully", whiteTexture); @@ -1757,34 +1623,30 @@ void rlglInit(int width, int height) defaultShader = LoadShaderDefault(); currentShader = defaultShader; - // Init default vertex arrays buffers (lines, triangles, quads) + // Init default vertex arrays buffers LoadBuffersDefault(); - - // Init temp vertex buffer, used when transformation required (translate, rotate, scale) - tempBuffer = (Vector3 *)malloc(sizeof(Vector3)*TEMP_VERTEX_BUFFER_SIZE); - - for (int i = 0; i < TEMP_VERTEX_BUFFER_SIZE; i++) tempBuffer[i] = Vector3Zero(); + + // Init transformations matrix accumulator + transformMatrix = MatrixIdentity(); // Init draw calls tracking system - draws = (DrawCall *)malloc(sizeof(DrawCall)*MAX_DRAWS_BY_TEXTURE); + draws = (DrawCall *)malloc(sizeof(DrawCall)*MAX_DRAWCALL_REGISTERED); - for (int i = 0; i < MAX_DRAWS_BY_TEXTURE; i++) + for (int i = 0; i < MAX_DRAWCALL_REGISTERED; i++) { + draws[i].mode = RL_QUADS; draws[i].vertexCount = 0; - draws[i].vaoId = 0; - draws[i].shaderId = 0; - draws[i].textureId = 0; - - draws[i].projection = MatrixIdentity(); - draws[i].modelview = MatrixIdentity(); + //draws[i].vaoId = 0; + //draws[i].shaderId = 0; + draws[i].textureId = whiteTexture; + //draws[i].projection = MatrixIdentity(); + //draws[i].modelview = MatrixIdentity(); } drawsCounter = 1; - draws[0].textureId = whiteTexture; // Set default draw texture id - currentDrawMode = RL_TRIANGLES; // Set default draw mode // Init internal matrix stack (emulating OpenGL 1.1) - for (int i = 0; i < MATRIX_STACK_SIZE; i++) stack[i] = MatrixIdentity(); + for (int i = 0; i < MAX_MATRIX_STACK_SIZE; i++) stack[i] = MatrixIdentity(); // Init internal projection and modelview matrices projection = MatrixIdentity(); @@ -1832,24 +1694,19 @@ void rlglClose(void) { #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) UnloadShaderDefault(); // Unload default shader - UnloadBuffersDefault(); // Unload default buffers (lines, triangles, quads) + UnloadBuffersDefault(); // Unload default buffers glDeleteTextures(1, &whiteTexture); // Unload default texture TraceLog(LOG_INFO, "[TEX ID %i] Unloaded texture data (base white texture) from VRAM", whiteTexture); free(draws); - free(tempBuffer); #endif } -// Drawing batches: triangles, quads, lines +// Update and draw internal buffers void rlglDraw(void) { #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - // NOTE: In a future version, models could be stored in a stack... - //for (int i = 0; i < modelsCount; i++) rlDrawMesh(models[i]->mesh, models[i]->material, models[i]->transform); - - // NOTE: Default buffers upload and draw UpdateBuffersDefault(); DrawBuffersDefault(); // NOTE: Stereo rendering is checked inside #endif @@ -1862,7 +1719,7 @@ int rlGetVersion(void) return OPENGL_11; #elif defined(GRAPHICS_API_OPENGL_21) #if defined(__APPLE__) - return OPENGL_33; // NOTE: Force OpenGL 3.3 on OSX + return OPENGL_33; // NOTE: Force OpenGL 3.3 on OSX #else return OPENGL_21; #endif @@ -1878,13 +1735,7 @@ bool rlCheckBufferLimit(int type, int vCount) { bool overflow = false; #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - switch (type) - { - case RL_LINES: overflow = ((lines.vCounter + vCount)/2 >= MAX_LINES_BATCH); break; - case RL_TRIANGLES: overflow = ((triangles.vCounter + vCount)/3 >= MAX_TRIANGLES_BATCH); break; - case RL_QUADS: overflow = ((quads.vCounter + vCount)/4 >= MAX_QUADS_BATCH); break; - default: break; - } + if ((vertexData[currentBuffer].vCounter + vCount)/4 >= MAX_BATCH_ELEMENTS) overflow = true; #endif return overflow; } @@ -2098,6 +1949,7 @@ unsigned int rlLoadTexture(void *data, int width, int height, int format, int mi } // Update already loaded texture in GPU with new data +// TODO: We don't know safely if internal texture format is the expected one... void rlUpdateTexture(unsigned int id, int width, int height, int format, const void *data) { glBindTexture(GL_TEXTURE_2D, id); @@ -2881,25 +2733,6 @@ void *rlReadTexturePixels(Texture2D texture) return pixels; } -/* -// TODO: Record draw calls to be processed in batch -// NOTE: Global state must be kept -void rlRecordDraw(void) -{ - // TODO: Before adding a new draw, check if anything changed from last stored draw -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - draws[drawsCounter].vertexCount = currentState.vertexCount; - draws[drawsCounter].vaoId = currentState.vaoId; // lines.id, trangles.id, quads.id? - draws[drawsCounter].textureId = currentState.textureId; // whiteTexture? - draws[drawsCounter].shaderId = currentState.shaderId; // defaultShader.id - draws[drawsCounter].projection = projection; - draws[drawsCounter].modelview = modelview; - - drawsCounter++; -#endif -} -*/ - //---------------------------------------------------------------------------------- // Module Functions Definition - Shaders Functions // NOTE: Those functions are exposed directly to the user in raylib.h @@ -2934,12 +2767,12 @@ Shader GetShaderDefault(void) // NOTE: text chars array should be freed manually char *LoadText(const char *fileName) { - FILE *textFile; + FILE *textFile = NULL; char *text = NULL; if (fileName != NULL) { - textFile = fopen(fileName,"r"); + textFile = fopen(fileName,"rt"); if (textFile != NULL) { @@ -2950,7 +2783,8 @@ char *LoadText(const char *fileName) if (size > 0) { text = (char *)malloc(sizeof(char)*(size + 1)); - fread(text, sizeof(char), size, textFile); + int count = fread(text, sizeof(char), size, textFile); + text[count] = '\0'; } fclose(textFile); @@ -3833,7 +3667,7 @@ static unsigned int LoadShaderProgram(unsigned int vShaderId, unsigned int fShad // Load default shader (just vertex positioning and texture coloring) -// NOTE: This shader program is used for batch buffers (lines, triangles, quads) +// NOTE: This shader program is used for internal buffers static Shader LoadShaderDefault(void) { Shader shader = { 0 }; @@ -3974,171 +3808,93 @@ static void UnloadShaderDefault(void) glDeleteProgram(defaultShader.id); } -// Load default internal buffers (lines, triangles, quads) +// Load default internal buffers static void LoadBuffersDefault(void) { - // [CPU] Allocate and initialize float array buffers to store vertex data (lines, triangles, quads) + // Initialize CPU (RAM) arrays (vertex position, texcoord, color data and indexes) //-------------------------------------------------------------------------------------------- - - // Lines - Initialize arrays (vertex position and color data) - lines.vertices = (float *)malloc(sizeof(float)*3*2*MAX_LINES_BATCH); // 3 float by vertex, 2 vertex by line - lines.colors = (unsigned char *)malloc(sizeof(unsigned char)*4*2*MAX_LINES_BATCH); // 4 float by color, 2 colors by line - lines.texcoords = NULL; - lines.indices = NULL; - - for (int i = 0; i < (3*2*MAX_LINES_BATCH); i++) lines.vertices[i] = 0.0f; - for (int i = 0; i < (4*2*MAX_LINES_BATCH); i++) lines.colors[i] = 0; - - lines.vCounter = 0; - lines.cCounter = 0; - lines.tcCounter = 0; - - // Triangles - Initialize arrays (vertex position and color data) - triangles.vertices = (float *)malloc(sizeof(float)*3*3*MAX_TRIANGLES_BATCH); // 3 float by vertex, 3 vertex by triangle - triangles.colors = (unsigned char *)malloc(sizeof(unsigned char)*4*3*MAX_TRIANGLES_BATCH); // 4 float by color, 3 colors by triangle - triangles.texcoords = NULL; - triangles.indices = NULL; - - for (int i = 0; i < (3*3*MAX_TRIANGLES_BATCH); i++) triangles.vertices[i] = 0.0f; - for (int i = 0; i < (4*3*MAX_TRIANGLES_BATCH); i++) triangles.colors[i] = 0; - - triangles.vCounter = 0; - triangles.cCounter = 0; - triangles.tcCounter = 0; - - // Quads - Initialize arrays (vertex position, texcoord, color data and indexes) - quads.vertices = (float *)malloc(sizeof(float)*3*4*MAX_QUADS_BATCH); // 3 float by vertex, 4 vertex by quad - quads.texcoords = (float *)malloc(sizeof(float)*2*4*MAX_QUADS_BATCH); // 2 float by texcoord, 4 texcoord by quad - quads.colors = (unsigned char *)malloc(sizeof(unsigned char)*4*4*MAX_QUADS_BATCH); // 4 float by color, 4 colors by quad + for (int i = 0; i < MAX_BATCH_BUFFERING; i++) + { + vertexData[i].vertices = (float *)malloc(sizeof(float)*3*4*MAX_BATCH_ELEMENTS); // 3 float by vertex, 4 vertex by quad + vertexData[i].texcoords = (float *)malloc(sizeof(float)*2*4*MAX_BATCH_ELEMENTS); // 2 float by texcoord, 4 texcoord by quad + vertexData[i].colors = (unsigned char *)malloc(sizeof(unsigned char)*4*4*MAX_BATCH_ELEMENTS); // 4 float by color, 4 colors by quad #if defined(GRAPHICS_API_OPENGL_33) - quads.indices = (unsigned int *)malloc(sizeof(unsigned int)*6*MAX_QUADS_BATCH); // 6 int by quad (indices) + vertexData[i].indices = (unsigned int *)malloc(sizeof(unsigned int)*6*MAX_BATCH_ELEMENTS); // 6 int by quad (indices) #elif defined(GRAPHICS_API_OPENGL_ES2) - quads.indices = (unsigned short *)malloc(sizeof(unsigned short)*6*MAX_QUADS_BATCH); // 6 int by quad (indices) + vertexData[i].indices = (unsigned short *)malloc(sizeof(unsigned short)*6*MAX_BATCH_ELEMENTS); // 6 int by quad (indices) #endif - for (int i = 0; i < (3*4*MAX_QUADS_BATCH); i++) quads.vertices[i] = 0.0f; - for (int i = 0; i < (2*4*MAX_QUADS_BATCH); i++) quads.texcoords[i] = 0.0f; - for (int i = 0; i < (4*4*MAX_QUADS_BATCH); i++) quads.colors[i] = 0; + for (int j = 0; j < (3*4*MAX_BATCH_ELEMENTS); j++) vertexData[i].vertices[j] = 0.0f; + for (int j = 0; j < (2*4*MAX_BATCH_ELEMENTS); j++) vertexData[i].texcoords[j] = 0.0f; + for (int j = 0; j < (4*4*MAX_BATCH_ELEMENTS); j++) vertexData[i].colors[j] = 0; - int k = 0; + int k = 0; - // Indices can be initialized right now - for (int i = 0; i < (6*MAX_QUADS_BATCH); i+=6) - { - quads.indices[i] = 4*k; - quads.indices[i+1] = 4*k+1; - quads.indices[i+2] = 4*k+2; - quads.indices[i+3] = 4*k; - quads.indices[i+4] = 4*k+2; - quads.indices[i+5] = 4*k+3; + // Indices can be initialized right now + for (int j = 0; j < (6*MAX_BATCH_ELEMENTS); j += 6) + { + vertexData[i].indices[j] = 4*k; + vertexData[i].indices[j + 1] = 4*k + 1; + vertexData[i].indices[j + 2] = 4*k + 2; + vertexData[i].indices[j + 3] = 4*k; + vertexData[i].indices[j + 4] = 4*k + 2; + vertexData[i].indices[j + 5] = 4*k + 3; - k++; + k++; + } + + vertexData[i].vCounter = 0; + vertexData[i].tcCounter = 0; + vertexData[i].cCounter = 0; } - quads.vCounter = 0; - quads.tcCounter = 0; - quads.cCounter = 0; - - TraceLog(LOG_INFO, "[CPU] Default buffers initialized successfully (lines, triangles, quads)"); + TraceLog(LOG_INFO, "Internal buffers initialized successfully (CPU)"); //-------------------------------------------------------------------------------------------- - // [GPU] Upload vertex data and initialize VAOs/VBOs (lines, triangles, quads) - // NOTE: Default buffers are linked to use currentShader (defaultShader) + // Upload to GPU (VRAM) vertex data and initialize VAOs/VBOs //-------------------------------------------------------------------------------------------- - - // Upload and link lines vertex buffers - if (vaoSupported) + for (int i = 0; i < MAX_BATCH_BUFFERING; i++) { - // Initialize Lines VAO - glGenVertexArrays(1, &lines.vaoId); - glBindVertexArray(lines.vaoId); - } + if (vaoSupported) + { + // Initialize Quads VAO + glGenVertexArrays(1, &vertexData[i].vaoId); + glBindVertexArray(vertexData[i].vaoId); + } - // Lines - Vertex buffers binding and attributes enable - // Vertex position buffer (shader-location = 0) - glGenBuffers(2, &lines.vboId[0]); - glBindBuffer(GL_ARRAY_BUFFER, lines.vboId[0]); - glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*2*MAX_LINES_BATCH, lines.vertices, GL_DYNAMIC_DRAW); - glEnableVertexAttribArray(currentShader.locs[LOC_VERTEX_POSITION]); - glVertexAttribPointer(currentShader.locs[LOC_VERTEX_POSITION], 3, GL_FLOAT, 0, 0, 0); + // Quads - Vertex buffers binding and attributes enable + // Vertex position buffer (shader-location = 0) + glGenBuffers(1, &vertexData[i].vboId[0]); + glBindBuffer(GL_ARRAY_BUFFER, vertexData[i].vboId[0]); + glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*4*MAX_BATCH_ELEMENTS, vertexData[i].vertices, GL_DYNAMIC_DRAW); + glEnableVertexAttribArray(currentShader.locs[LOC_VERTEX_POSITION]); + glVertexAttribPointer(currentShader.locs[LOC_VERTEX_POSITION], 3, GL_FLOAT, 0, 0, 0); - // Vertex color buffer (shader-location = 3) - glGenBuffers(2, &lines.vboId[1]); - glBindBuffer(GL_ARRAY_BUFFER, lines.vboId[1]); - glBufferData(GL_ARRAY_BUFFER, sizeof(unsigned char)*4*2*MAX_LINES_BATCH, lines.colors, GL_DYNAMIC_DRAW); - glEnableVertexAttribArray(currentShader.locs[LOC_VERTEX_COLOR]); - glVertexAttribPointer(currentShader.locs[LOC_VERTEX_COLOR], 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); + // Vertex texcoord buffer (shader-location = 1) + glGenBuffers(1, &vertexData[i].vboId[1]); + glBindBuffer(GL_ARRAY_BUFFER, vertexData[i].vboId[1]); + glBufferData(GL_ARRAY_BUFFER, sizeof(float)*2*4*MAX_BATCH_ELEMENTS, vertexData[i].texcoords, GL_DYNAMIC_DRAW); + glEnableVertexAttribArray(currentShader.locs[LOC_VERTEX_TEXCOORD01]); + glVertexAttribPointer(currentShader.locs[LOC_VERTEX_TEXCOORD01], 2, GL_FLOAT, 0, 0, 0); - if (vaoSupported) TraceLog(LOG_INFO, "[VAO ID %i] Default buffers VAO initialized successfully (lines)", lines.vaoId); - else TraceLog(LOG_INFO, "[VBO ID %i][VBO ID %i] Default buffers VBOs initialized successfully (lines)", lines.vboId[0], lines.vboId[1]); + // Vertex color buffer (shader-location = 3) + glGenBuffers(1, &vertexData[i].vboId[2]); + glBindBuffer(GL_ARRAY_BUFFER, vertexData[i].vboId[2]); + glBufferData(GL_ARRAY_BUFFER, sizeof(unsigned char)*4*4*MAX_BATCH_ELEMENTS, vertexData[i].colors, GL_DYNAMIC_DRAW); + glEnableVertexAttribArray(currentShader.locs[LOC_VERTEX_COLOR]); + glVertexAttribPointer(currentShader.locs[LOC_VERTEX_COLOR], 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); - // Upload and link triangles vertex buffers - if (vaoSupported) - { - // Initialize Triangles VAO - glGenVertexArrays(1, &triangles.vaoId); - glBindVertexArray(triangles.vaoId); - } - - // Triangles - Vertex buffers binding and attributes enable - // Vertex position buffer (shader-location = 0) - glGenBuffers(1, &triangles.vboId[0]); - glBindBuffer(GL_ARRAY_BUFFER, triangles.vboId[0]); - glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*3*MAX_TRIANGLES_BATCH, triangles.vertices, GL_DYNAMIC_DRAW); - glEnableVertexAttribArray(currentShader.locs[LOC_VERTEX_POSITION]); - glVertexAttribPointer(currentShader.locs[LOC_VERTEX_POSITION], 3, GL_FLOAT, 0, 0, 0); - - // Vertex color buffer (shader-location = 3) - glGenBuffers(1, &triangles.vboId[1]); - glBindBuffer(GL_ARRAY_BUFFER, triangles.vboId[1]); - glBufferData(GL_ARRAY_BUFFER, sizeof(unsigned char)*4*3*MAX_TRIANGLES_BATCH, triangles.colors, GL_DYNAMIC_DRAW); - glEnableVertexAttribArray(currentShader.locs[LOC_VERTEX_COLOR]); - glVertexAttribPointer(currentShader.locs[LOC_VERTEX_COLOR], 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); - - if (vaoSupported) TraceLog(LOG_INFO, "[VAO ID %i] Default buffers VAO initialized successfully (triangles)", triangles.vaoId); - else TraceLog(LOG_INFO, "[VBO ID %i][VBO ID %i] Default buffers VBOs initialized successfully (triangles)", triangles.vboId[0], triangles.vboId[1]); - - // Upload and link quads vertex buffers - if (vaoSupported) - { - // Initialize Quads VAO - glGenVertexArrays(1, &quads.vaoId); - glBindVertexArray(quads.vaoId); - } - - // Quads - Vertex buffers binding and attributes enable - // Vertex position buffer (shader-location = 0) - glGenBuffers(1, &quads.vboId[0]); - glBindBuffer(GL_ARRAY_BUFFER, quads.vboId[0]); - glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*4*MAX_QUADS_BATCH, quads.vertices, GL_DYNAMIC_DRAW); - glEnableVertexAttribArray(currentShader.locs[LOC_VERTEX_POSITION]); - glVertexAttribPointer(currentShader.locs[LOC_VERTEX_POSITION], 3, GL_FLOAT, 0, 0, 0); - - // Vertex texcoord buffer (shader-location = 1) - glGenBuffers(1, &quads.vboId[1]); - glBindBuffer(GL_ARRAY_BUFFER, quads.vboId[1]); - glBufferData(GL_ARRAY_BUFFER, sizeof(float)*2*4*MAX_QUADS_BATCH, quads.texcoords, GL_DYNAMIC_DRAW); - glEnableVertexAttribArray(currentShader.locs[LOC_VERTEX_TEXCOORD01]); - glVertexAttribPointer(currentShader.locs[LOC_VERTEX_TEXCOORD01], 2, GL_FLOAT, 0, 0, 0); - - // Vertex color buffer (shader-location = 3) - glGenBuffers(1, &quads.vboId[2]); - glBindBuffer(GL_ARRAY_BUFFER, quads.vboId[2]); - glBufferData(GL_ARRAY_BUFFER, sizeof(unsigned char)*4*4*MAX_QUADS_BATCH, quads.colors, GL_DYNAMIC_DRAW); - glEnableVertexAttribArray(currentShader.locs[LOC_VERTEX_COLOR]); - glVertexAttribPointer(currentShader.locs[LOC_VERTEX_COLOR], 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); - - // Fill index buffer - glGenBuffers(1, &quads.vboId[3]); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, quads.vboId[3]); + // Fill index buffer + glGenBuffers(1, &vertexData[i].vboId[3]); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vertexData[i].vboId[3]); #if defined(GRAPHICS_API_OPENGL_33) - glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(int)*6*MAX_QUADS_BATCH, quads.indices, GL_STATIC_DRAW); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(int)*6*MAX_BATCH_ELEMENTS, vertexData[i].indices, GL_STATIC_DRAW); #elif defined(GRAPHICS_API_OPENGL_ES2) - glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(short)*6*MAX_QUADS_BATCH, quads.indices, GL_STATIC_DRAW); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(short)*6*MAX_BATCH_ELEMENTS, vertexData[i].indices, GL_STATIC_DRAW); #endif - - if (vaoSupported) TraceLog(LOG_INFO, "[VAO ID %i] Default buffers VAO initialized successfully (quads)", quads.vaoId); - else TraceLog(LOG_INFO, "[VBO ID %i][VBO ID %i][VBO ID %i][VBO ID %i] Default buffers VBOs initialized successfully (quads)", quads.vboId[0], quads.vboId[1], quads.vboId[2], quads.vboId[3]); + } + + TraceLog(LOG_INFO, "Internal buffers uploaded successfully (GPU)"); // Unbind the current VAO if (vaoSupported) glBindVertexArray(0); @@ -4150,74 +3906,48 @@ static void LoadBuffersDefault(void) // TODO: If no data changed on the CPU arrays --> No need to re-update GPU arrays (change flag required) static void UpdateBuffersDefault(void) { - // Update lines vertex buffers - if (lines.vCounter > 0) + // Update vertex buffers data + if (vertexData[currentBuffer].vCounter > 0) { - // Activate Lines VAO - if (vaoSupported) glBindVertexArray(lines.vaoId); + // Activate elements VAO + if (vaoSupported) glBindVertexArray(vertexData[currentBuffer].vaoId); - // Lines - vertex positions buffer - glBindBuffer(GL_ARRAY_BUFFER, lines.vboId[0]); - //glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*2*MAX_LINES_BATCH, lines.vertices, GL_DYNAMIC_DRAW); - glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(float)*3*lines.vCounter, lines.vertices); // target - offset (in bytes) - size (in bytes) - data pointer + // Vertex positions buffer + glBindBuffer(GL_ARRAY_BUFFER, vertexData[currentBuffer].vboId[0]); + glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(float)*3*vertexData[currentBuffer].vCounter, vertexData[currentBuffer].vertices); + //glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*4*MAX_BATCH_ELEMENTS, vertexData[currentBuffer].vertices, GL_DYNAMIC_DRAW); // Update all buffer + + // Texture coordinates buffer + glBindBuffer(GL_ARRAY_BUFFER, vertexData[currentBuffer].vboId[1]); + glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(float)*2*vertexData[currentBuffer].vCounter, vertexData[currentBuffer].texcoords); + //glBufferData(GL_ARRAY_BUFFER, sizeof(float)*2*4*MAX_BATCH_ELEMENTS, vertexData[currentBuffer].texcoords, GL_DYNAMIC_DRAW); // Update all buffer - // Lines - colors buffer - glBindBuffer(GL_ARRAY_BUFFER, lines.vboId[1]); - //glBufferData(GL_ARRAY_BUFFER, sizeof(float)*4*2*MAX_LINES_BATCH, lines.colors, GL_DYNAMIC_DRAW); - glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(unsigned char)*4*lines.cCounter, lines.colors); + // Colors buffer + glBindBuffer(GL_ARRAY_BUFFER, vertexData[currentBuffer].vboId[2]); + glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(unsigned char)*4*vertexData[currentBuffer].vCounter, vertexData[currentBuffer].colors); + //glBufferData(GL_ARRAY_BUFFER, sizeof(float)*4*4*MAX_BATCH_ELEMENTS, vertexData[currentBuffer].colors, GL_DYNAMIC_DRAW); // Update all buffer + + // NOTE: glMapBuffer() causes sync issue. + // If GPU is working with this buffer, glMapBuffer() will wait(stall) until GPU to finish its job. + // To avoid waiting (idle), you can call first glBufferData() with NULL pointer before glMapBuffer(). + // If you do that, the previous data in PBO will be discarded and glMapBuffer() returns a new + // allocated pointer immediately even if GPU is still working with the previous data. + + // Another option: map the buffer object into client's memory + // Probably this code could be moved somewhere else... + // vertexData[currentBuffer].vertices = (float *)glMapBuffer(GL_ARRAY_BUFFER, GL_READ_WRITE); + // if(vertexData[currentBuffer].vertices) + // { + // Update vertex data + // } + // glUnmapBuffer(GL_ARRAY_BUFFER); + + // Unbind the current VAO + if (vaoSupported) glBindVertexArray(0); } - - // Update triangles vertex buffers - if (triangles.vCounter > 0) - { - // Activate Triangles VAO - if (vaoSupported) glBindVertexArray(triangles.vaoId); - - // Triangles - vertex positions buffer - glBindBuffer(GL_ARRAY_BUFFER, triangles.vboId[0]); - //glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*3*MAX_TRIANGLES_BATCH, triangles.vertices, GL_DYNAMIC_DRAW); - glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(float)*3*triangles.vCounter, triangles.vertices); - - // Triangles - colors buffer - glBindBuffer(GL_ARRAY_BUFFER, triangles.vboId[1]); - //glBufferData(GL_ARRAY_BUFFER, sizeof(float)*4*3*MAX_TRIANGLES_BATCH, triangles.colors, GL_DYNAMIC_DRAW); - glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(unsigned char)*4*triangles.cCounter, triangles.colors); - } - - // Update quads vertex buffers - if (quads.vCounter > 0) - { - // Activate Quads VAO - if (vaoSupported) glBindVertexArray(quads.vaoId); - - // Quads - vertex positions buffer - glBindBuffer(GL_ARRAY_BUFFER, quads.vboId[0]); - //glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*4*MAX_QUADS_BATCH, quads.vertices, GL_DYNAMIC_DRAW); - glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(float)*3*quads.vCounter, quads.vertices); - - // Quads - texture coordinates buffer - glBindBuffer(GL_ARRAY_BUFFER, quads.vboId[1]); - //glBufferData(GL_ARRAY_BUFFER, sizeof(float)*2*4*MAX_QUADS_BATCH, quads.texcoords, GL_DYNAMIC_DRAW); - glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(float)*2*quads.vCounter, quads.texcoords); - - // Quads - colors buffer - glBindBuffer(GL_ARRAY_BUFFER, quads.vboId[2]); - //glBufferData(GL_ARRAY_BUFFER, sizeof(float)*4*4*MAX_QUADS_BATCH, quads.colors, GL_DYNAMIC_DRAW); - glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(unsigned char)*4*quads.vCounter, quads.colors); - - // Another option would be using buffer mapping... - //quads.vertices = glMapBuffer(GL_ARRAY_BUFFER, GL_READ_WRITE); - // Now we can modify vertices - //glUnmapBuffer(GL_ARRAY_BUFFER); - } - //-------------------------------------------------------------- - - // Unbind the current VAO - if (vaoSupported) glBindVertexArray(0); } // Draw default internal buffers vertex data -// NOTE: We draw in this order: lines, triangles, quads static void DrawBuffersDefault(void) { Matrix matProjection = projection; @@ -4230,13 +3960,14 @@ static void DrawBuffersDefault(void) for (int eye = 0; eye < eyesCount; eye++) { - #if defined(SUPPORT_VR_SIMULATOR) +#if defined(SUPPORT_VR_SIMULATOR) if (eyesCount == 2) SetStereoView(eye, matProjection, matModelView); - #endif +#endif - // Set current shader and upload current MVP matrix - if ((lines.vCounter > 0) || (triangles.vCounter > 0) || (quads.vCounter > 0)) + // Draw quads buffers + if (vertexData[currentBuffer].vCounter > 0) { + // Set current shader and upload current MVP matrix glUseProgram(currentShader.id); // Create modelview-projection matrix @@ -4247,117 +3978,50 @@ static void DrawBuffersDefault(void) glUniform1i(currentShader.locs[LOC_MAP_DIFFUSE], 0); // NOTE: Additional map textures not considered for default buffers drawing - } + + int vertexOffset = 0; - // Draw lines buffers - if (lines.vCounter > 0) - { - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, whiteTexture); - - if (vaoSupported) - { - glBindVertexArray(lines.vaoId); - } + if (vaoSupported) glBindVertexArray(vertexData[currentBuffer].vaoId); else { // Bind vertex attrib: position (shader-location = 0) - glBindBuffer(GL_ARRAY_BUFFER, lines.vboId[0]); - glVertexAttribPointer(currentShader.locs[LOC_VERTEX_POSITION], 3, GL_FLOAT, 0, 0, 0); - glEnableVertexAttribArray(currentShader.locs[LOC_VERTEX_POSITION]); - - // Bind vertex attrib: color (shader-location = 3) - glBindBuffer(GL_ARRAY_BUFFER, lines.vboId[1]); - glVertexAttribPointer(currentShader.locs[LOC_VERTEX_COLOR], 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); - glEnableVertexAttribArray(currentShader.locs[LOC_VERTEX_COLOR]); - } - - glDrawArrays(GL_LINES, 0, lines.vCounter); - - if (!vaoSupported) glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindTexture(GL_TEXTURE_2D, 0); - } - - // Draw triangles buffers - if (triangles.vCounter > 0) - { - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, whiteTexture); - - if (vaoSupported) - { - glBindVertexArray(triangles.vaoId); - } - else - { - // Bind vertex attrib: position (shader-location = 0) - glBindBuffer(GL_ARRAY_BUFFER, triangles.vboId[0]); - glVertexAttribPointer(currentShader.locs[LOC_VERTEX_POSITION], 3, GL_FLOAT, 0, 0, 0); - glEnableVertexAttribArray(currentShader.locs[LOC_VERTEX_POSITION]); - - // Bind vertex attrib: color (shader-location = 3) - glBindBuffer(GL_ARRAY_BUFFER, triangles.vboId[1]); - glVertexAttribPointer(currentShader.locs[LOC_VERTEX_COLOR], 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); - glEnableVertexAttribArray(currentShader.locs[LOC_VERTEX_COLOR]); - } - - glDrawArrays(GL_TRIANGLES, 0, triangles.vCounter); - - if (!vaoSupported) glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindTexture(GL_TEXTURE_2D, 0); - } - - // Draw quads buffers - if (quads.vCounter > 0) - { - int quadsCount = 0; - int numIndicesToProcess = 0; - int indicesOffset = 0; - - if (vaoSupported) - { - glBindVertexArray(quads.vaoId); - } - else - { - // Bind vertex attrib: position (shader-location = 0) - glBindBuffer(GL_ARRAY_BUFFER, quads.vboId[0]); + glBindBuffer(GL_ARRAY_BUFFER, vertexData[currentBuffer].vboId[0]); glVertexAttribPointer(currentShader.locs[LOC_VERTEX_POSITION], 3, GL_FLOAT, 0, 0, 0); glEnableVertexAttribArray(currentShader.locs[LOC_VERTEX_POSITION]); // Bind vertex attrib: texcoord (shader-location = 1) - glBindBuffer(GL_ARRAY_BUFFER, quads.vboId[1]); + glBindBuffer(GL_ARRAY_BUFFER, vertexData[currentBuffer].vboId[1]); glVertexAttribPointer(currentShader.locs[LOC_VERTEX_TEXCOORD01], 2, GL_FLOAT, 0, 0, 0); glEnableVertexAttribArray(currentShader.locs[LOC_VERTEX_TEXCOORD01]); // Bind vertex attrib: color (shader-location = 3) - glBindBuffer(GL_ARRAY_BUFFER, quads.vboId[2]); + glBindBuffer(GL_ARRAY_BUFFER, vertexData[currentBuffer].vboId[2]); glVertexAttribPointer(currentShader.locs[LOC_VERTEX_COLOR], 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); glEnableVertexAttribArray(currentShader.locs[LOC_VERTEX_COLOR]); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, quads.vboId[3]); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vertexData[currentBuffer].vboId[3]); } + glActiveTexture(GL_TEXTURE0); + for (int i = 0; i < drawsCounter; i++) { - quadsCount = draws[i].vertexCount/4; - numIndicesToProcess = quadsCount*6; // Get number of Quads*6 index by Quad - - //TraceLog(LOG_DEBUG, "Quads to render: %i - Vertex Count: %i", quadsCount, draws[i].vertexCount); - - glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, draws[i].textureId); - // NOTE: The final parameter tells the GPU the offset in bytes from the start of the index buffer to the location of the first index to process - #if defined(GRAPHICS_API_OPENGL_33) - glDrawElements(GL_TRIANGLES, numIndicesToProcess, GL_UNSIGNED_INT, (GLvoid *)(sizeof(GLuint)*indicesOffset)); - #elif defined(GRAPHICS_API_OPENGL_ES2) - glDrawElements(GL_TRIANGLES, numIndicesToProcess, GL_UNSIGNED_SHORT, (GLvoid *)(sizeof(GLushort)*indicesOffset)); - #endif - //GLenum err; - //if ((err = glGetError()) != GL_NO_ERROR) TraceLog(LOG_INFO, "OpenGL error: %i", (int)err); //GL_INVALID_ENUM! + if ((draws[i].mode == RL_LINES) || (draws[i].mode == RL_TRIANGLES)) glDrawArrays(draws[i].mode, vertexOffset, draws[i].vertexCount); + else + { +#if defined(GRAPHICS_API_OPENGL_33) + // We need to define the number of indices to be processed: quadsCount*6 + // NOTE: The final parameter tells the GPU the offset in bytes from the + // start of the index buffer to the location of the first index to process + glDrawElements(GL_TRIANGLES, draws[i].vertexCount/4*6, GL_UNSIGNED_INT, (GLvoid *)(sizeof(GLuint)*vertexOffset/4*6)); +#elif defined(GRAPHICS_API_OPENGL_ES2) + glDrawElements(GL_TRIANGLES, draws[i].vertexCount/4*6, GL_UNSIGNED_SHORT, (GLvoid *)(sizeof(GLuint)*vertexOffset/4*6)); +#endif + } - indicesOffset += draws[i].vertexCount/4*6; + vertexOffset += draws[i].vertexCount; } if (!vaoSupported) @@ -4375,13 +4039,9 @@ static void DrawBuffersDefault(void) } // Reset vertex counters for next frame - lines.vCounter = 0; - lines.cCounter = 0; - triangles.vCounter = 0; - triangles.cCounter = 0; - quads.vCounter = 0; - quads.tcCounter = 0; - quads.cCounter = 0; + vertexData[currentBuffer].vCounter = 0; + vertexData[currentBuffer].tcCounter = 0; + vertexData[currentBuffer].cCounter = 0; // Reset depth for next draw currentDepth = -1.0f; @@ -4391,9 +4051,14 @@ static void DrawBuffersDefault(void) modelview = matModelView; // Reset draws counter - drawsCounter = 1; - draws[0].textureId = whiteTexture; + draws[0].mode = RL_QUADS; draws[0].vertexCount = 0; + draws[0].textureId = whiteTexture; + drawsCounter = 1; + + // Change to next buffer in the list + currentBuffer++; + if (currentBuffer >= MAX_BATCH_BUFFERING) currentBuffer = 0; } // Unload default internal buffers vertex data from CPU and GPU @@ -4408,35 +4073,23 @@ static void UnloadBuffersDefault(void) glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - // Delete VBOs from GPU (VRAM) - glDeleteBuffers(1, &lines.vboId[0]); - glDeleteBuffers(1, &lines.vboId[1]); - glDeleteBuffers(1, &triangles.vboId[0]); - glDeleteBuffers(1, &triangles.vboId[1]); - glDeleteBuffers(1, &quads.vboId[0]); - glDeleteBuffers(1, &quads.vboId[1]); - glDeleteBuffers(1, &quads.vboId[2]); - glDeleteBuffers(1, &quads.vboId[3]); - - if (vaoSupported) + for (int i = 0; i < MAX_BATCH_BUFFERING; i++) { + // Delete VBOs from GPU (VRAM) + glDeleteBuffers(1, &vertexData[i].vboId[0]); + glDeleteBuffers(1, &vertexData[i].vboId[1]); + glDeleteBuffers(1, &vertexData[i].vboId[2]); + glDeleteBuffers(1, &vertexData[i].vboId[3]); + // Delete VAOs from GPU (VRAM) - glDeleteVertexArrays(1, &lines.vaoId); - glDeleteVertexArrays(1, &triangles.vaoId); - glDeleteVertexArrays(1, &quads.vaoId); + if (vaoSupported) glDeleteVertexArrays(1, &vertexData[i].vaoId); + + // Free vertex arrays memory from CPU (RAM) + free(vertexData[i].vertices); + free(vertexData[i].texcoords); + free(vertexData[i].colors); + free(vertexData[i].indices); } - - // Free vertex arrays memory from CPU (RAM) - free(lines.vertices); - free(lines.colors); - - free(triangles.vertices); - free(triangles.colors); - - free(quads.vertices); - free(quads.texcoords); - free(quads.colors); - free(quads.indices); } // Renders a 1x1 XY quad in NDC