From b0bfe5812fafd9b35057af4880311d7796ac61fd Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 24 Mar 2019 21:51:23 +0100 Subject: [PATCH 01/74] Update BINDINGS.md --- BINDINGS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/BINDINGS.md b/BINDINGS.md index b926b824d..6f0f493d4 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -19,6 +19,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to - [raylib-java](https://github.com/XoanaIO/raylib-java) : raylib **Java** binding - [raylib-chaiscript](https://github.com/RobLoach/raylib-chaiscript) : raylib **ChaiScript** binding - [node-raylib](https://github.com/RobLoach/node-raylib) : **Node.js** raylib binding + - [odin-raylib](https://github.com/kevinw/odin-raylib): **Odin** raylib binding - *[raylib flat-assembler Usage example]()* - *[raylib COBOL Usage example](https://github.com/Martinfx/Cobol/tree/master/OpenCobol/Games/raylib)* From 86ef08c4082ab5b4214b6f0e9abd2905c19c0be7 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 25 Mar 2019 10:12:38 +0100 Subject: [PATCH 02/74] Update README.md --- README.md | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index b11665c8d..ba7f5389a 100644 --- a/README.md +++ b/README.md @@ -4,11 +4,13 @@ raylib is highly inspired by Borland BGI graphics lib and by XNA framework. -raylib could be useful for prototyping, tools development, graphic applications, embedded systems and education. +raylib is especially well suited for prototyping, tooling, graphical applications, embedded systems and education. NOTE for ADVENTURERS: raylib is a programming library to enjoy videogames programming; no fancy interface, no visual helpers, no auto-debugging... just coding in the most -pure spartan-programmers way. Are you ready to learn? Jump to [code examples!](http://www.raylib.com/examples.html) +pure spartan-programmers way. + +Ready to learn? Jump to [code examples!](http://www.raylib.com/examples.html) [![Build Status](https://travis-ci.org/raysan5/raylib.svg?branch=master)](https://travis-ci.org/raysan5/raylib) [![https://ci.appveyor.com/api/projects/status/github/raysan5/raylib?svg=true](https://ci.appveyor.com/api/projects/status/github/raysan5/raylib?svg=true)](https://ci.appveyor.com/project/raysan5/raylib) @@ -18,20 +20,20 @@ pure spartan-programmers way. Are you ready to learn? Jump to [code examples!](h features -------- - - **NO external dependencies**, all required libraries included with raylib + - **NO external dependencies**, all required libraries are bundled into raylib - Multiple platforms supported: **Windows, Linux, MacOS, Android... and many more!** - Written in plain C code (C99) in PascalCase/camelCase notation - Hardware accelerated with OpenGL (**1.1, 2.1, 3.3 or ES 2.0**) - **Unique OpenGL abstraction layer** (usable as standalone module): [rlgl](https://github.com/raysan5/raylib/blob/master/src/rlgl.h) - Multiple Fonts formats supported (XNA fonts, AngelCode fonts, TTF) - Outstanding texture formats support, including compressed formats (DXT, ETC, ASTC) - - **Full 3d support** for 3d Shapes, Models, Billboards, Heightmaps and more! + - **Full 3D support**, including 3D Shapes, Models, Billboards, Heightmaps and more! - Flexible Materials system, supporting classic maps and **PBR maps** - - Shaders support, including Model shaders and Postprocessing shaders + - Custom shaders, including model and postprocessing shaders. - **Powerful math module** for Vector, Matrix and Quaternion operations: [raymath](https://github.com/raysan5/raylib/blob/master/src/raymath.h) - Audio loading and playing with streaming support (WAV, OGG, MP3, FLAC, XM, MOD) - **VR stereo rendering** support with configurable HMD device parameters - - Bindings to **Lua** ([raylib-lua](https://github.com/raysan5/raylib-lua)), **Go** ([raylib-go](https://github.com/gen2brain/raylib-go)) and [more](https://github.com/raysan5/raylib/blob/master/CONTRIBUTING.md#raylib-bindings)! + - Bindings to [multiple code languages](https://github.com/raysan5/raylib/blob/master/BINDINGS.md)! raylib uses on its [core](https://github.com/raysan5/raylib/blob/master/src/core.c) module the outstanding [GLFW3](http://www.glfw.org/) library, embedded inside raylib in the form of [rglfw](https://github.com/raysan5/raylib/blob/master/src/rglfw.c) module, avoiding that way external dependencies. @@ -46,16 +48,16 @@ raylib uses internally multiple single-file header-only libraries to support mul build and installation ---------------------- -Binary releases for Windows, Linux and macOS are available at the [Github Releases](https://github.com/raysan5/raylib/releases) page. raylib is also available via multiple package managers on multiple OS distributions. For more info check [raylib Wiki](https://github.com/raysan5/raylib/wiki). +Binary releases for Windows, Linux and macOS are available at the [Github Releases page](https://github.com/raysan5/raylib/releases). raylib is also available via multiple package managers on multiple OS distributions. For more info check [raylib Wiki](https://github.com/raysan5/raylib/wiki). -To build raylib yourself, check out also the [raylib Wiki](https://github.com/raysan5/raylib/wiki) for detailed instructions. +If you wish to build raylib yourself, [the raylib Wiki](https://github.com/raysan5/raylib/wiki) also contains detailed instructions on how to approach that. -raylib has been developed using exclusively two tools: +raylib has been developed exclusively using two tools: - * Notepad++ (text editor) - [http://notepad-plus-plus.org/](http://notepad-plus-plus.org/) - * MinGW (GCC compiler) - [http://www.mingw.org/](http://www.mingw.org/) - -Those are the tools recommended to develop with raylib, in fact, those are the tools my students use. + * Notepad++ (text editor) - [http://notepad-plus-plus.org](http://notepad-plus-plus.org/) + * MinGW (GCC compiler) - [http://www.mingw.org](http://www.mingw.org/) + +Those tools are recommended for raylib development. contact ------- @@ -71,7 +73,7 @@ contact If you are using raylib and you enjoy it, please, [let me know][raysan5]. -If you feel you can help, then, [helpme!](http://www.raylib.com/helpme.html) +If you are able to contribute, then please [helpme](http://www.raylib.com/helpme.html)! license ------- From a7b06e7f014a8d0a8628482e63d7c567f7b4a388 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 25 Mar 2019 10:13:42 +0100 Subject: [PATCH 03/74] Update BINDINGS.md --- BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index 6f0f493d4..dec135110 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -19,7 +19,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to - [raylib-java](https://github.com/XoanaIO/raylib-java) : raylib **Java** binding - [raylib-chaiscript](https://github.com/RobLoach/raylib-chaiscript) : raylib **ChaiScript** binding - [node-raylib](https://github.com/RobLoach/node-raylib) : **Node.js** raylib binding - - [odin-raylib](https://github.com/kevinw/odin-raylib): **Odin** raylib binding + - [raylib-odin](https://github.com/kevinw/raylib-odin): **Odin** raylib binding - *[raylib flat-assembler Usage example]()* - *[raylib COBOL Usage example](https://github.com/Martinfx/Cobol/tree/master/OpenCobol/Games/raylib)* From 7524fdc3e1815245841cbfb0193f4dc234916de0 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 25 Mar 2019 12:30:20 +0100 Subject: [PATCH 04/74] Review gestures disable flag --- src/core.c | 56 +++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 41 insertions(+), 15 deletions(-) diff --git a/src/core.c b/src/core.c index aab60f13c..7e807ed41 100644 --- a/src/core.c +++ b/src/core.c @@ -3684,7 +3684,6 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event) ProcessGestureEvent(gestureEvent); } #else - // Support only simple touch position if (flags == AMOTION_EVENT_ACTION_DOWN) { @@ -3788,6 +3787,7 @@ static EM_BOOL EmscriptenTouchCallback(int eventType, const EmscriptenTouchEvent } */ +#if defined(SUPPORT_GESTURES_SYSTEM) GestureEvent gestureEvent; // Register touch actions @@ -3822,6 +3822,17 @@ static EM_BOOL EmscriptenTouchCallback(int eventType, const EmscriptenTouchEvent // Gesture data is sent to gestures system for processing ProcessGestureEvent(gestureEvent); +#else + // Support only simple touch position + if (eventType == EMSCRIPTEN_EVENT_TOUCHSTART) + { + // Get first touch position + touchPosition[0] = (Vector2){ touchEvent->touches[0].targetX, touchEvent->touches[0].targetY }; + + touchPosition[0].x /= (float)GetScreenWidth(); + touchPosition[0].y /= (float)GetScreenHeight(); + } +#endif return 1; } @@ -4242,9 +4253,10 @@ static void EventThreadSpawn(char *device) static void *EventThread(void *arg) { struct input_event event; - GestureEvent gestureEvent; InputEventWorker *worker = (InputEventWorker *)arg; - bool GestureNeedsUpdate = false; + + int touchAction = -1; + bool gestureUpdate = false; while (!windowShouldClose) { @@ -4257,16 +4269,18 @@ static void *EventThread(void *arg) { mousePosition.x += event.value; touchPosition[0].x = mousePosition.x; - gestureEvent.touchAction = TOUCH_MOVE; - GestureNeedsUpdate = true; + + touchAction = TOUCH_MOVE; + gestureUpdate = true; } if (event.code == REL_Y) { mousePosition.y += event.value; touchPosition[0].y = mousePosition.y; - gestureEvent.touchAction = TOUCH_MOVE; - GestureNeedsUpdate = true; + + touchAction = TOUCH_MOVE; + gestureUpdate = true; } if (event.code == REL_WHEEL) @@ -4282,15 +4296,17 @@ static void *EventThread(void *arg) if (event.code == ABS_X) { mousePosition.x = (event.value - worker->absRange.x)*screenWidth/worker->absRange.width; // Scale acording to absRange - gestureEvent.touchAction = TOUCH_MOVE; - GestureNeedsUpdate = true; + + touchAction = TOUCH_MOVE; + gestureUpdate = true; } if (event.code == ABS_Y) { mousePosition.y = (event.value - worker->absRange.y)*screenHeight/worker->absRange.height; // Scale acording to absRange - gestureEvent.touchAction = TOUCH_MOVE; - GestureNeedsUpdate = true; + + touchAction = TOUCH_MOVE; + gestureUpdate = true; } // Multitouch movement @@ -4328,9 +4344,10 @@ static void *EventThread(void *arg) if ((event.code == BTN_TOUCH) || (event.code == BTN_LEFT)) { currentMouseStateEvdev[MOUSE_LEFT_BUTTON] = event.value; - if (event.value > 0) gestureEvent.touchAction = TOUCH_DOWN; - else gestureEvent.touchAction = TOUCH_UP; - GestureNeedsUpdate = true; + + if (event.value > 0) touchAction = TOUCH_DOWN; + else touchAction = TOUCH_UP; + gestureUpdate = true; } if (event.code == BTN_RIGHT) currentMouseStateEvdev[MOUSE_RIGHT_BUTTON] = event.value; @@ -4346,22 +4363,31 @@ static void *EventThread(void *arg) if (mousePosition.y > screenHeight/mouseScale.y) mousePosition.y = screenHeight/mouseScale.y; // Gesture update - if (GestureNeedsUpdate) + if (gestureUpdate) { +#if defined(SUPPORT_GESTURES_SYSTEM) + GestureEvent gestureEvent = { 0 }; + gestureEvent.pointCount = 0; + gestureEvent.touchAction = touchAction; + if (touchPosition[0].x >= 0) gestureEvent.pointCount++; if (touchPosition[1].x >= 0) gestureEvent.pointCount++; if (touchPosition[2].x >= 0) gestureEvent.pointCount++; if (touchPosition[3].x >= 0) gestureEvent.pointCount++; + gestureEvent.pointerId[0] = 0; gestureEvent.pointerId[1] = 1; gestureEvent.pointerId[2] = 2; gestureEvent.pointerId[3] = 3; + gestureEvent.position[0] = touchPosition[0]; gestureEvent.position[1] = touchPosition[1]; gestureEvent.position[2] = touchPosition[2]; gestureEvent.position[3] = touchPosition[3]; + ProcessGestureEvent(gestureEvent); +#endif } } else From 3a1e884d03d741a5bd9b9b57b204d27eff313583 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 27 Mar 2019 20:19:33 +0100 Subject: [PATCH 05/74] Update BINDINGS.md --- BINDINGS.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/BINDINGS.md b/BINDINGS.md index dec135110..ac8276b9f 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -23,6 +23,8 @@ Some people ported raylib to other languages in form of bindings or wrappers to - *[raylib flat-assembler Usage example]()* - *[raylib COBOL Usage example](https://github.com/Martinfx/Cobol/tree/master/OpenCobol/Games/raylib)* +Missing some language? Check the [bindings not yet in this list](https://gist.github.com/raysan5/5764cc5b885183f523fce47f098f3d9b#bindings-not-yet-in-the-official-list) or create a new binding! :) + Usually, raylib bindings follow the convention: `raylib-{language}` Let me know if you're writing a new binding for raylib, I will list it here and I usually From 165ced94286073a708d69119e8e8fde381710da2 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 28 Mar 2019 13:03:25 +0100 Subject: [PATCH 06/74] Small tweak --- src/shapes.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/shapes.c b/src/shapes.c index fd28f3a3b..af683f2ee 100644 --- a/src/shapes.c +++ b/src/shapes.c @@ -214,7 +214,7 @@ void DrawCircleSector(Vector2 center, float radius, int startAngle, int endAngle rlDisableTexture(); #else - if (rlCheckBufferLimit(3*((360/CIRCLE_SECTOR_LENGTH)/2))) rlglDraw(); + if (rlCheckBufferLimit(3*360/CIRCLE_SECTOR_LENGTH)) rlglDraw(); rlBegin(RL_TRIANGLES); for (int i = startAngle; i < endAngle; i += CIRCLE_SECTOR_LENGTH) @@ -239,7 +239,7 @@ void DrawCircleGradient(int centerX, int centerY, float radius, Color color1, Co for (int i = 0; i < 360; i += 10) { rlColor4ub(color1.r, color1.g, color1.b, color1.a); - rlVertex2i(centerX, centerY); + rlVertex2f(centerX, centerY); rlColor4ub(color2.r, color2.g, color2.b, color2.a); rlVertex2f(centerX + sinf(DEG2RAD*i)*radius, centerY + cosf(DEG2RAD*i)*radius); rlColor4ub(color2.r, color2.g, color2.b, color2.a); From 186d34827af93ec760c81a2abea09991c01556b3 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 28 Mar 2019 18:05:32 +0100 Subject: [PATCH 07/74] Align LINES and TRIANGLES drawing When drawing LINES or TRIANGLES, vertex are accumulated in same buffer as QUADS and new draw calls are registered but QUADS drawing uses an index buffer for optimization, so, when adding LINES/TRIANGLES vertices we need to make sure next draw calls for QUADS keep aligned with indices buffer. To get that we just add some alignment vertex at the end of the LINES/TRIANGLES draw calls, to make them multiple of 4 vertex. --- src/rlgl.h | 59 ++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 46 insertions(+), 13 deletions(-) diff --git a/src/rlgl.h b/src/rlgl.h index 00658fa14..d4faec188 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -728,6 +728,7 @@ typedef struct DynamicBuffer { typedef struct DrawCall { int mode; // Drawing mode: LINES, TRIANGLES, QUADS int vertexCount; // Number of vertex of the draw + int vertexAlignment; // Number of vertex required for index alignment (LINES, TRIANGLES) //unsigned int vaoId; // Vertex Array id to be used on the draw //unsigned int shaderId; // Shader id to be used on the draw unsigned int textureId; // Texture id to be used on the draw @@ -1123,7 +1124,27 @@ void rlBegin(int mode) // 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 (draws[drawsCounter - 1].vertexCount > 0) + { + // Make sure current draws[i].vertexCount is aligned a multiple of 4, + // that way, following QUADS drawing will keep aligned with index processing + // It implies adding some extra alignment vertex at the end of the draw, + // those vertex are not processed but they are considered as an additional offset + // for the next set of vertex to be drawn + if (draws[drawsCounter - 1].mode == RL_LINES) draws[drawsCounter - 1].vertexAlignment = ((draws[drawsCounter - 1].vertexCount < 4)? draws[drawsCounter - 1].vertexCount : draws[drawsCounter - 1].vertexCount%4); + else if (draws[drawsCounter - 1].mode == RL_TRIANGLES) draws[drawsCounter - 1].vertexAlignment = ((draws[drawsCounter - 1].vertexCount < 4)? 1 : (4 - (draws[drawsCounter - 1].vertexCount%4))); + + if (rlCheckBufferLimit(draws[drawsCounter - 1].vertexAlignment)) rlglDraw(); + else + { + vertexData[currentBuffer].vCounter += draws[drawsCounter - 1].vertexAlignment; + vertexData[currentBuffer].cCounter += draws[drawsCounter - 1].vertexAlignment; + vertexData[currentBuffer].tcCounter += draws[drawsCounter - 1].vertexAlignment; + + drawsCounter++; + } + } + if (drawsCounter >= MAX_DRAWCALL_REGISTERED) rlglDraw(); draws[drawsCounter - 1].mode = mode; @@ -1135,15 +1156,6 @@ void rlBegin(int mode) // Finish vertex providing void rlEnd(void) { - // 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 vertexCount = draws[drawsCounter - 1].vertexCount; - int vertexToAlign = (vertexCount >= 4)? vertexCount%4 : (4 - 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 @@ -1283,7 +1295,27 @@ void rlEnableTexture(unsigned int id) #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) if (draws[drawsCounter - 1].textureId != id) { - if (draws[drawsCounter - 1].vertexCount > 0) drawsCounter++; + if (draws[drawsCounter - 1].vertexCount > 0) + { + // Make sure current draws[i].vertexCount is aligned a multiple of 4, + // that way, following QUADS drawing will keep aligned with index processing + // It implies adding some extra alignment vertex at the end of the draw, + // those vertex are not processed but they are considered as an additional offset + // for the next set of vertex to be drawn + if (draws[drawsCounter - 1].mode == RL_LINES) draws[drawsCounter - 1].vertexAlignment = ((draws[drawsCounter - 1].vertexCount < 4)? draws[drawsCounter - 1].vertexCount : draws[drawsCounter - 1].vertexCount%4); + else if (draws[drawsCounter - 1].mode == RL_TRIANGLES) draws[drawsCounter - 1].vertexAlignment = ((draws[drawsCounter - 1].vertexCount < 4)? 1 : (4 - (draws[drawsCounter - 1].vertexCount%4))); + + if (rlCheckBufferLimit(draws[drawsCounter - 1].vertexAlignment)) rlglDraw(); + else + { + vertexData[currentBuffer].vCounter += draws[drawsCounter - 1].vertexAlignment; + vertexData[currentBuffer].cCounter += draws[drawsCounter - 1].vertexAlignment; + vertexData[currentBuffer].tcCounter += draws[drawsCounter - 1].vertexAlignment; + + drawsCounter++; + } + } + if (drawsCounter >= MAX_DRAWCALL_REGISTERED) rlglDraw(); draws[drawsCounter - 1].textureId = id; @@ -1682,6 +1714,7 @@ void rlglInit(int width, int height) { draws[i].mode = RL_QUADS; draws[i].vertexCount = 0; + draws[i].vertexAlignment = 0; //draws[i].vaoId = 0; //draws[i].shaderId = 0; draws[i].textureId = defaultTextureId; @@ -4190,8 +4223,8 @@ static void DrawBuffersDefault(void) glDrawElements(GL_TRIANGLES, draws[i].vertexCount/4*6, GL_UNSIGNED_SHORT, (GLvoid *)(sizeof(GLushort)*vertexOffset/4*6)); #endif } - - vertexOffset += draws[i].vertexCount; + + vertexOffset += (draws[i].vertexCount + draws[i].vertexAlignment); } if (!vaoSupported) From 88dfd2ab236a28db3cf1e3715b9e98a426de48d6 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 28 Mar 2019 18:53:41 +0100 Subject: [PATCH 08/74] REDESIGNED: DrawCircleSector() --- src/raylib.h | 2 +- src/shapes.c | 74 +++++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 60 insertions(+), 16 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index c72f0682d..5db50c040 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1043,7 +1043,7 @@ RLAPI void DrawLineV(Vector2 startPos, Vector2 endPos, Color color); RLAPI void DrawLineEx(Vector2 startPos, Vector2 endPos, float thick, Color color); // Draw a line defining thickness RLAPI void DrawLineBezier(Vector2 startPos, Vector2 endPos, float thick, Color color); // Draw a line using cubic-bezier curves in-out RLAPI void DrawCircle(int centerX, int centerY, float radius, Color color); // Draw a color-filled circle -RLAPI void DrawCircleSector(Vector2 center, float radius, int startAngle, int endAngle, Color color); // Draw a piece of a circle +RLAPI void DrawCircleSector(Vector2 center, float radius, int startAngle, int endAngle, int segments, Color color); // Draw a piece of a circle RLAPI void DrawCircleGradient(int centerX, int centerY, float radius, Color color1, Color color2); // Draw a gradient-filled circle RLAPI void DrawCircleV(Vector2 center, float radius, Color color); // Draw a color-filled circle (Vector version) RLAPI void DrawCircleLines(int centerX, int centerY, float radius, Color color); // Draw circle outline diff --git a/src/shapes.c b/src/shapes.c index af683f2ee..5d93078b0 100644 --- a/src/shapes.c +++ b/src/shapes.c @@ -183,18 +183,40 @@ void DrawCircle(int centerX, int centerY, float radius, Color color) } // Draw a piece of a circle -// TODO: Support better angle resolution (now limited to CIRCLE_SECTOR_LENGTH) -void DrawCircleSector(Vector2 center, float radius, int startAngle, int endAngle, Color color) +void DrawCircleSector(Vector2 center, float radius, int startAngle, int endAngle, int segments, Color color) { - #define CIRCLE_SECTOR_LENGTH 10 - + // Function expects (endAngle > startAngle) + if (endAngle < startAngle) + { + // Swap values + int tmp = startAngle; + startAngle = endAngle; + endAngle = tmp; + } + + if (segments < 4) + { + // Calculate how many segments we need to draw a smooth circle, taken from https://stackoverflow.com/a/2244088 + #define CIRCLE_ERROR_RATE 0.5f + + // Calculate the maximum angle between segments based on the error rate. + float th = acosf(2*powf(1 - CIRCLE_ERROR_RATE/radius, 2) - 1); + segments = (endAngle - startAngle)*ceilf(2*PI/th)/360; + + if (segments <= 0) segments = 4; + } + + float stepLength = (float)(endAngle - startAngle)/(float)segments; + float angle = startAngle; + #if defined(SUPPORT_QUADS_DRAW_MODE) - if (rlCheckBufferLimit(4*((360/CIRCLE_SECTOR_LENGTH)/2))) rlglDraw(); + if (rlCheckBufferLimit(4*segments/2)) rlglDraw(); rlEnableTexture(GetShapesTexture().id); rlBegin(RL_QUADS); - for (int i = startAngle; i < endAngle; i += CIRCLE_SECTOR_LENGTH*2) + // NOTE: Every QUAD actually represents two segments + for (int i = 0; i < segments/2; i++) { rlColor4ub(color.r, color.g, color.b, color.a); @@ -202,28 +224,50 @@ void DrawCircleSector(Vector2 center, float radius, int startAngle, int endAngle rlVertex2f(center.x, center.y); rlTexCoord2f(recTexShapes.x/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height); - rlVertex2f(center.x + sinf(DEG2RAD*i)*radius, center.y + cosf(DEG2RAD*i)*radius); - + rlVertex2f(center.x + sinf(DEG2RAD*angle)*radius, center.y + cosf(DEG2RAD*angle)*radius); + rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height); - rlVertex2f(center.x + sinf(DEG2RAD*(i + CIRCLE_SECTOR_LENGTH))*radius, center.y + cosf(DEG2RAD*(i + CIRCLE_SECTOR_LENGTH))*radius); + rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*radius, center.y + cosf(DEG2RAD*(angle + stepLength))*radius); rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, recTexShapes.y/texShapes.height); - rlVertex2f(center.x + sinf(DEG2RAD*(i + CIRCLE_SECTOR_LENGTH*2))*radius, center.y + cosf(DEG2RAD*(i + CIRCLE_SECTOR_LENGTH*2))*radius); + rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength*2))*radius, center.y + cosf(DEG2RAD*(angle + stepLength*2))*radius); + + angle += (stepLength*2); + } + + // NOTE: In case number of segments is odd, we add one last piece to the cake + if (segments%2) + { + rlColor4ub(color.r, color.g, color.b, color.a); + + rlTexCoord2f(recTexShapes.x/texShapes.width, recTexShapes.y/texShapes.height); + rlVertex2f(center.x, center.y); + + rlTexCoord2f(recTexShapes.x/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height); + rlVertex2f(center.x + sinf(DEG2RAD*angle)*radius, center.y + cosf(DEG2RAD*angle)*radius); + + rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height); + rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*radius, center.y + cosf(DEG2RAD*(angle + stepLength))*radius); + + rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, recTexShapes.y/texShapes.height); + rlVertex2f(center.x, center.y); } rlEnd(); rlDisableTexture(); #else - if (rlCheckBufferLimit(3*360/CIRCLE_SECTOR_LENGTH)) rlglDraw(); + if (rlCheckBufferLimit(3*segments)) rlglDraw(); rlBegin(RL_TRIANGLES); - for (int i = startAngle; i < endAngle; i += CIRCLE_SECTOR_LENGTH) + for (int i = 0; i < segments; i++) { rlColor4ub(color.r, color.g, color.b, color.a); rlVertex2f(center.x, center.y); - rlVertex2f(center.x + sinf(DEG2RAD*i)*radius, center.y + cosf(DEG2RAD*i)*radius); - rlVertex2f(center.x + sinf(DEG2RAD*(i + CIRCLE_SECTOR_LENGTH))*radius, center.y + cosf(DEG2RAD*(i + CIRCLE_SECTOR_LENGTH))*radius); + rlVertex2f(center.x + sinf(DEG2RAD*angle)*radius, center.y + cosf(DEG2RAD*angle)*radius); + rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*radius, center.y + cosf(DEG2RAD*(angle + stepLength))*radius); + + angle += stepLength; } rlEnd(); #endif @@ -252,7 +296,7 @@ void DrawCircleGradient(int centerX, int centerY, float radius, Color color1, Co // NOTE: On OpenGL 3.3 and ES2 we use QUADS to avoid drawing order issues (view rlglDraw) void DrawCircleV(Vector2 center, float radius, Color color) { - DrawCircleSector(center, radius, 0, 360, color); + DrawCircleSector(center, radius, 0, 360, 36, color); } // Draw circle outline From b1e914bbf317cad17eeb3b161dc04c0a7ec78d0d Mon Sep 17 00:00:00 2001 From: Berni8k Date: Thu, 28 Mar 2019 19:46:39 +0100 Subject: [PATCH 09/74] RaspberryPi Keyboard input with evdev Based on pull request from user "DarkElvenAngel", better integrated with the current event system and enhanced with buffer to help with fast typing at low framerates. --- src/core.c | 85 +++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 77 insertions(+), 8 deletions(-) diff --git a/src/core.c b/src/core.c index 7e807ed41..d70d0827d 100644 --- a/src/core.c +++ b/src/core.c @@ -361,6 +361,15 @@ typedef struct { static InputEventWorker eventWorkers[10]; // List of worker threads for every monitored "/dev/input/event" +typedef struct{ + int Contents[8]; + char Head; + char Tail; +} KeyEventFifo; + +static KeyEventFifo lastKeyPressedEvdev; // Buffer for holding keydown events as they arrive (Needed due to multitreading of event workers) +static char currentKeyStateEvdev[512] = { 0 }; // Registers current frame key state from event based driver (Needs to be seperate because the legacy console based method clears keys on every frame) + #endif #if defined(PLATFORM_WEB) static bool toggleCursorLock = false; // Ask for cursor pointer lock on next click @@ -470,7 +479,7 @@ static EM_BOOL EmscriptenGamepadCallback(int eventType, const EmscriptenGamepadE static void InitKeyboard(void); // Init raw keyboard system (standard input reading) static void ProcessKeyboard(void); // Process keyboard events static void RestoreKeyboard(void); // Restore keyboard system -static void InitMouse(void); // Mouse initialization (including mouse thread) +static void InitEvdevInput(void); // Mouse initialization (including mouse thread) static void EventThreadSpawn(char *device); // Identifies a input device and spawns a thread to handle it if needed static void *EventThread(void *arg); // Input device events reading thread static void InitGamepad(void); // Init raw gamepad input @@ -590,7 +599,7 @@ void InitWindow(int width, int height, const char *title) #if defined(PLATFORM_RPI) // Init raw input system - InitMouse(); // Mouse init + InitEvdevInput(); // Mouse init InitKeyboard(); // Keyboard init InitGamepad(); // Gamepad init #endif @@ -3054,8 +3063,16 @@ static void PollInputEvents(void) #endif #if defined(PLATFORM_RPI) + // Register previous keys states - for (int i = 0; i < 512; i++) previousKeyState[i] = currentKeyState[i]; + for (int i = 0; i < 512; i++)previousKeyState[i] = currentKeyState[i]; + + // Grab a keypress from the evdev fifo if avalable + if(lastKeyPressedEvdev.Head != lastKeyPressedEvdev.Tail) + { + lastKeyPressed = lastKeyPressedEvdev.Contents[lastKeyPressedEvdev.Tail]; // Read the key from the buffer + lastKeyPressedEvdev.Tail = (lastKeyPressedEvdev.Tail + 1) & 0x07; // Increment the tail pointer forwards and binary wraparound after 7 (fifo is 8 elements long) + } // Register previous mouse states previousMouseWheelY = currentMouseWheelY; @@ -3211,10 +3228,10 @@ static void PollInputEvents(void) #endif #if defined(PLATFORM_RPI) - // NOTE: Mouse input events polling is done asynchonously in another pthread - MouseThread() + // NOTE: Mouse input events polling is done asynchonously in another pthread - EventThread() // NOTE: Keyboard reading could be done using input_event(s) reading or just read from stdin, - // we use method 2 (stdin) but maybe in a future we should change to method 1... + // we now use both methods inside here. 2nd method is still used for legacy purposes (Allows for input trough SSH console) ProcessKeyboard(); // NOTE: Gamepad (Joystick) input events polling is done asynchonously in another pthread - GamepadThread() @@ -3921,6 +3938,9 @@ static void ProcessKeyboard(void) // Reset pressed keys array (it will be filled below) for (int i = 0; i < 512; i++) currentKeyState[i] = 0; + // Check keys from event input workers (This is the new keyboard reading method) + for (int i = 0; i < 512; i++)currentKeyState[i] = currentKeyStateEvdev[i]; + // Fill all read bytes (looking for keys) for (int i = 0; i < bufferByteCount; i++) { @@ -4021,8 +4041,8 @@ static void RestoreKeyboard(void) ioctl(STDIN_FILENO, KDSKBMODE, defaultKeyboardMode); } -// Mouse initialization (including mouse thread) -static void InitMouse(void) +// Initialise user input from evdev(/dev/input/event) this means mouse, keyboard or gamepad devices +static void InitEvdevInput(void) { char path[MAX_FILEPATH_LENGTH]; DIR *directory; @@ -4034,6 +4054,11 @@ static void InitMouse(void) touchPosition[i].x = -1; touchPosition[i].y = -1; } + // Reset keypress buffer + lastKeyPressedEvdev.Head = 0; + lastKeyPressedEvdev.Tail = 0; + // Reset keyboard key state + for (int i = 0; i < 512; i++) currentKeyStateEvdev[i] = 0; // Open the linux directory of "/dev/input" directory = opendir(DEFAULT_EVDEV_PATH); @@ -4202,7 +4227,7 @@ static void EventThreadSpawn(char *device) // Decide what to do with the device //------------------------------------------------------------------------------------------------------- - if (worker->isTouch || worker->isMouse) + if (worker->isTouch || worker->isMouse || worker->isKeyboard) { // Looks like a interesting device TraceLog(LOG_INFO, "Opening input device [%s] (%s%s%s%s%s)", device, @@ -4252,14 +4277,35 @@ static void EventThreadSpawn(char *device) // Input device events reading thread static void *EventThread(void *arg) { + // Scancode to keycode mapping for US keyboards + // TODO: Proabobly replace this with a keymap from the X11 to get the correct regional map for the keyboard (Currently non US keyboards will have the wrong mapping for some keys) + static const int keymap_US[] = + {0,256,49,50,51,52,53,54,55,56,57,48,45,61,259,258,81,87,69,82,84, + 89,85,73,79,80,91,93,257,341,65,83,68,70,71,72,74,75,76,59,39,96, + 340,92,90,88,67,86,66,78,77,44,46,47,344,332,342,32,280,290,291, + 292,293,294,295,296,297,298,299,282,281,327,328,329,333,324,325, + 326,334,321,322,323,320,330,0,85,86,300,301,89,90,91,92,93,94,95, + 335,345,331,283,346,101,268,265,266,263,262,269,264,267,260,261, + 112,113,114,115,116,117,118,119,120,121,122,123,124,125,347,127, + 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, + 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, + 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, + 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191, + 192,193,194,0,0,0,0,0,200,201,202,203,204,205,206,207,208,209,210, + 211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226, + 227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242, + 243,244,245,246,247,248,0,0,0,0,0,0,0,}; + struct input_event event; InputEventWorker *worker = (InputEventWorker *)arg; int touchAction = -1; bool gestureUpdate = false; + int keycode; while (!windowShouldClose) { + // Try to read data from the device and only continue if successful if (read(worker->fd, &event, sizeof(event)) == (int)sizeof(event)) { // Relative movement parsing @@ -4341,6 +4387,8 @@ static void *EventThread(void *arg) // Button parsing if (event.type == EV_KEY) { + + // Mouse button parsing if ((event.code == BTN_TOUCH) || (event.code == BTN_LEFT)) { currentMouseStateEvdev[MOUSE_LEFT_BUTTON] = event.value; @@ -4353,6 +4401,27 @@ static void *EventThread(void *arg) if (event.code == BTN_RIGHT) currentMouseStateEvdev[MOUSE_RIGHT_BUTTON] = event.value; if (event.code == BTN_MIDDLE) currentMouseStateEvdev[MOUSE_MIDDLE_BUTTON] = event.value; + + // Keyboard button parsing + if((event.code >= 1) && (event.code <= 255)) //Keyboard keys appear for codes 1 to 255 + { + keycode = keymap_US[event.code & 0xFF]; // The code we get is a scancode so we look up the apropriate keycode + // Make sure we got a valid keycode + if((keycode > 0) && (keycode < sizeof(currentKeyState))) + { + // Store the key information for raylib to later use + currentKeyStateEvdev[keycode] = event.value; + if(event.value > 0) + { + // Add the key int the fifo + lastKeyPressedEvdev.Contents[lastKeyPressedEvdev.Head] = keycode; // Put the data at the front of the fifo snake + lastKeyPressedEvdev.Head = (lastKeyPressedEvdev.Head + 1) & 0x07; // Increment the head pointer forwards and binary wraparound after 7 (fifo is 8 elements long) + // TODO: This fifo is not fully threadsafe with multiple writers, so multiple keyboards hitting a key at the exact same time could miss a key (double write to head before it was incremented) + } + TraceLog(LOG_DEBUG, "KEY%s ScanCode: %4i KeyCode: %4i",event.value == 0 ? "UP":"DOWN", event.code, keycode); + } + } + } // Screen confinement From ea96d0afea630cb5f174a5d433f46f722522fbb3 Mon Sep 17 00:00:00 2001 From: Berni8k Date: Thu, 28 Mar 2019 20:38:13 +0100 Subject: [PATCH 10/74] Fixes compile error when SUPPORT_GESTURES_SYSTEM is undefined on RPi --- src/core.c | 40 +++++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/src/core.c b/src/core.c index d70d0827d..7cfa6f8b7 100644 --- a/src/core.c +++ b/src/core.c @@ -4315,18 +4315,22 @@ static void *EventThread(void *arg) { mousePosition.x += event.value; touchPosition[0].x = mousePosition.x; - - touchAction = TOUCH_MOVE; - gestureUpdate = true; + + #if defined(SUPPORT_GESTURES_SYSTEM) + touchAction = TOUCH_MOVE; + gestureUpdate = true; + #endif } if (event.code == REL_Y) { mousePosition.y += event.value; touchPosition[0].y = mousePosition.y; - - touchAction = TOUCH_MOVE; - gestureUpdate = true; + + #if defined(SUPPORT_GESTURES_SYSTEM) + touchAction = TOUCH_MOVE; + gestureUpdate = true; + #endif } if (event.code == REL_WHEEL) @@ -4342,17 +4346,21 @@ static void *EventThread(void *arg) if (event.code == ABS_X) { mousePosition.x = (event.value - worker->absRange.x)*screenWidth/worker->absRange.width; // Scale acording to absRange - - touchAction = TOUCH_MOVE; - gestureUpdate = true; + + #if defined(SUPPORT_GESTURES_SYSTEM) + touchAction = TOUCH_MOVE; + gestureUpdate = true; + #endif } if (event.code == ABS_Y) { mousePosition.y = (event.value - worker->absRange.y)*screenHeight/worker->absRange.height; // Scale acording to absRange - - touchAction = TOUCH_MOVE; - gestureUpdate = true; + + #if defined(SUPPORT_GESTURES_SYSTEM) + touchAction = TOUCH_MOVE; + gestureUpdate = true; + #endif } // Multitouch movement @@ -4393,9 +4401,11 @@ static void *EventThread(void *arg) { currentMouseStateEvdev[MOUSE_LEFT_BUTTON] = event.value; - if (event.value > 0) touchAction = TOUCH_DOWN; - else touchAction = TOUCH_UP; - gestureUpdate = true; + #if defined(SUPPORT_GESTURES_SYSTEM) + if (event.value > 0) touchAction = TOUCH_DOWN; + else touchAction = TOUCH_UP; + gestureUpdate = true; + #endif } if (event.code == BTN_RIGHT) currentMouseStateEvdev[MOUSE_RIGHT_BUTTON] = event.value; From 69656cb090a53705c515975c127405af87d4f15d Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 29 Mar 2019 12:23:02 +0100 Subject: [PATCH 11/74] Added comment --- src/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core.c b/src/core.c index 7cfa6f8b7..7f2f7f1c3 100644 --- a/src/core.c +++ b/src/core.c @@ -1164,7 +1164,7 @@ void BeginMode2D(Camera2D camera) Matrix matTransform = MatrixMultiply(MatrixMultiply(matOrigin, MatrixMultiply(matScale, matRotation)), matTranslation); - rlMultMatrixf(MatrixToFloat(matTransform)); + rlMultMatrixf(MatrixToFloat(matTransform)); // Apply transformation to modelview } // Ends 2D mode with custom camera From 876c64b1e530b4b9826eef8fcfc5c3bc567c6be8 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 29 Mar 2019 12:27:50 +0100 Subject: [PATCH 12/74] WARNING: This could break something If we have no data to update/draw, we avoid update/draw. On `DrawBuffersDefault()` if no vertes data is available nothing is drawn but some globals: vertexData, projection, modelview, draws... are reseted. There shouldn't be any problem if we don't touch those globals in case no vertex have been processed but, just in case, I warn about it. --- src/rlgl.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/rlgl.h b/src/rlgl.h index d4faec188..49ab1de54 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -1786,8 +1786,12 @@ void rlglClose(void) void rlglDraw(void) { #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - UpdateBuffersDefault(); - DrawBuffersDefault(); // NOTE: Stereo rendering is checked inside + // Only process data if we have data to process + if (vertexData[currentBuffer].vCounter > 0) + { + UpdateBuffersDefault(); + DrawBuffersDefault(); // NOTE: Stereo rendering is checked inside + } #endif } From ab9c6da26f79796a04ca79401617a7244ec9c2a8 Mon Sep 17 00:00:00 2001 From: Demizdor Date: Fri, 29 Mar 2019 16:22:09 +0200 Subject: [PATCH 13/74] Added DrawRing(), DrawRingLines() and DrawCircleSectorLines() --- src/raylib.h | 3 + src/shapes.c | 240 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 243 insertions(+) diff --git a/src/raylib.h b/src/raylib.h index 5db50c040..2f3b7be28 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1044,9 +1044,12 @@ RLAPI void DrawLineEx(Vector2 startPos, Vector2 endPos, float thick, Color color RLAPI void DrawLineBezier(Vector2 startPos, Vector2 endPos, float thick, Color color); // Draw a line using cubic-bezier curves in-out RLAPI void DrawCircle(int centerX, int centerY, float radius, Color color); // Draw a color-filled circle RLAPI void DrawCircleSector(Vector2 center, float radius, int startAngle, int endAngle, int segments, Color color); // Draw a piece of a circle +RLAPI void DrawCircleSectorLines(Vector2 center, float radius, int startAngle, int endAngle, int segments, Color color); // Draw circle sector outline RLAPI void DrawCircleGradient(int centerX, int centerY, float radius, Color color1, Color color2); // Draw a gradient-filled circle RLAPI void DrawCircleV(Vector2 center, float radius, Color color); // Draw a color-filled circle (Vector version) RLAPI void DrawCircleLines(int centerX, int centerY, float radius, Color color); // Draw circle outline +RLAPI void DrawRing(Vector2 center, float innerRadius, float outerRadius, int startAngle, int endAngle, int segments, Color color); // Draw ring +RLAPI void DrawRingLines(Vector2 center, float innerRadius, float outerRadius, int startAngle, int endAngle, int segments, Color color); // Draw ring outline RLAPI void DrawRectangle(int posX, int posY, int width, int height, Color color); // Draw a color-filled rectangle RLAPI void DrawRectangleV(Vector2 position, Vector2 size, Color color); // Draw a color-filled rectangle (Vector version) RLAPI void DrawRectangleRec(Rectangle rec, Color color); // Draw a color-filled rectangle diff --git a/src/shapes.c b/src/shapes.c index 5d93078b0..7e8c0f4b3 100644 --- a/src/shapes.c +++ b/src/shapes.c @@ -185,6 +185,8 @@ void DrawCircle(int centerX, int centerY, float radius, Color color) // Draw a piece of a circle void DrawCircleSector(Vector2 center, float radius, int startAngle, int endAngle, int segments, Color color) { + if(radius == 0) return; // Check this or we'll get a div by zero error otherwise + // Function expects (endAngle > startAngle) if (endAngle < startAngle) { @@ -273,6 +275,70 @@ void DrawCircleSector(Vector2 center, float radius, int startAngle, int endAngle #endif } +void DrawCircleSectorLines(Vector2 center, float radius, int startAngle, int endAngle, int segments, Color color) +{ + if(radius == 0) return; // Check this or we'll get a div by zero error otherwise + + // Function expects (endAngle > startAngle) + if (endAngle < startAngle) + { + // Swap values + int tmp = startAngle; + startAngle = endAngle; + endAngle = tmp; + } + + if (segments < 4) + { + // Calculate how many segments we need to draw a smooth circle, taken from https://stackoverflow.com/a/2244088 + #ifndef CIRCLE_ERROR_RATE + #define CIRCLE_ERROR_RATE 0.5f + #endif + + // Calculate the maximum angle between segments based on the error rate. + float th = acosf(2*powf(1 - CIRCLE_ERROR_RATE/radius, 2) - 1); + segments = (endAngle - startAngle)*ceilf(2*PI/th)/360; + + if (segments <= 0) segments = 4; + } + + float stepLength = (float)(endAngle - startAngle)/(float)segments; + float angle = startAngle; + + // Hide the cap lines when the circle is full + bool showCapLines = true; + int limit = 2*(segments + 2); + if((endAngle - startAngle) % 360 == 0) { limit = 2*segments; showCapLines = false; } + + if (rlCheckBufferLimit(limit)) rlglDraw(); + + rlBegin(RL_LINES); + if(showCapLines) + { + rlColor4ub(color.r, color.g, color.b, color.a); + rlVertex2f(center.x, center.y); + rlVertex2f(center.x + sinf(DEG2RAD*angle)*radius, center.y + cosf(DEG2RAD*angle)*radius); + } + + for (int i = 0; i < segments; i++) + { + rlColor4ub(color.r, color.g, color.b, color.a); + + rlVertex2f(center.x + sinf(DEG2RAD*angle)*radius, center.y + cosf(DEG2RAD*angle)*radius); + rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*radius, center.y + cosf(DEG2RAD*(angle + stepLength))*radius); + + angle += stepLength; + } + + if(showCapLines) + { + rlColor4ub(color.r, color.g, color.b, color.a); + rlVertex2f(center.x, center.y); + rlVertex2f(center.x + sinf(DEG2RAD*angle)*radius, center.y + cosf(DEG2RAD*angle)*radius); + } + rlEnd(); +} + // Draw a gradient-filled circle // NOTE: Gradient goes from center (color1) to border (color2) void DrawCircleGradient(int centerX, int centerY, float radius, Color color1, Color color2) @@ -316,6 +382,180 @@ void DrawCircleLines(int centerX, int centerY, float radius, Color color) rlEnd(); } +void DrawRing(Vector2 center, float innerRadius, float outerRadius, int startAngle, int endAngle, int segments, Color color) +{ + if(startAngle == endAngle) return; + + // Function expects (outerRadius > innerRadius) + if(outerRadius < innerRadius) + { + float tmp = outerRadius; + outerRadius = innerRadius; + innerRadius = tmp; + if(outerRadius == 0) return; // Check this or we'll get a div by zero error otherwise + } + + // Function expects (endAngle > startAngle) + if (endAngle < startAngle) + { + // Swap values + int tmp = startAngle; + startAngle = endAngle; + endAngle = tmp; + } + + if (segments < 4) + { + // Calculate how many segments we need to draw a smooth circle, taken from https://stackoverflow.com/a/2244088 + #ifndef CIRCLE_ERROR_RATE + #define CIRCLE_ERROR_RATE 0.5f + #endif + // Calculate the maximum angle between segments based on the error rate. + float th = acosf(2*powf(1 - CIRCLE_ERROR_RATE/outerRadius, 2) - 1); + segments = (endAngle - startAngle)*ceilf(2*PI/th)/360; + + if (segments <= 0) segments = 4; + } + + // Not a ring + if(innerRadius == 0) + { + DrawCircleSector(center, outerRadius, startAngle, endAngle, segments, color); + return; + } + + float stepLength = (float)(endAngle - startAngle)/(float)segments; + float angle = startAngle; + +#if defined(SUPPORT_QUADS_DRAW_MODE) + if (rlCheckBufferLimit(4*segments)) rlglDraw(); + + rlEnableTexture(GetShapesTexture().id); + + rlBegin(RL_QUADS); + for (int i = 0; i < segments; i++) + { + rlColor4ub(color.r, color.g, color.b, color.a); + + rlTexCoord2f(recTexShapes.x/texShapes.width, recTexShapes.y/texShapes.height); + rlVertex2f(center.x + sinf(DEG2RAD*angle)*innerRadius, center.y + cosf(DEG2RAD*angle)*innerRadius); + + rlTexCoord2f(recTexShapes.x/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height); + rlVertex2f(center.x + sinf(DEG2RAD*angle)*outerRadius, center.y + cosf(DEG2RAD*angle)*outerRadius); + + rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height); + rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*outerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*outerRadius); + + rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, recTexShapes.y/texShapes.height); + rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*innerRadius); + + angle += stepLength; + } + rlEnd(); + + rlDisableTexture(); +#else + if (rlCheckBufferLimit(6*segments)) rlglDraw(); + + rlBegin(RL_TRIANGLES); + for (int i = 0; i < segments; i++) + { + rlColor4ub(color.r, color.g, color.b, color.a); + + rlVertex2f(center.x + sinf(DEG2RAD*angle)*innerRadius, center.y + cosf(DEG2RAD*angle)*innerRadius); + rlVertex2f(center.x + sinf(DEG2RAD*angle)*outerRadius, center.y + cosf(DEG2RAD*angle)*outerRadius); + rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*innerRadius); + + rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*innerRadius); + rlVertex2f(center.x + sinf(DEG2RAD*angle)*outerRadius, center.y + cosf(DEG2RAD*angle)*outerRadius); + rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*outerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*outerRadius); + + angle += stepLength; + } + rlEnd(); +#endif +} + +void DrawRingLines(Vector2 center, float innerRadius, float outerRadius, int startAngle, int endAngle, int segments, Color color) +{ + if(startAngle == endAngle) return; + + // Function expects (outerRadius > innerRadius) + if(outerRadius < innerRadius) + { + float tmp = outerRadius; + outerRadius = innerRadius; + innerRadius = tmp; + if(outerRadius == 0) return; // Check this or we'll get a div by zero error otherwise + } + + // Function expects (endAngle > startAngle) + if (endAngle < startAngle) + { + // Swap values + int tmp = startAngle; + startAngle = endAngle; + endAngle = tmp; + } + + if (segments < 4) + { + // Calculate how many segments we need to draw a smooth circle, taken from https://stackoverflow.com/a/2244088 + #ifndef CIRCLE_ERROR_RATE + #define CIRCLE_ERROR_RATE 0.5f + #endif + // Calculate the maximum angle between segments based on the error rate. + float th = acosf(2*powf(1 - CIRCLE_ERROR_RATE/outerRadius, 2) - 1); + segments = (endAngle - startAngle)*ceilf(2*PI/th)/360; + + if (segments <= 0) segments = 4; + } + + if(innerRadius == 0) + { + DrawCircleSectorLines(center, outerRadius, startAngle, endAngle, segments, color); + return; + } + + float stepLength = (float)(endAngle - startAngle)/(float)segments; + float angle = startAngle; + + bool showCapLines = true; + int limit = 4*(segments + 1); + if((endAngle - startAngle) % 360 == 0) { limit = 4*segments; showCapLines = false; } + + if (rlCheckBufferLimit(limit)) rlglDraw(); + + rlBegin(RL_LINES); + if(showCapLines) + { + rlColor4ub(color.r, color.g, color.b, color.a); + rlVertex2f(center.x + sinf(DEG2RAD*angle)*outerRadius, center.y + cosf(DEG2RAD*angle)*outerRadius); + rlVertex2f(center.x + sinf(DEG2RAD*angle)*innerRadius, center.y + cosf(DEG2RAD*angle)*innerRadius); + } + + for (int i = 0; i < segments; i++) + { + rlColor4ub(color.r, color.g, color.b, color.a); + + rlVertex2f(center.x + sinf(DEG2RAD*angle)*outerRadius, center.y + cosf(DEG2RAD*angle)*outerRadius); + rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*outerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*outerRadius); + + rlVertex2f(center.x + sinf(DEG2RAD*angle)*innerRadius, center.y + cosf(DEG2RAD*angle)*innerRadius); + rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*innerRadius); + + angle += stepLength; + } + + if(showCapLines) + { + rlColor4ub(color.r, color.g, color.b, color.a); + rlVertex2f(center.x + sinf(DEG2RAD*angle)*outerRadius, center.y + cosf(DEG2RAD*angle)*outerRadius); + rlVertex2f(center.x + sinf(DEG2RAD*angle)*innerRadius, center.y + cosf(DEG2RAD*angle)*innerRadius); + } + rlEnd(); +} + // Draw a color-filled rectangle void DrawRectangle(int posX, int posY, int width, int height, Color color) { From a643dc4ca016ce5791608852ab1357f2d94ebe9b Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 29 Mar 2019 16:48:23 +0100 Subject: [PATCH 14/74] WARNING: Redesigned model struct for multi-meshes This is quite a big change, Model struct has been redesigned to support multiple meshes and multiple materials, most 3d fileformats contain multiple meshes and reference multiple materials. Consequently, multiple functions have been reviewed. LoadOBJ(), LoadIQM(), LoadGLFT() now return a Model. Current LoadOBJ() is not valid anymore, actually, tinyobj_loader_c library is considered for replacement. --- src/external/tinyobj_loader_c.h | 1583 +++++++++++++++++++++++++++++++ src/models.c | 155 +-- src/raylib.h | 19 +- 3 files changed, 1684 insertions(+), 73 deletions(-) create mode 100644 src/external/tinyobj_loader_c.h diff --git a/src/external/tinyobj_loader_c.h b/src/external/tinyobj_loader_c.h new file mode 100644 index 000000000..e9d015fff --- /dev/null +++ b/src/external/tinyobj_loader_c.h @@ -0,0 +1,1583 @@ +/* + The MIT License (MIT) + + Copyright (c) 2016 - 2019 Syoyo Fujita and many contributors. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + */ +#ifndef TINOBJ_LOADER_C_H_ +#define TINOBJ_LOADER_C_H_ + +/* @todo { Remove stddef dependency. size_t? } */ +#include + +typedef struct { + char *name; + + float ambient[3]; + float diffuse[3]; + float specular[3]; + float transmittance[3]; + float emission[3]; + float shininess; + float ior; /* index of refraction */ + float dissolve; /* 1 == opaque; 0 == fully transparent */ + /* illumination model (see http://www.fileformat.info/format/material/) */ + int illum; + + int pad0; + + char *ambient_texname; /* map_Ka */ + char *diffuse_texname; /* map_Kd */ + char *specular_texname; /* map_Ks */ + char *specular_highlight_texname; /* map_Ns */ + char *bump_texname; /* map_bump, bump */ + char *displacement_texname; /* disp */ + char *alpha_texname; /* map_d */ +} tinyobj_material_t; + +typedef struct { + char *name; /* group name or object name. */ + unsigned int face_offset; + unsigned int length; +} tinyobj_shape_t; + +typedef struct { int v_idx, vt_idx, vn_idx; } tinyobj_vertex_index_t; + +typedef struct { + unsigned int num_vertices; + unsigned int num_normals; + unsigned int num_texcoords; + unsigned int num_faces; + unsigned int num_face_num_verts; + + int pad0; + + float *vertices; + float *normals; + float *texcoords; + tinyobj_vertex_index_t *faces; + int *face_num_verts; + int *material_ids; +} tinyobj_attrib_t; + + +#define TINYOBJ_FLAG_TRIANGULATE (1 << 0) + +#define TINYOBJ_INVALID_INDEX (0x80000000) + +#define TINYOBJ_SUCCESS (0) +#define TINYOBJ_ERROR_EMPTY (-1) +#define TINYOBJ_ERROR_INVALID_PARAMETER (-2) +#define TINYOBJ_ERROR_FILE_OPERATION (-3) + +/* Parse wavefront .obj(.obj string data is expanded to linear char array `buf') + * flags are combination of TINYOBJ_FLAG_*** + * Returns TINYOBJ_SUCCESS if things goes well. + * Returns TINYOBJ_ERR_*** when there is an error. + */ +extern int tinyobj_parse_obj(tinyobj_attrib_t *attrib, tinyobj_shape_t **shapes, + size_t *num_shapes, tinyobj_material_t **materials, + size_t *num_materials, const char *buf, size_t len, + unsigned int flags); +extern int tinyobj_parse_mtl_file(tinyobj_material_t **materials_out, + size_t *num_materials_out, + const char *filename); + +extern void tinyobj_attrib_init(tinyobj_attrib_t *attrib); +extern void tinyobj_attrib_free(tinyobj_attrib_t *attrib); +extern void tinyobj_shapes_free(tinyobj_shape_t *shapes, size_t num_shapes); +extern void tinyobj_materials_free(tinyobj_material_t *materials, + size_t num_materials); + +#ifdef TINYOBJ_LOADER_C_IMPLEMENTATION +#include +#include +#include +#include + +#if defined(TINYOBJ_MALLOC) && defined(TINYOBJ_REALLOC) && defined(TINYOBJ_CALLOC) && defined(TINYOBJ_FREE) +/* ok */ +#elif !defined(TINYOBJ_MALLOC) && !defined(TINYOBJ_REALLOC) && !defined(TINYOBJ_CALLOC) && !defined(TINYOBJ_FREE) +/* ok */ +#else +#error "Must define all or none of TINYOBJ_MALLOC, TINYOBJ_REALLOC, TINYOBJ_CALLOC and TINYOBJ_FREE." +#endif + +#ifndef TINYOBJ_MALLOC +#include +#define TINYOBJ_MALLOC malloc +#define TINYOBJ_REALLOC realloc +#define TINYOBJ_CALLOC calloc +#define TINYOBJ_FREE free +#endif + +#define TINYOBJ_MAX_FACES_PER_F_LINE (16) + +#define IS_SPACE(x) (((x) == ' ') || ((x) == '\t')) +#define IS_DIGIT(x) ((unsigned int)((x) - '0') < (unsigned int)(10)) +#define IS_NEW_LINE(x) (((x) == '\r') || ((x) == '\n') || ((x) == '\0')) + +static void skip_space(const char **token) { + while ((*token)[0] == ' ' || (*token)[0] == '\t') { + (*token)++; + } +} + +static void skip_space_and_cr(const char **token) { + while ((*token)[0] == ' ' || (*token)[0] == '\t' || (*token)[0] == '\r') { + (*token)++; + } +} + +static int until_space(const char *token) { + const char *p = token; + while (p[0] != '\0' && p[0] != ' ' && p[0] != '\t' && p[0] != '\r') { + p++; + } + + return (int)(p - token); +} + +static size_t length_until_newline(const char *token, size_t n) { + size_t len = 0; + + /* Assume token[n-1] = '\0' */ + for (len = 0; len < n - 1; len++) { + if (token[len] == '\n') { + break; + } + if ((token[len] == '\r') && ((len < (n - 2)) && (token[len + 1] != '\n'))) { + break; + } + } + + return len; +} + +static size_t length_until_line_feed(const char *token, size_t n) { + size_t len = 0; + + /* Assume token[n-1] = '\0' */ + for (len = 0; len < n; len++) { + if ((token[len] == '\n') || (token[len] == '\r')) { + break; + } + } + + return len; +} + +/* http://stackoverflow.com/questions/5710091/how-does-atoi-function-in-c-work +*/ +static int my_atoi(const char *c) { + int value = 0; + int sign = 1; + if (*c == '+' || *c == '-') { + if (*c == '-') sign = -1; + c++; + } + while (((*c) >= '0') && ((*c) <= '9')) { /* isdigit(*c) */ + value *= 10; + value += (int)(*c - '0'); + c++; + } + return value * sign; +} + +/* Make index zero-base, and also support relative index. */ +static int fixIndex(int idx, size_t n) { + if (idx > 0) return idx - 1; + if (idx == 0) return 0; + return (int)n + idx; /* negative value = relative */ +} + +/* Parse raw triples: i, i/j/k, i//k, i/j */ +static tinyobj_vertex_index_t parseRawTriple(const char **token) { + tinyobj_vertex_index_t vi; + /* 0x80000000 = -2147483648 = invalid */ + vi.v_idx = (int)(0x80000000); + vi.vn_idx = (int)(0x80000000); + vi.vt_idx = (int)(0x80000000); + + vi.v_idx = my_atoi((*token)); + while ((*token)[0] != '\0' && (*token)[0] != '/' && (*token)[0] != ' ' && + (*token)[0] != '\t' && (*token)[0] != '\r') { + (*token)++; + } + if ((*token)[0] != '/') { + return vi; + } + (*token)++; + + /* i//k */ + if ((*token)[0] == '/') { + (*token)++; + vi.vn_idx = my_atoi((*token)); + while ((*token)[0] != '\0' && (*token)[0] != '/' && (*token)[0] != ' ' && + (*token)[0] != '\t' && (*token)[0] != '\r') { + (*token)++; + } + return vi; + } + + /* i/j/k or i/j */ + vi.vt_idx = my_atoi((*token)); + while ((*token)[0] != '\0' && (*token)[0] != '/' && (*token)[0] != ' ' && + (*token)[0] != '\t' && (*token)[0] != '\r') { + (*token)++; + } + if ((*token)[0] != '/') { + return vi; + } + + /* i/j/k */ + (*token)++; /* skip '/' */ + vi.vn_idx = my_atoi((*token)); + while ((*token)[0] != '\0' && (*token)[0] != '/' && (*token)[0] != ' ' && + (*token)[0] != '\t' && (*token)[0] != '\r') { + (*token)++; + } + return vi; +} + +static int parseInt(const char **token) { + int i = 0; + skip_space(token); + i = my_atoi((*token)); + (*token) += until_space((*token)); + return i; +} + +/* + * Tries to parse a floating point number located at s. + * + * s_end should be a location in the string where reading should absolutely + * stop. For example at the end of the string, to prevent buffer overflows. + * + * Parses the following EBNF grammar: + * sign = "+" | "-" ; + * END = ? anything not in digit ? + * digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" ; + * integer = [sign] , digit , {digit} ; + * decimal = integer , ["." , integer] ; + * float = ( decimal , END ) | ( decimal , ("E" | "e") , integer , END ) ; + * + * Valid strings are for example: + * -0 +3.1417e+2 -0.0E-3 1.0324 -1.41 11e2 + * + * If the parsing is a success, result is set to the parsed value and true + * is returned. + * + * The function is greedy and will parse until any of the following happens: + * - a non-conforming character is encountered. + * - s_end is reached. + * + * The following situations triggers a failure: + * - s >= s_end. + * - parse failure. + */ +static int tryParseDouble(const char *s, const char *s_end, double *result) { + double mantissa = 0.0; + /* This exponent is base 2 rather than 10. + * However the exponent we parse is supposed to be one of ten, + * thus we must take care to convert the exponent/and or the + * mantissa to a * 2^E, where a is the mantissa and E is the + * exponent. + * To get the final double we will use ldexp, it requires the + * exponent to be in base 2. + */ + int exponent = 0; + + /* NOTE: THESE MUST BE DECLARED HERE SINCE WE ARE NOT ALLOWED + * TO JUMP OVER DEFINITIONS. + */ + char sign = '+'; + char exp_sign = '+'; + char const *curr = s; + + /* How many characters were read in a loop. */ + int read = 0; + /* Tells whether a loop terminated due to reaching s_end. */ + int end_not_reached = 0; + + /* + BEGIN PARSING. + */ + + if (s >= s_end) { + return 0; /* fail */ + } + + /* Find out what sign we've got. */ + if (*curr == '+' || *curr == '-') { + sign = *curr; + curr++; + } else if (IS_DIGIT(*curr)) { /* Pass through. */ + } else { + goto fail; + } + + /* Read the integer part. */ + end_not_reached = (curr != s_end); + while (end_not_reached && IS_DIGIT(*curr)) { + mantissa *= 10; + mantissa += (int)(*curr - 0x30); + curr++; + read++; + end_not_reached = (curr != s_end); + } + + /* We must make sure we actually got something. */ + if (read == 0) goto fail; + /* We allow numbers of form "#", "###" etc. */ + if (!end_not_reached) goto assemble; + + /* Read the decimal part. */ + if (*curr == '.') { + curr++; + read = 1; + end_not_reached = (curr != s_end); + while (end_not_reached && IS_DIGIT(*curr)) { + /* pow(10.0, -read) */ + double frac_value = 1.0; + int f; + for (f = 0; f < read; f++) { + frac_value *= 0.1; + } + mantissa += (int)(*curr - 0x30) * frac_value; + read++; + curr++; + end_not_reached = (curr != s_end); + } + } else if (*curr == 'e' || *curr == 'E') { + } else { + goto assemble; + } + + if (!end_not_reached) goto assemble; + + /* Read the exponent part. */ + if (*curr == 'e' || *curr == 'E') { + curr++; + /* Figure out if a sign is present and if it is. */ + end_not_reached = (curr != s_end); + if (end_not_reached && (*curr == '+' || *curr == '-')) { + exp_sign = *curr; + curr++; + } else if (IS_DIGIT(*curr)) { /* Pass through. */ + } else { + /* Empty E is not allowed. */ + goto fail; + } + + read = 0; + end_not_reached = (curr != s_end); + while (end_not_reached && IS_DIGIT(*curr)) { + exponent *= 10; + exponent += (int)(*curr - 0x30); + curr++; + read++; + end_not_reached = (curr != s_end); + } + if (read == 0) goto fail; + } + +assemble : + + { + double a = 1.0; /* = pow(5.0, exponent); */ + double b = 1.0; /* = 2.0^exponent */ + int i; + for (i = 0; i < exponent; i++) { + a = a * 5.0; + } + + for (i = 0; i < exponent; i++) { + b = b * 2.0; + } + + if (exp_sign == '-') { + a = 1.0 / a; + b = 1.0 / b; + } + + *result = + /* (sign == '+' ? 1 : -1) * ldexp(mantissa * pow(5.0, exponent), + exponent); */ + (sign == '+' ? 1 : -1) * (mantissa * a * b); + } + + return 1; +fail: + return 0; +} + +static float parseFloat(const char **token) { + const char *end; + double val = 0.0; + float f = 0.0f; + skip_space(token); + end = (*token) + until_space((*token)); + val = 0.0; + tryParseDouble((*token), end, &val); + f = (float)(val); + (*token) = end; + return f; +} + +static void parseFloat2(float *x, float *y, const char **token) { + (*x) = parseFloat(token); + (*y) = parseFloat(token); +} + +static void parseFloat3(float *x, float *y, float *z, const char **token) { + (*x) = parseFloat(token); + (*y) = parseFloat(token); + (*z) = parseFloat(token); +} + +static char *my_strdup(const char *s, size_t max_length) { + char *d; + size_t len; + + if (s == NULL) return NULL; + + /* Do not consider CRLF line ending(#19) */ + len = length_until_line_feed(s, max_length); + /* len = strlen(s); */ + + /* trim line ending and append '\0' */ + d = (char *)TINYOBJ_MALLOC(len + 1); /* + '\0' */ + memcpy(d, s, (size_t)(len)); + d[len] = '\0'; + + return d; +} + +static char *my_strndup(const char *s, size_t len) { + char *d; + size_t slen; + + if (s == NULL) return NULL; + if (len == 0) return NULL; + + d = (char *)TINYOBJ_MALLOC(len + 1); /* + '\0' */ + slen = strlen(s); + if (slen < len) { + memcpy(d, s, slen); + d[slen] = '\0'; + } else { + memcpy(d, s, len); + d[len] = '\0'; + } + + return d; +} + +char *dynamic_fgets(char **buf, size_t *size, FILE *file) { + char *offset; + char *ret; + size_t old_size; + + if (!(ret = fgets(*buf, (int)*size, file))) { + return ret; + } + + if (NULL != strchr(*buf, '\n')) { + return ret; + } + + do { + old_size = *size; + *size *= 2; + *buf = (char*)TINYOBJ_REALLOC(*buf, *size); + offset = &((*buf)[old_size - 1]); + + ret = fgets(offset, (int)(old_size + 1), file); + } while(ret && (NULL == strchr(*buf, '\n'))); + + return ret; +} + +static void initMaterial(tinyobj_material_t *material) { + int i; + material->name = NULL; + material->ambient_texname = NULL; + material->diffuse_texname = NULL; + material->specular_texname = NULL; + material->specular_highlight_texname = NULL; + material->bump_texname = NULL; + material->displacement_texname = NULL; + material->alpha_texname = NULL; + for (i = 0; i < 3; i++) { + material->ambient[i] = 0.f; + material->diffuse[i] = 0.f; + material->specular[i] = 0.f; + material->transmittance[i] = 0.f; + material->emission[i] = 0.f; + } + material->illum = 0; + material->dissolve = 1.f; + material->shininess = 1.f; + material->ior = 1.f; +} + +/* Implementation of string to int hashtable */ + +#define HASH_TABLE_ERROR 1 +#define HASH_TABLE_SUCCESS 0 + +#define HASH_TABLE_DEFAULT_SIZE 10 + +typedef struct hash_table_entry_t +{ + unsigned long hash; + int filled; + int pad0; + long value; + + struct hash_table_entry_t* next; +} hash_table_entry_t; + +typedef struct +{ + unsigned long* hashes; + hash_table_entry_t* entries; + size_t capacity; + size_t n; +} hash_table_t; + +static unsigned long hash_djb2(const unsigned char* str) +{ + unsigned long hash = 5381; + int c; + + while ((c = *str++)) { + hash = ((hash << 5) + hash) + (unsigned long)(c); + } + + return hash; +} + +static void create_hash_table(size_t start_capacity, hash_table_t* hash_table) +{ + if (start_capacity < 1) + start_capacity = HASH_TABLE_DEFAULT_SIZE; + hash_table->hashes = (unsigned long*) TINYOBJ_MALLOC(start_capacity * sizeof(unsigned long)); + hash_table->entries = (hash_table_entry_t*) TINYOBJ_CALLOC(start_capacity, sizeof(hash_table_entry_t)); + hash_table->capacity = start_capacity; + hash_table->n = 0; +} + +static void destroy_hash_table(hash_table_t* hash_table) +{ + TINYOBJ_FREE(hash_table->entries); + TINYOBJ_FREE(hash_table->hashes); +} + +/* Insert with quadratic probing */ +static int hash_table_insert_value(unsigned long hash, long value, hash_table_t* hash_table) +{ + /* Insert value */ + size_t start_index = hash % hash_table->capacity; + size_t index = start_index; + hash_table_entry_t* start_entry = hash_table->entries + start_index; + size_t i; + hash_table_entry_t* entry; + + for (i = 1; hash_table->entries[index].filled; i++) + { + if (i >= hash_table->capacity) + return HASH_TABLE_ERROR; + index = (start_index + (i * i)) % hash_table->capacity; + } + + entry = hash_table->entries + index; + entry->hash = hash; + entry->filled = 1; + entry->value = value; + + if (index != start_index) { + /* This is a new entry, but not the start entry, hence we need to add a next pointer to our entry */ + entry->next = start_entry->next; + start_entry->next = entry; + } + + return HASH_TABLE_SUCCESS; +} + +static int hash_table_insert(unsigned long hash, long value, hash_table_t* hash_table) +{ + int ret = hash_table_insert_value(hash, value, hash_table); + if (ret == HASH_TABLE_SUCCESS) + { + hash_table->hashes[hash_table->n] = hash; + hash_table->n++; + } + return ret; +} + +static hash_table_entry_t* hash_table_find(unsigned long hash, hash_table_t* hash_table) +{ + hash_table_entry_t* entry = hash_table->entries + (hash % hash_table->capacity); + while (entry) + { + if (entry->hash == hash && entry->filled) + { + return entry; + } + entry = entry->next; + } + return NULL; +} + +static void hash_table_maybe_grow(size_t new_n, hash_table_t* hash_table) +{ + size_t new_capacity; + hash_table_t new_hash_table; + size_t i; + + if (new_n <= hash_table->capacity) { + return; + } + new_capacity = 2 * ((2 * hash_table->capacity) > new_n ? hash_table->capacity : new_n); + /* Create a new hash table. We're not calling create_hash_table because we want to realloc the hash array */ + new_hash_table.hashes = hash_table->hashes = (unsigned long*) TINYOBJ_REALLOC((void*) hash_table->hashes, sizeof(unsigned long) * new_capacity); + new_hash_table.entries = (hash_table_entry_t*) TINYOBJ_CALLOC(new_capacity, sizeof(hash_table_entry_t)); + new_hash_table.capacity = new_capacity; + new_hash_table.n = hash_table->n; + + /* Rehash */ + for (i = 0; i < hash_table->capacity; i++) + { + hash_table_entry_t* entry = hash_table_find(hash_table->hashes[i], hash_table); + hash_table_insert_value(hash_table->hashes[i], entry->value, &new_hash_table); + } + + TINYOBJ_FREE(hash_table->entries); + (*hash_table) = new_hash_table; +} + +static int hash_table_exists(const char* name, hash_table_t* hash_table) +{ + return hash_table_find(hash_djb2((const unsigned char*)name), hash_table) != NULL; +} + +static void hash_table_set(const char* name, size_t val, hash_table_t* hash_table) +{ + /* Hash name */ + unsigned long hash = hash_djb2((const unsigned char *)name); + + hash_table_entry_t* entry = hash_table_find(hash, hash_table); + if (entry) + { + entry->value = (long)val; + return; + } + + /* Expand if necessary + * Grow until the element has been added + */ + do + { + hash_table_maybe_grow(hash_table->n + 1, hash_table); + } + while (hash_table_insert(hash, (long)val, hash_table) != HASH_TABLE_SUCCESS); +} + +static long hash_table_get(const char* name, hash_table_t* hash_table) +{ + hash_table_entry_t* ret = hash_table_find(hash_djb2((const unsigned char*)(name)), hash_table); + return ret->value; +} + +static tinyobj_material_t *tinyobj_material_add(tinyobj_material_t *prev, + size_t num_materials, + tinyobj_material_t *new_mat) { + tinyobj_material_t *dst; + dst = (tinyobj_material_t *)TINYOBJ_REALLOC( + prev, sizeof(tinyobj_material_t) * (num_materials + 1)); + + dst[num_materials] = (*new_mat); /* Just copy pointer for char* members */ + return dst; +} + +static int tinyobj_parse_and_index_mtl_file(tinyobj_material_t **materials_out, + size_t *num_materials_out, + const char *filename, + hash_table_t* material_table) { + tinyobj_material_t material; + size_t buffer_size = 128; + char *linebuf; + FILE *fp; + size_t num_materials = 0; + tinyobj_material_t *materials = NULL; + int has_previous_material = 0; + const char *line_end = NULL; + + if (materials_out == NULL) { + return TINYOBJ_ERROR_INVALID_PARAMETER; + } + + if (num_materials_out == NULL) { + return TINYOBJ_ERROR_INVALID_PARAMETER; + } + + (*materials_out) = NULL; + (*num_materials_out) = 0; + + fp = fopen(filename, "r"); + if (!fp) { + fprintf(stderr, "TINYOBJ: Error reading file '%s': %s (%d)\n", filename, strerror(errno), errno); + return TINYOBJ_ERROR_FILE_OPERATION; + } + + /* Create a default material */ + initMaterial(&material); + + linebuf = (char*)TINYOBJ_MALLOC(buffer_size); + while (NULL != dynamic_fgets(&linebuf, &buffer_size, fp)) { + const char *token = linebuf; + + line_end = token + strlen(token); + + /* Skip leading space. */ + token += strspn(token, " \t"); + + assert(token); + if (token[0] == '\0') continue; /* empty line */ + + if (token[0] == '#') continue; /* comment line */ + + /* new mtl */ + if ((0 == strncmp(token, "newmtl", 6)) && IS_SPACE((token[6]))) { + char namebuf[4096]; + + /* flush previous material. */ + if (has_previous_material) { + materials = tinyobj_material_add(materials, num_materials, &material); + num_materials++; + } else { + has_previous_material = 1; + } + + /* initial temporary material */ + initMaterial(&material); + + /* set new mtl name */ + token += 7; +#ifdef _MSC_VER + sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf)); +#else + sscanf(token, "%s", namebuf); +#endif + material.name = my_strdup(namebuf, (size_t) (line_end - token)); + + /* Add material to material table */ + if (material_table) + hash_table_set(material.name, num_materials, material_table); + + continue; + } + + /* ambient */ + if (token[0] == 'K' && token[1] == 'a' && IS_SPACE((token[2]))) { + float r, g, b; + token += 2; + parseFloat3(&r, &g, &b, &token); + material.ambient[0] = r; + material.ambient[1] = g; + material.ambient[2] = b; + continue; + } + + /* diffuse */ + if (token[0] == 'K' && token[1] == 'd' && IS_SPACE((token[2]))) { + float r, g, b; + token += 2; + parseFloat3(&r, &g, &b, &token); + material.diffuse[0] = r; + material.diffuse[1] = g; + material.diffuse[2] = b; + continue; + } + + /* specular */ + if (token[0] == 'K' && token[1] == 's' && IS_SPACE((token[2]))) { + float r, g, b; + token += 2; + parseFloat3(&r, &g, &b, &token); + material.specular[0] = r; + material.specular[1] = g; + material.specular[2] = b; + continue; + } + + /* transmittance */ + if (token[0] == 'K' && token[1] == 't' && IS_SPACE((token[2]))) { + float r, g, b; + token += 2; + parseFloat3(&r, &g, &b, &token); + material.transmittance[0] = r; + material.transmittance[1] = g; + material.transmittance[2] = b; + continue; + } + + /* ior(index of refraction) */ + if (token[0] == 'N' && token[1] == 'i' && IS_SPACE((token[2]))) { + token += 2; + material.ior = parseFloat(&token); + continue; + } + + /* emission */ + if (token[0] == 'K' && token[1] == 'e' && IS_SPACE(token[2])) { + float r, g, b; + token += 2; + parseFloat3(&r, &g, &b, &token); + material.emission[0] = r; + material.emission[1] = g; + material.emission[2] = b; + continue; + } + + /* shininess */ + if (token[0] == 'N' && token[1] == 's' && IS_SPACE(token[2])) { + token += 2; + material.shininess = parseFloat(&token); + continue; + } + + /* illum model */ + if (0 == strncmp(token, "illum", 5) && IS_SPACE(token[5])) { + token += 6; + material.illum = parseInt(&token); + continue; + } + + /* dissolve */ + if ((token[0] == 'd' && IS_SPACE(token[1]))) { + token += 1; + material.dissolve = parseFloat(&token); + continue; + } + if (token[0] == 'T' && token[1] == 'r' && IS_SPACE(token[2])) { + token += 2; + /* Invert value of Tr(assume Tr is in range [0, 1]) */ + material.dissolve = 1.0f - parseFloat(&token); + continue; + } + + /* ambient texture */ + if ((0 == strncmp(token, "map_Ka", 6)) && IS_SPACE(token[6])) { + token += 7; + material.ambient_texname = my_strdup(token, (size_t) (line_end - token)); + continue; + } + + /* diffuse texture */ + if ((0 == strncmp(token, "map_Kd", 6)) && IS_SPACE(token[6])) { + token += 7; + material.diffuse_texname = my_strdup(token, (size_t) (line_end - token)); + continue; + } + + /* specular texture */ + if ((0 == strncmp(token, "map_Ks", 6)) && IS_SPACE(token[6])) { + token += 7; + material.specular_texname = my_strdup(token, (size_t) (line_end - token)); + continue; + } + + /* specular highlight texture */ + if ((0 == strncmp(token, "map_Ns", 6)) && IS_SPACE(token[6])) { + token += 7; + material.specular_highlight_texname = my_strdup(token, (size_t) (line_end - token)); + continue; + } + + /* bump texture */ + if ((0 == strncmp(token, "map_bump", 8)) && IS_SPACE(token[8])) { + token += 9; + material.bump_texname = my_strdup(token, (size_t) (line_end - token)); + continue; + } + + /* alpha texture */ + if ((0 == strncmp(token, "map_d", 5)) && IS_SPACE(token[5])) { + token += 6; + material.alpha_texname = my_strdup(token, (size_t) (line_end - token)); + continue; + } + + /* bump texture */ + if ((0 == strncmp(token, "bump", 4)) && IS_SPACE(token[4])) { + token += 5; + material.bump_texname = my_strdup(token, (size_t) (line_end - token)); + continue; + } + + /* displacement texture */ + if ((0 == strncmp(token, "disp", 4)) && IS_SPACE(token[4])) { + token += 5; + material.displacement_texname = my_strdup(token, (size_t) (line_end - token)); + continue; + } + + /* @todo { unknown parameter } */ + } + + if (material.name) { + /* Flush last material element */ + materials = tinyobj_material_add(materials, num_materials, &material); + num_materials++; + } + + (*num_materials_out) = num_materials; + (*materials_out) = materials; + + if (linebuf) { + TINYOBJ_FREE(linebuf); + } + + return TINYOBJ_SUCCESS; +} + +int tinyobj_parse_mtl_file(tinyobj_material_t **materials_out, + size_t *num_materials_out, + const char *filename) { + return tinyobj_parse_and_index_mtl_file(materials_out, num_materials_out, filename, NULL); +} + + +typedef enum { + COMMAND_EMPTY, + COMMAND_V, + COMMAND_VN, + COMMAND_VT, + COMMAND_F, + COMMAND_G, + COMMAND_O, + COMMAND_USEMTL, + COMMAND_MTLLIB + +} CommandType; + +typedef struct { + float vx, vy, vz; + float nx, ny, nz; + float tx, ty; + + /* @todo { Use dynamic array } */ + tinyobj_vertex_index_t f[TINYOBJ_MAX_FACES_PER_F_LINE]; + size_t num_f; + + int f_num_verts[TINYOBJ_MAX_FACES_PER_F_LINE]; + size_t num_f_num_verts; + + const char *group_name; + unsigned int group_name_len; + int pad0; + + const char *object_name; + unsigned int object_name_len; + int pad1; + + const char *material_name; + unsigned int material_name_len; + int pad2; + + const char *mtllib_name; + unsigned int mtllib_name_len; + + CommandType type; +} Command; + +static int parseLine(Command *command, const char *p, size_t p_len, + int triangulate) { + char linebuf[4096]; + const char *token; + assert(p_len < 4095); + + memcpy(linebuf, p, p_len); + linebuf[p_len] = '\0'; + + token = linebuf; + + command->type = COMMAND_EMPTY; + + /* Skip leading space. */ + skip_space(&token); + + assert(token); + if (token[0] == '\0') { /* empty line */ + return 0; + } + + if (token[0] == '#') { /* comment line */ + return 0; + } + + /* vertex */ + if (token[0] == 'v' && IS_SPACE((token[1]))) { + float x, y, z; + token += 2; + parseFloat3(&x, &y, &z, &token); + command->vx = x; + command->vy = y; + command->vz = z; + command->type = COMMAND_V; + return 1; + } + + /* normal */ + if (token[0] == 'v' && token[1] == 'n' && IS_SPACE((token[2]))) { + float x, y, z; + token += 3; + parseFloat3(&x, &y, &z, &token); + command->nx = x; + command->ny = y; + command->nz = z; + command->type = COMMAND_VN; + return 1; + } + + /* texcoord */ + if (token[0] == 'v' && token[1] == 't' && IS_SPACE((token[2]))) { + float x, y; + token += 3; + parseFloat2(&x, &y, &token); + command->tx = x; + command->ty = y; + command->type = COMMAND_VT; + return 1; + } + + /* face */ + if (token[0] == 'f' && IS_SPACE((token[1]))) { + size_t num_f = 0; + + tinyobj_vertex_index_t f[TINYOBJ_MAX_FACES_PER_F_LINE]; + token += 2; + skip_space(&token); + + while (!IS_NEW_LINE(token[0])) { + tinyobj_vertex_index_t vi = parseRawTriple(&token); + skip_space_and_cr(&token); + + f[num_f] = vi; + num_f++; + } + + command->type = COMMAND_F; + + if (triangulate) { + size_t k; + size_t n = 0; + + tinyobj_vertex_index_t i0 = f[0]; + tinyobj_vertex_index_t i1; + tinyobj_vertex_index_t i2 = f[1]; + + assert(3 * num_f < TINYOBJ_MAX_FACES_PER_F_LINE); + + for (k = 2; k < num_f; k++) { + i1 = i2; + i2 = f[k]; + command->f[3 * n + 0] = i0; + command->f[3 * n + 1] = i1; + command->f[3 * n + 2] = i2; + + command->f_num_verts[n] = 3; + n++; + } + command->num_f = 3 * n; + command->num_f_num_verts = n; + + } else { + size_t k = 0; + assert(num_f < TINYOBJ_MAX_FACES_PER_F_LINE); + for (k = 0; k < num_f; k++) { + command->f[k] = f[k]; + } + + command->num_f = num_f; + command->f_num_verts[0] = (int)num_f; + command->num_f_num_verts = 1; + } + + return 1; + } + + /* use mtl */ + if ((0 == strncmp(token, "usemtl", 6)) && IS_SPACE((token[6]))) { + token += 7; + + skip_space(&token); + command->material_name = p + (token - linebuf); + command->material_name_len = (unsigned int)length_until_newline( + token, (p_len - (size_t)(token - linebuf)) + 1); + command->type = COMMAND_USEMTL; + + return 1; + } + + /* load mtl */ + if ((0 == strncmp(token, "mtllib", 6)) && IS_SPACE((token[6]))) { + /* By specification, `mtllib` should be appear only once in .obj */ + token += 7; + + skip_space(&token); + command->mtllib_name = p + (token - linebuf); + command->mtllib_name_len = (unsigned int)length_until_newline( + token, p_len - (size_t)(token - linebuf)) + + 1; + command->type = COMMAND_MTLLIB; + + return 1; + } + + /* group name */ + if (token[0] == 'g' && IS_SPACE((token[1]))) { + /* @todo { multiple group name. } */ + token += 2; + + command->group_name = p + (token - linebuf); + command->group_name_len = (unsigned int)length_until_newline( + token, p_len - (size_t)(token - linebuf)) + + 1; + command->type = COMMAND_G; + + return 1; + } + + /* object name */ + if (token[0] == 'o' && IS_SPACE((token[1]))) { + /* @todo { multiple object name? } */ + token += 2; + + command->object_name = p + (token - linebuf); + command->object_name_len = (unsigned int)length_until_newline( + token, p_len - (size_t)(token - linebuf)) + + 1; + command->type = COMMAND_O; + + return 1; + } + + return 0; +} + +typedef struct { + size_t pos; + size_t len; +} LineInfo; + +static int is_line_ending(const char *p, size_t i, size_t end_i) { + if (p[i] == '\0') return 1; + if (p[i] == '\n') return 1; /* this includes \r\n */ + if (p[i] == '\r') { + if (((i + 1) < end_i) && (p[i + 1] != '\n')) { /* detect only \r case */ + return 1; + } + } + return 0; +} + +int tinyobj_parse_obj(tinyobj_attrib_t *attrib, tinyobj_shape_t **shapes, + size_t *num_shapes, tinyobj_material_t **materials_out, + size_t *num_materials_out, const char *buf, size_t len, + unsigned int flags) { + LineInfo *line_infos = NULL; + Command *commands = NULL; + size_t num_lines = 0; + + size_t num_v = 0; + size_t num_vn = 0; + size_t num_vt = 0; + size_t num_f = 0; + size_t num_faces = 0; + + int mtllib_line_index = -1; + + tinyobj_material_t *materials = NULL; + size_t num_materials = 0; + + hash_table_t material_table; + + if (len < 1) return TINYOBJ_ERROR_INVALID_PARAMETER; + if (attrib == NULL) return TINYOBJ_ERROR_INVALID_PARAMETER; + if (shapes == NULL) return TINYOBJ_ERROR_INVALID_PARAMETER; + if (num_shapes == NULL) return TINYOBJ_ERROR_INVALID_PARAMETER; + if (buf == NULL) return TINYOBJ_ERROR_INVALID_PARAMETER; + if (materials_out == NULL) return TINYOBJ_ERROR_INVALID_PARAMETER; + if (num_materials_out == NULL) return TINYOBJ_ERROR_INVALID_PARAMETER; + + tinyobj_attrib_init(attrib); + /* 1. Find '\n' and create line data. */ + { + size_t i; + size_t end_idx = len; + size_t prev_pos = 0; + size_t line_no = 0; + size_t last_line_ending = 0; + + /* Count # of lines. */ + for (i = 0; i < end_idx; i++) { + if (is_line_ending(buf, i, end_idx)) { + num_lines++; + last_line_ending = i; + } + } + /* The last char from the input may not be a line + * ending character so add an extra line if there + * are more characters after the last line ending + * that was found. */ + if (end_idx - last_line_ending > 0) { + num_lines++; + } + + if (num_lines == 0) return TINYOBJ_ERROR_EMPTY; + + line_infos = (LineInfo *)TINYOBJ_MALLOC(sizeof(LineInfo) * num_lines); + + /* Fill line infos. */ + for (i = 0; i < end_idx; i++) { + if (is_line_ending(buf, i, end_idx)) { + line_infos[line_no].pos = prev_pos; + line_infos[line_no].len = i - prev_pos; + prev_pos = i + 1; + line_no++; + } + } + if (end_idx - last_line_ending > 0) { + line_infos[line_no].pos = prev_pos; + line_infos[line_no].len = end_idx - 1 - last_line_ending; + } + } + + commands = (Command *)TINYOBJ_MALLOC(sizeof(Command) * num_lines); + + create_hash_table(HASH_TABLE_DEFAULT_SIZE, &material_table); + + /* 2. parse each line */ + { + size_t i = 0; + for (i = 0; i < num_lines; i++) { + int ret = parseLine(&commands[i], &buf[line_infos[i].pos], + line_infos[i].len, flags & TINYOBJ_FLAG_TRIANGULATE); + if (ret) { + if (commands[i].type == COMMAND_V) { + num_v++; + } else if (commands[i].type == COMMAND_VN) { + num_vn++; + } else if (commands[i].type == COMMAND_VT) { + num_vt++; + } else if (commands[i].type == COMMAND_F) { + num_f += commands[i].num_f; + num_faces += commands[i].num_f_num_verts; + } + + if (commands[i].type == COMMAND_MTLLIB) { + mtllib_line_index = (int)i; + } + } + } + } + + /* line_infos are not used anymore. Release memory. */ + if (line_infos) { + TINYOBJ_FREE(line_infos); + } + + /* Load material(if exits) */ + if (mtllib_line_index >= 0 && commands[mtllib_line_index].mtllib_name && + commands[mtllib_line_index].mtllib_name_len > 0) { + char *filename = my_strndup(commands[mtllib_line_index].mtllib_name, + commands[mtllib_line_index].mtllib_name_len); + + int ret = tinyobj_parse_and_index_mtl_file(&materials, &num_materials, filename, &material_table); + + if (ret != TINYOBJ_SUCCESS) { + /* warning. */ + fprintf(stderr, "TINYOBJ: Failed to parse material file '%s': %d\n", filename, ret); + } + + TINYOBJ_FREE(filename); + + } + + /* Construct attributes */ + + { + size_t v_count = 0; + size_t n_count = 0; + size_t t_count = 0; + size_t f_count = 0; + size_t face_count = 0; + int material_id = -1; /* -1 = default unknown material. */ + size_t i = 0; + + attrib->vertices = (float *)TINYOBJ_MALLOC(sizeof(float) * num_v * 3); + attrib->num_vertices = (unsigned int)num_v; + attrib->normals = (float *)TINYOBJ_MALLOC(sizeof(float) * num_vn * 3); + attrib->num_normals = (unsigned int)num_vn; + attrib->texcoords = (float *)TINYOBJ_MALLOC(sizeof(float) * num_vt * 2); + attrib->num_texcoords = (unsigned int)num_vt; + attrib->faces = (tinyobj_vertex_index_t *)TINYOBJ_MALLOC( + sizeof(tinyobj_vertex_index_t) * num_f); + attrib->num_faces = (unsigned int)num_f; + attrib->face_num_verts = (int *)TINYOBJ_MALLOC(sizeof(int) * num_faces); + attrib->material_ids = (int *)TINYOBJ_MALLOC(sizeof(int) * num_faces); + attrib->num_face_num_verts = (unsigned int)num_faces; + + for (i = 0; i < num_lines; i++) { + if (commands[i].type == COMMAND_EMPTY) { + continue; + } else if (commands[i].type == COMMAND_USEMTL) { + /* @todo + if (commands[t][i].material_name && + commands[t][i].material_name_len > 0) { + std::string material_name(commands[t][i].material_name, + commands[t][i].material_name_len); + + if (material_map.find(material_name) != material_map.end()) { + material_id = material_map[material_name]; + } else { + // Assign invalid material ID + material_id = -1; + } + } + */ + if (commands[i].material_name && + commands[i].material_name_len >0) + { + /* Create a null terminated string */ + char* material_name_null_term = (char*) TINYOBJ_MALLOC(commands[i].material_name_len + 1); + memcpy((void*) material_name_null_term, (const void*) commands[i].material_name, commands[i].material_name_len); + material_name_null_term[commands[i].material_name_len - 1] = 0; + + if (hash_table_exists(material_name_null_term, &material_table)) + material_id = (int)hash_table_get(material_name_null_term, &material_table); + else + material_id = -1; + + TINYOBJ_FREE(material_name_null_term); + } + } else if (commands[i].type == COMMAND_V) { + attrib->vertices[3 * v_count + 0] = commands[i].vx; + attrib->vertices[3 * v_count + 1] = commands[i].vy; + attrib->vertices[3 * v_count + 2] = commands[i].vz; + v_count++; + } else if (commands[i].type == COMMAND_VN) { + attrib->normals[3 * n_count + 0] = commands[i].nx; + attrib->normals[3 * n_count + 1] = commands[i].ny; + attrib->normals[3 * n_count + 2] = commands[i].nz; + n_count++; + } else if (commands[i].type == COMMAND_VT) { + attrib->texcoords[2 * t_count + 0] = commands[i].tx; + attrib->texcoords[2 * t_count + 1] = commands[i].ty; + t_count++; + } else if (commands[i].type == COMMAND_F) { + size_t k = 0; + for (k = 0; k < commands[i].num_f; k++) { + tinyobj_vertex_index_t vi = commands[i].f[k]; + int v_idx = fixIndex(vi.v_idx, v_count); + int vn_idx = fixIndex(vi.vn_idx, n_count); + int vt_idx = fixIndex(vi.vt_idx, t_count); + attrib->faces[f_count + k].v_idx = v_idx; + attrib->faces[f_count + k].vn_idx = vn_idx; + attrib->faces[f_count + k].vt_idx = vt_idx; + } + + for (k = 0; k < commands[i].num_f_num_verts; k++) { + attrib->material_ids[face_count + k] = material_id; + attrib->face_num_verts[face_count + k] = commands[i].f_num_verts[k]; + } + + f_count += commands[i].num_f; + face_count += commands[i].num_f_num_verts; + } + } + } + + /* 5. Construct shape information. */ + { + unsigned int face_count = 0; + size_t i = 0; + size_t n = 0; + size_t shape_idx = 0; + + const char *shape_name = NULL; + unsigned int shape_name_len = 0; + const char *prev_shape_name = NULL; + unsigned int prev_shape_name_len = 0; + unsigned int prev_shape_face_offset = 0; + unsigned int prev_face_offset = 0; + tinyobj_shape_t prev_shape = {NULL, 0, 0}; + + /* Find the number of shapes in .obj */ + for (i = 0; i < num_lines; i++) { + if (commands[i].type == COMMAND_O || commands[i].type == COMMAND_G) { + n++; + } + } + + /* Allocate array of shapes with maximum possible size(+1 for unnamed + * group/object). + * Actual # of shapes found in .obj is determined in the later */ + (*shapes) = (tinyobj_shape_t*)TINYOBJ_MALLOC(sizeof(tinyobj_shape_t) * (n + 1)); + + for (i = 0; i < num_lines; i++) { + if (commands[i].type == COMMAND_O || commands[i].type == COMMAND_G) { + if (commands[i].type == COMMAND_O) { + shape_name = commands[i].object_name; + shape_name_len = commands[i].object_name_len; + } else { + shape_name = commands[i].group_name; + shape_name_len = commands[i].group_name_len; + } + + if (face_count == 0) { + /* 'o' or 'g' appears before any 'f' */ + prev_shape_name = shape_name; + prev_shape_name_len = shape_name_len; + prev_shape_face_offset = face_count; + prev_face_offset = face_count; + } else { + if (shape_idx == 0) { + /* 'o' or 'g' after some 'v' lines. */ + (*shapes)[shape_idx].name = my_strndup( + prev_shape_name, prev_shape_name_len); /* may be NULL */ + (*shapes)[shape_idx].face_offset = prev_shape.face_offset; + (*shapes)[shape_idx].length = face_count - prev_face_offset; + shape_idx++; + + prev_face_offset = face_count; + + } else { + if ((face_count - prev_face_offset) > 0) { + (*shapes)[shape_idx].name = + my_strndup(prev_shape_name, prev_shape_name_len); + (*shapes)[shape_idx].face_offset = prev_face_offset; + (*shapes)[shape_idx].length = face_count - prev_face_offset; + shape_idx++; + prev_face_offset = face_count; + } + } + + /* Record shape info for succeeding 'o' or 'g' command. */ + prev_shape_name = shape_name; + prev_shape_name_len = shape_name_len; + prev_shape_face_offset = face_count; + } + } + if (commands[i].type == COMMAND_F) { + face_count++; + } + } + + if ((face_count - prev_face_offset) > 0) { + size_t length = face_count - prev_shape_face_offset; + if (length > 0) { + (*shapes)[shape_idx].name = + my_strndup(prev_shape_name, prev_shape_name_len); + (*shapes)[shape_idx].face_offset = prev_face_offset; + (*shapes)[shape_idx].length = face_count - prev_face_offset; + shape_idx++; + } + } else { + /* Guess no 'v' line occurrence after 'o' or 'g', so discards current + * shape information. */ + } + + (*num_shapes) = shape_idx; + } + + if (commands) { + TINYOBJ_FREE(commands); + } + + destroy_hash_table(&material_table); + + (*materials_out) = materials; + (*num_materials_out) = num_materials; + + return TINYOBJ_SUCCESS; +} + +void tinyobj_attrib_init(tinyobj_attrib_t *attrib) { + attrib->vertices = NULL; + attrib->num_vertices = 0; + attrib->normals = NULL; + attrib->num_normals = 0; + attrib->texcoords = NULL; + attrib->num_texcoords = 0; + attrib->faces = NULL; + attrib->num_faces = 0; + attrib->face_num_verts = NULL; + attrib->num_face_num_verts = 0; + attrib->material_ids = NULL; +} + +void tinyobj_attrib_free(tinyobj_attrib_t *attrib) { + if (attrib->vertices) TINYOBJ_FREE(attrib->vertices); + if (attrib->normals) TINYOBJ_FREE(attrib->normals); + if (attrib->texcoords) TINYOBJ_FREE(attrib->texcoords); + if (attrib->faces) TINYOBJ_FREE(attrib->faces); + if (attrib->face_num_verts) TINYOBJ_FREE(attrib->face_num_verts); + if (attrib->material_ids) TINYOBJ_FREE(attrib->material_ids); +} + +void tinyobj_shapes_free(tinyobj_shape_t *shapes, size_t num_shapes) { + size_t i; + if (shapes == NULL) return; + + for (i = 0; i < num_shapes; i++) { + if (shapes[i].name) TINYOBJ_FREE(shapes[i].name); + } + + TINYOBJ_FREE(shapes); +} + +void tinyobj_materials_free(tinyobj_material_t *materials, + size_t num_materials) { + size_t i; + if (materials == NULL) return; + + for (i = 0; i < num_materials; i++) { + if (materials[i].name) TINYOBJ_FREE(materials[i].name); + if (materials[i].ambient_texname) TINYOBJ_FREE(materials[i].ambient_texname); + if (materials[i].diffuse_texname) TINYOBJ_FREE(materials[i].diffuse_texname); + if (materials[i].specular_texname) TINYOBJ_FREE(materials[i].specular_texname); + if (materials[i].specular_highlight_texname) + TINYOBJ_FREE(materials[i].specular_highlight_texname); + if (materials[i].bump_texname) TINYOBJ_FREE(materials[i].bump_texname); + if (materials[i].displacement_texname) + TINYOBJ_FREE(materials[i].displacement_texname); + if (materials[i].alpha_texname) TINYOBJ_FREE(materials[i].alpha_texname); + } + + TINYOBJ_FREE(materials); +} +#endif /* TINYOBJ_LOADER_C_IMPLEMENTATION */ + +#endif /* TINOBJ_LOADER_C_H_ */ diff --git a/src/models.c b/src/models.c index a5dbf5e7c..c9d92315a 100644 --- a/src/models.c +++ b/src/models.c @@ -52,6 +52,11 @@ #include "rlgl.h" // raylib OpenGL abstraction layer to OpenGL 1.1, 2.1, 3.3+ or ES2 +#if defined(SUPPORT_FILEFORMAT_OBJ) + #define TINYOBJ_LOADER_C_IMPLEMENTATION + #include "external/tinyobj_loader_c.h" // OBJ file format loading +#endif + #if defined(SUPPORT_FILEFORMAT_IQM) #define RIQM_IMPLEMENTATION #include "external/riqm.h" // IQM file format loading @@ -86,16 +91,16 @@ // Module specific Functions Declaration //---------------------------------------------------------------------------------- #if defined(SUPPORT_FILEFORMAT_OBJ) -static Mesh LoadOBJ(const char *fileName); // Load OBJ mesh data +static Model LoadOBJ(const char *fileName); // Load OBJ mesh data #endif #if defined(SUPPORT_FILEFORMAT_MTL) static Material LoadMTL(const char *fileName); // Load MTL material data #endif #if defined(SUPPORT_FILEFORMAT_GLTF) -static Mesh LoadIQM(const char *fileName); // Load IQM mesh data +static Model LoadIQM(const char *fileName); // Load IQM mesh data #endif #if defined(SUPPORT_FILEFORMAT_GLTF) -static Mesh LoadGLTF(const char *fileName); // Load GLTF mesh data +static Model LoadGLTF(const char *fileName); // Load GLTF mesh data #endif //---------------------------------------------------------------------------------- @@ -618,9 +623,18 @@ Model LoadModel(const char *fileName) { Model model = { 0 }; - model.mesh = LoadMesh(fileName); - model.transform = MatrixIdentity(); - model.material = LoadMaterialDefault(); +#if defined(SUPPORT_FILEFORMAT_OBJ) + if (IsFileExtension(fileName, ".obj")) model = LoadOBJ(fileName); +#endif +#if defined(SUPPORT_FILEFORMAT_GLTF) + if (IsFileExtension(fileName, ".gltf")) model = LoadGLTF(fileName); +#endif +#if defined(SUPPORT_FILEFORMAT_IQM) + if (IsFileExtension(fileName, ".iqm")) model = LoadIQM(fileName); +#endif + + if (model.meshCount == 0) TraceLog(LOG_WARNING, "[%s] No meshes can be loaded", fileName); + if (model.materialCount == 0) TraceLog(LOG_WARNING, "[%s] No materials can be loaded", fileName); return model; } @@ -633,9 +647,18 @@ Model LoadModelFromMesh(Mesh mesh) { Model model = { 0 }; - model.mesh = mesh; model.transform = MatrixIdentity(); - model.material = LoadMaterialDefault(); + + model.meshCount = 1; + model.meshes = (Mesh *)malloc(model.meshCount*sizeof(Mesh)); + model.meshes[0] = mesh; + + model.materialCount = 1; + model.materials = (Material *)malloc(model.materialCount*sizeof(Material)); + model.materials[0] = LoadMaterialDefault(); + + model.meshMaterial = (int *)malloc(model.meshCount*sizeof(int)); + model.meshMaterial[0] = 0; // First material index return model; } @@ -643,10 +666,11 @@ Model LoadModelFromMesh(Mesh mesh) // Unload model from memory (RAM and/or VRAM) void UnloadModel(Model model) { - UnloadMesh(&model.mesh); - UnloadMaterial(model.material); + for (int i = 0; i < model.meshCount; i++) UnloadMesh(&model.meshes[i]); + for (int i = 0; i < model.materialCount; i++) UnloadMaterial(model.materials[i]); + free(model.meshMaterial); - TraceLog(LOG_INFO, "Unloaded model data (mesh and material) from RAM and VRAM"); + TraceLog(LOG_INFO, "Unloaded model data from RAM and VRAM"); } // Load mesh from file @@ -655,12 +679,8 @@ Mesh LoadMesh(const char *fileName) { Mesh mesh = { 0 }; -#if defined(SUPPORT_FILEFORMAT_OBJ) - if (IsFileExtension(fileName, ".obj")) mesh = LoadOBJ(fileName); -#else - TraceLog(LOG_WARNING, "[%s] Mesh fileformat not supported, it can't be loaded", fileName); -#endif - + // TODO: Review this function, should still exist? + #if defined(SUPPORT_MESH_GENERATION) if (mesh.vertexCount == 0) { @@ -1855,9 +1875,12 @@ void DrawModelEx(Model model, Vector3 position, Vector3 rotationAxis, float rota //Matrix matModel = MatrixMultiply(model.transform, matTransform); // Transform to world-space coordinates model.transform = MatrixMultiply(model.transform, matTransform); - model.material.maps[MAP_DIFFUSE].color = tint; // TODO: Multiply tint color by diffuse color? - rlDrawMesh(model.mesh, model.material, model.transform); + for (int i = 0; i < model.meshCount; i++) + { + model.materials[model.meshMaterial[i]].maps[MAP_DIFFUSE].color = tint; + rlDrawMesh(model.meshes[i], model.materials[model.meshMaterial[i]], model.transform); + } } // Draw a model wires (with texture if set) @@ -2079,41 +2102,45 @@ RayHitInfo GetCollisionRayModel(Ray ray, Model *model) { RayHitInfo result = { 0 }; - // If mesh doesn't have vertex data on CPU, can't test it. - if (!model->mesh.vertices) return result; - - // model->mesh.triangleCount may not be set, vertexCount is more reliable - int triangleCount = model->mesh.vertexCount/3; - - // Test against all triangles in mesh - for (int i = 0; i < triangleCount; i++) + for (int i = 0; i < model->meshCount; i++) { - Vector3 a, b, c; - Vector3 *vertdata = (Vector3 *)model->mesh.vertices; - - if (model->mesh.indices) + // Check if meshhas vertex data on CPU for testing + if (model->meshes[i].vertices != NULL) { - a = vertdata[model->mesh.indices[i*3 + 0]]; - b = vertdata[model->mesh.indices[i*3 + 1]]; - c = vertdata[model->mesh.indices[i*3 + 2]]; - } - else - { - a = vertdata[i*3 + 0]; - b = vertdata[i*3 + 1]; - c = vertdata[i*3 + 2]; - } + // model->mesh.triangleCount may not be set, vertexCount is more reliable + int triangleCount = model->meshes[i].vertexCount/3; - a = Vector3Transform(a, model->transform); - b = Vector3Transform(b, model->transform); - c = Vector3Transform(c, model->transform); + // Test against all triangles in mesh + for (int i = 0; i < triangleCount; i++) + { + Vector3 a, b, c; + Vector3 *vertdata = (Vector3 *)model->meshes[i].vertices; - RayHitInfo triHitInfo = GetCollisionRayTriangle(ray, a, b, c); + if (model->meshes[i].indices) + { + a = vertdata[model->meshes[i].indices[i*3 + 0]]; + b = vertdata[model->meshes[i].indices[i*3 + 1]]; + c = vertdata[model->meshes[i].indices[i*3 + 2]]; + } + else + { + a = vertdata[i*3 + 0]; + b = vertdata[i*3 + 1]; + c = vertdata[i*3 + 2]; + } - if (triHitInfo.hit) - { - // Save the closest hit triangle - if ((!result.hit) || (result.distance > triHitInfo.distance)) result = triHitInfo; + a = Vector3Transform(a, model->transform); + b = Vector3Transform(b, model->transform); + c = Vector3Transform(c, model->transform); + + RayHitInfo triHitInfo = GetCollisionRayTriangle(ray, a, b, c); + + if (triHitInfo.hit) + { + // Save the closest hit triangle + if ((!result.hit) || (result.distance > triHitInfo.distance)) result = triHitInfo; + } + } } } @@ -2329,10 +2356,13 @@ void MeshBinormals(Mesh *mesh) #if defined(SUPPORT_FILEFORMAT_OBJ) // Load OBJ mesh data -static Mesh LoadOBJ(const char *fileName) +static Model LoadOBJ(const char *fileName) { - Mesh mesh = { 0 }; + Model model = { 0 }; + // TODO: Use tinyobj_loader_c library + +/* char dataType = 0; char comments[200]; @@ -2568,11 +2598,12 @@ static Mesh LoadOBJ(const char *fileName) free(midVertices); free(midNormals); free(midTexCoords); +*/ // NOTE: At this point we have all vertex, texcoord, normal data for the model in mesh struct TraceLog(LOG_INFO, "[%s] Model loaded successfully in RAM (CPU)", fileName); - return mesh; + return model; } #endif @@ -2743,21 +2774,21 @@ static Material LoadMTL(const char *fileName) #if defined(SUPPORT_FILEFORMAT_GLTF) // Load IQM mesh data -static Mesh LoadIQM(const char *fileName) +static Model LoadIQM(const char *fileName) { - Mesh mesh = { 0 }; + Model model = { 0 }; // TODO: Load IQM file - return mesh; + return model; } #endif #if defined(SUPPORT_FILEFORMAT_GLTF) // Load glTF mesh data -static Mesh LoadGLTF(const char *fileName) +static Model LoadGLTF(const char *fileName) { - Mesh mesh = { 0 }; + Model model = { 0 }; // glTF file loading FILE *gltfFile = fopen(fileName, "rb"); @@ -2765,7 +2796,7 @@ static Mesh LoadGLTF(const char *fileName) if (gltfFile == NULL) { TraceLog(LOG_WARNING, "[%s] glTF file could not be opened", fileName); - return mesh; + return model; } fseek(gltfFile, 0, SEEK_END); @@ -2790,15 +2821,15 @@ static Mesh LoadGLTF(const char *fileName) printf("Version: %d\n", data.version); printf("Meshes: %lu\n", data.meshes_count); - // TODO: Process glTF data and map to mesh + // TODO: Process glTF data and map to model - // NOTE: data.buffers[] and data.images[] should be loaded - // using buffers[n].uri and images[n].uri... or use cgltf_load_buffers(&options, data, fileName); + // NOTE: data.buffers[] should be loaded to model.meshes and data.images[] should be loaded to model.materials + // Use buffers[n].uri and images[n].uri... or use cgltf_load_buffers(&options, data, fileName); cgltf_free(&data); } else TraceLog(LOG_WARNING, "[%s] glTF data could not be loaded", fileName); - return mesh; + return model; } #endif diff --git a/src/raylib.h b/src/raylib.h index 5db50c040..1a344306a 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -339,18 +339,15 @@ typedef struct Material { // Model type typedef struct Model { - Mesh mesh; // Vertex data buffers (RAM and VRAM) Matrix transform; // Local transform matrix - Material material; // Shader and textures data - /* - Mesh *meshes; // Vertex data buffers (RAM and VRAM) - int meshCount; + + int meshCount; // Number of meshes + Mesh *meshes; // Meshes array - Material *materials; // Shader and textures data - int materialCount; - - int *meshMaterial; // Material assigned to every mesh - */ + int materialCount; // Number of materials + Material *materials; // Materials array + + int *meshMaterial; // Mesh material number } Model; // Ray type (useful for raycast) @@ -1226,7 +1223,7 @@ RLAPI void DrawGizmo(Vector3 position); //------------------------------------------------------------------------------------ // Model loading/unloading functions -RLAPI Model LoadModel(const char *fileName); // Load model from files (mesh and material) +RLAPI Model LoadModel(const char *fileName); // Load model from files (meshes and materials) RLAPI Model LoadModelFromMesh(Mesh mesh); // Load model from generated mesh RLAPI void UnloadModel(Model model); // Unload model from memory (RAM and/or VRAM) From 8a73c5d0b403264ebd66cb5a2e1b21f91f6991b0 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 29 Mar 2019 17:15:22 +0100 Subject: [PATCH 15/74] Replace custom OBJ/MTL implementations by tinyobj_loader -WIP- --- src/models.c | 495 +++++++++------------------------------------------ 1 file changed, 89 insertions(+), 406 deletions(-) diff --git a/src/models.c b/src/models.c index c9d92315a..ec2713eda 100644 --- a/src/models.c +++ b/src/models.c @@ -52,9 +52,9 @@ #include "rlgl.h" // raylib OpenGL abstraction layer to OpenGL 1.1, 2.1, 3.3+ or ES2 -#if defined(SUPPORT_FILEFORMAT_OBJ) +#if defined(SUPPORT_FILEFORMAT_OBJ) || defined(SUPPORT_FILEFORMAT_MTL) #define TINYOBJ_LOADER_C_IMPLEMENTATION - #include "external/tinyobj_loader_c.h" // OBJ file format loading + #include "external/tinyobj_loader_c.h" // OBJ/MTL file formats loading #endif #if defined(SUPPORT_FILEFORMAT_IQM) @@ -93,9 +93,6 @@ #if defined(SUPPORT_FILEFORMAT_OBJ) static Model LoadOBJ(const char *fileName); // Load OBJ mesh data #endif -#if defined(SUPPORT_FILEFORMAT_MTL) -static Material LoadMTL(const char *fileName); // Load MTL material data -#endif #if defined(SUPPORT_FILEFORMAT_GLTF) static Model LoadIQM(const char *fileName); // Load IQM mesh data #endif @@ -1811,7 +1808,17 @@ Material LoadMaterial(const char *fileName) Material material = { 0 }; #if defined(SUPPORT_FILEFORMAT_MTL) - if (IsFileExtension(fileName, ".mtl")) material = LoadMTL(fileName); + if (IsFileExtension(fileName, ".mtl")) + { + tinyobj_material_t *materials; + int materialCount = 0; + + int result = tinyobj_parse_mtl_file(&materials, &materialCount, fileName); + + // TODO: Process materials to return + + tinyobj_materials_free(materials, materialCount); + } #else TraceLog(LOG_WARNING, "[%s] Material fileformat not supported, it can't be loaded", fileName); #endif @@ -2359,419 +2366,95 @@ void MeshBinormals(Mesh *mesh) static Model LoadOBJ(const char *fileName) { Model model = { 0 }; - - // TODO: Use tinyobj_loader_c library -/* - char dataType = 0; - char comments[200]; + tinyobj_attrib_t attrib; + tinyobj_shape_t *meshes = NULL; + int meshCount = 0; + + tinyobj_material_t *materials = NULL; + int materialCount = 0; - int vertexCount = 0; - int normalCount = 0; - int texcoordCount = 0; - int triangleCount = 0; - - FILE *objFile; - - objFile = fopen(fileName, "rt"); - - if (objFile == NULL) + int dataLength = 0; + const char *data = get_file_data(&dataLength, fileName); + + if (data != NULL) { - TraceLog(LOG_WARNING, "[%s] OBJ file could not be opened", fileName); - return mesh; - } - - // First reading pass: Get vertexCount, normalCount, texcoordCount, triangleCount - // NOTE: vertex, texcoords and normals could be optimized (to be used indexed on faces definition) - // NOTE: faces MUST be defined as TRIANGLES (3 vertex per face) - while (!feof(objFile)) - { - dataType = 0; - fscanf(objFile, "%c", &dataType); - - switch (dataType) + unsigned int flags = TINYOBJ_FLAG_TRIANGULATE; + int ret = tinyobj_parse_obj(&attrib, &meshes, &meshCount, &materials, &materialCount, data, dataLength, flags); + + if (ret != TINYOBJ_SUCCESS) TraceLog(LOG_WARNING, "[%s] Model data could not be loaded", fileName); + else TraceLog(LOG_INFO, "[%s] Model data loaded successfully: %i meshes / %i materials", fileName, meshCount, materialCount); + + for (int i = 0; i < meshCount; i++) { - case '#': // Comments - case 'o': // Object name (One OBJ file can contain multible named meshes) - case 'g': // Group name - case 's': // Smoothing level - case 'm': // mtllib [external .mtl file name] - case 'u': // usemtl [material name] - { - fgets(comments, 200, objFile); - } break; - case 'v': - { - fscanf(objFile, "%c", &dataType); - - if (dataType == 't') // Read texCoord - { - texcoordCount++; - fgets(comments, 200, objFile); - } - else if (dataType == 'n') // Read normals - { - normalCount++; - fgets(comments, 200, objFile); - } - else // Read vertex - { - vertexCount++; - fgets(comments, 200, objFile); - } - } break; - case 'f': - { - triangleCount++; - fgets(comments, 200, objFile); - } break; - default: break; + printf("shape[%d] name = %s\n", i, meshes[i].name); } + + /* + // Data reference to process + typedef struct { + char *name; + + float ambient[3]; + float diffuse[3]; + float specular[3]; + float transmittance[3]; + float emission[3]; + float shininess; + float ior; // index of refraction + float dissolve; // 1 == opaque; 0 == fully transparent + // illumination model (see http://www.fileformat.info/format/material/) + int illum; + + int pad0; + + char *ambient_texname; // map_Ka + char *diffuse_texname; // map_Kd + char *specular_texname; // map_Ks + char *specular_highlight_texname; // map_Ns + char *bump_texname; // map_bump, bump + char *displacement_texname; // disp + char *alpha_texname; // map_d + } tinyobj_material_t; + + typedef struct { + char *name; // group name or object name + unsigned int face_offset; + unsigned int length; + } tinyobj_shape_t; + + typedef struct { int v_idx, vt_idx, vn_idx; } tinyobj_vertex_index_t; + + typedef struct { + unsigned int num_vertices; + unsigned int num_normals; + unsigned int num_texcoords; + unsigned int num_faces; + unsigned int num_face_num_verts; + + int pad0; + + float *vertices; + float *normals; + float *texcoords; + tinyobj_vertex_index_t *faces; + int *face_num_verts; + int *material_ids; + } tinyobj_attrib_t; + */ + + tinyobj_attrib_free(&attrib); + tinyobj_shapes_free(meshes, meshCount); + tinyobj_materials_free(materials, materialCount); } - TraceLog(LOG_DEBUG, "[%s] Model vertices: %i", fileName, vertexCount); - TraceLog(LOG_DEBUG, "[%s] Model texcoords: %i", fileName, texcoordCount); - TraceLog(LOG_DEBUG, "[%s] Model normals: %i", fileName, normalCount); - TraceLog(LOG_DEBUG, "[%s] Model triangles: %i", fileName, triangleCount); - - // Once we know the number of vertices to store, we create required arrays - Vector3 *midVertices = (Vector3 *)malloc(vertexCount*sizeof(Vector3)); - Vector3 *midNormals = NULL; - if (normalCount > 0) midNormals = (Vector3 *)malloc(normalCount*sizeof(Vector3)); - Vector2 *midTexCoords = NULL; - if (texcoordCount > 0) midTexCoords = (Vector2 *)malloc(texcoordCount*sizeof(Vector2)); - - int countVertex = 0; - int countNormals = 0; - int countTexCoords = 0; - - rewind(objFile); // Return to the beginning of the file, to read again - - // Second reading pass: Get vertex data to fill intermediate arrays - // NOTE: This second pass is required in case of multiple meshes defined in same OBJ - // TODO: Consider that different meshes can have different vertex data available (position, texcoords, normals) - while (!feof(objFile)) - { - fscanf(objFile, "%c", &dataType); - - switch (dataType) - { - case '#': case 'o': case 'g': case 's': case 'm': case 'u': case 'f': fgets(comments, 200, objFile); break; - case 'v': - { - fscanf(objFile, "%c", &dataType); - - if (dataType == 't') // Read texCoord - { - fscanf(objFile, "%f %f%*[^\n]s\n", &midTexCoords[countTexCoords].x, &midTexCoords[countTexCoords].y); - countTexCoords++; - - fscanf(objFile, "%c", &dataType); - } - else if (dataType == 'n') // Read normals - { - fscanf(objFile, "%f %f %f", &midNormals[countNormals].x, &midNormals[countNormals].y, &midNormals[countNormals].z); - countNormals++; - - fscanf(objFile, "%c", &dataType); - } - else // Read vertex - { - fscanf(objFile, "%f %f %f", &midVertices[countVertex].x, &midVertices[countVertex].y, &midVertices[countVertex].z); - countVertex++; - - fscanf(objFile, "%c", &dataType); - } - } break; - default: break; - } - } - - // At this point all vertex data (v, vt, vn) has been gathered on midVertices, midTexCoords, midNormals - // Now we can organize that data into our Mesh struct - - mesh.vertexCount = triangleCount*3; - - // Additional arrays to store vertex data as floats - mesh.vertices = (float *)malloc(mesh.vertexCount*3*sizeof(float)); - mesh.texcoords = (float *)malloc(mesh.vertexCount*2*sizeof(float)); - mesh.normals = (float *)malloc(mesh.vertexCount*3*sizeof(float)); - mesh.colors = NULL; - - int vCounter = 0; // Used to count vertices float by float - int tcCounter = 0; // Used to count texcoords float by float - int nCounter = 0; // Used to count normals float by float - - int vCount[3], vtCount[3], vnCount[3]; // Used to store triangle indices for v, vt, vn - - rewind(objFile); // Return to the beginning of the file, to read again - - if (normalCount == 0) TraceLog(LOG_INFO, "[%s] No normals data on OBJ, normals will be generated from faces data", fileName); - - // Third reading pass: Get faces (triangles) data and fill VertexArray - while (!feof(objFile)) - { - fscanf(objFile, "%c", &dataType); - - switch (dataType) - { - case '#': case 'o': case 'g': case 's': case 'm': case 'u': case 'v': fgets(comments, 200, objFile); break; - case 'f': - { - // NOTE: It could be that OBJ does not have normals or texcoords defined! - - if ((normalCount == 0) && (texcoordCount == 0)) fscanf(objFile, "%i %i %i", &vCount[0], &vCount[1], &vCount[2]); - else if (normalCount == 0) fscanf(objFile, "%i/%i %i/%i %i/%i", &vCount[0], &vtCount[0], &vCount[1], &vtCount[1], &vCount[2], &vtCount[2]); - else if (texcoordCount == 0) fscanf(objFile, "%i//%i %i//%i %i//%i", &vCount[0], &vnCount[0], &vCount[1], &vnCount[1], &vCount[2], &vnCount[2]); - else fscanf(objFile, "%i/%i/%i %i/%i/%i %i/%i/%i", &vCount[0], &vtCount[0], &vnCount[0], &vCount[1], &vtCount[1], &vnCount[1], &vCount[2], &vtCount[2], &vnCount[2]); - - mesh.vertices[vCounter] = midVertices[vCount[0]-1].x; - mesh.vertices[vCounter + 1] = midVertices[vCount[0]-1].y; - mesh.vertices[vCounter + 2] = midVertices[vCount[0]-1].z; - vCounter += 3; - mesh.vertices[vCounter] = midVertices[vCount[1]-1].x; - mesh.vertices[vCounter + 1] = midVertices[vCount[1]-1].y; - mesh.vertices[vCounter + 2] = midVertices[vCount[1]-1].z; - vCounter += 3; - mesh.vertices[vCounter] = midVertices[vCount[2]-1].x; - mesh.vertices[vCounter + 1] = midVertices[vCount[2]-1].y; - mesh.vertices[vCounter + 2] = midVertices[vCount[2]-1].z; - vCounter += 3; - - if (normalCount > 0) - { - mesh.normals[nCounter] = midNormals[vnCount[0]-1].x; - mesh.normals[nCounter + 1] = midNormals[vnCount[0]-1].y; - mesh.normals[nCounter + 2] = midNormals[vnCount[0]-1].z; - nCounter += 3; - mesh.normals[nCounter] = midNormals[vnCount[1]-1].x; - mesh.normals[nCounter + 1] = midNormals[vnCount[1]-1].y; - mesh.normals[nCounter + 2] = midNormals[vnCount[1]-1].z; - nCounter += 3; - mesh.normals[nCounter] = midNormals[vnCount[2]-1].x; - mesh.normals[nCounter + 1] = midNormals[vnCount[2]-1].y; - mesh.normals[nCounter + 2] = midNormals[vnCount[2]-1].z; - nCounter += 3; - } - else - { - // If normals not defined, they are calculated from the 3 vertices [N = (V2 - V1) x (V3 - V1)] - Vector3 norm = Vector3CrossProduct(Vector3Subtract(midVertices[vCount[1]-1], midVertices[vCount[0]-1]), Vector3Subtract(midVertices[vCount[2]-1], midVertices[vCount[0]-1])); - norm = Vector3Normalize(norm); - - mesh.normals[nCounter] = norm.x; - mesh.normals[nCounter + 1] = norm.y; - mesh.normals[nCounter + 2] = norm.z; - nCounter += 3; - mesh.normals[nCounter] = norm.x; - mesh.normals[nCounter + 1] = norm.y; - mesh.normals[nCounter + 2] = norm.z; - nCounter += 3; - mesh.normals[nCounter] = norm.x; - mesh.normals[nCounter + 1] = norm.y; - mesh.normals[nCounter + 2] = norm.z; - nCounter += 3; - } - - if (texcoordCount > 0) - { - // NOTE: If using negative texture coordinates with a texture filter of GL_CLAMP_TO_EDGE doesn't work! - // NOTE: Texture coordinates are Y flipped upside-down - mesh.texcoords[tcCounter] = midTexCoords[vtCount[0]-1].x; - mesh.texcoords[tcCounter + 1] = 1.0f - midTexCoords[vtCount[0]-1].y; - tcCounter += 2; - mesh.texcoords[tcCounter] = midTexCoords[vtCount[1]-1].x; - mesh.texcoords[tcCounter + 1] = 1.0f - midTexCoords[vtCount[1]-1].y; - tcCounter += 2; - mesh.texcoords[tcCounter] = midTexCoords[vtCount[2]-1].x; - mesh.texcoords[tcCounter + 1] = 1.0f - midTexCoords[vtCount[2]-1].y; - tcCounter += 2; - } - } break; - default: break; - } - } - - fclose(objFile); - - // Now we can free temp mid* arrays - free(midVertices); - free(midNormals); - free(midTexCoords); -*/ - - // NOTE: At this point we have all vertex, texcoord, normal data for the model in mesh struct + // NOTE: At this point we have all model data loaded TraceLog(LOG_INFO, "[%s] Model loaded successfully in RAM (CPU)", fileName); return model; } #endif -#if defined(SUPPORT_FILEFORMAT_MTL) -// Load MTL material data (specs: http://paulbourke.net/dataformats/mtl/) -// NOTE: Texture map parameters are not supported -static Material LoadMTL(const char *fileName) -{ - #define MAX_BUFFER_SIZE 128 - - Material material = { 0 }; - - char buffer[MAX_BUFFER_SIZE]; - Vector3 color = { 1.0f, 1.0f, 1.0f }; - char mapFileName[128]; - int result = 0; - - FILE *mtlFile; - - mtlFile = fopen(fileName, "rt"); - - if (mtlFile == NULL) - { - TraceLog(LOG_WARNING, "[%s] MTL file could not be opened", fileName); - return material; - } - - while (!feof(mtlFile)) - { - fgets(buffer, MAX_BUFFER_SIZE, mtlFile); - - switch (buffer[0]) - { - case 'n': // newmtl string Material name. Begins a new material description. - { - // TODO: Support multiple materials in a single .mtl - sscanf(buffer, "newmtl %127s", mapFileName); - } - case 'i': // illum int Illumination model - { - // illum = 1 if specular disabled - // illum = 2 if specular enabled (lambertian model) - // ... - } - case 'K': // Ka, Kd, Ks, Ke - { - switch (buffer[1]) - { - case 'a': // Ka float float float Ambient color (RGB) - { - sscanf(buffer, "Ka %f %f %f", &color.x, &color.y, &color.z); - // TODO: Support ambient color - //material.colAmbient.r = (unsigned char)(color.x*255); - //material.colAmbient.g = (unsigned char)(color.y*255); - //material.colAmbient.b = (unsigned char)(color.z*255); - } break; - case 'd': // Kd float float float Diffuse color (RGB) - { - sscanf(buffer, "Kd %f %f %f", &color.x, &color.y, &color.z); - material.maps[MAP_DIFFUSE].color.r = (unsigned char)(color.x*255); - material.maps[MAP_DIFFUSE].color.g = (unsigned char)(color.y*255); - material.maps[MAP_DIFFUSE].color.b = (unsigned char)(color.z*255); - } break; - case 's': // Ks float float float Specular color (RGB) - { - sscanf(buffer, "Ks %f %f %f", &color.x, &color.y, &color.z); - material.maps[MAP_SPECULAR].color.r = (unsigned char)(color.x*255); - material.maps[MAP_SPECULAR].color.g = (unsigned char)(color.y*255); - material.maps[MAP_SPECULAR].color.b = (unsigned char)(color.z*255); - } break; - case 'e': // Ke float float float Emmisive color (RGB) - { - // TODO: Support Ke? - } break; - default: break; - } - } break; - case 'N': // Ns, Ni - { - if (buffer[1] == 's') // Ns int Shininess (specular exponent). Ranges from 0 to 1000. - { - int shininess = 0; - sscanf(buffer, "Ns %i", &shininess); - - //material.params[PARAM_GLOSSINES] = (float)shininess; - } - else if (buffer[1] == 'i') // Ni int Refraction index. - { - // Not supported... - } - } break; - case 'm': // map_Kd, map_Ks, map_Ka, map_Bump, map_d - { - switch (buffer[4]) - { - case 'K': // Color texture maps - { - if (buffer[5] == 'd') // map_Kd string Diffuse color texture map. - { - result = sscanf(buffer, "map_Kd %127s", mapFileName); - if (result != EOF) material.maps[MAP_DIFFUSE].texture = LoadTexture(mapFileName); - } - else if (buffer[5] == 's') // map_Ks string Specular color texture map. - { - result = sscanf(buffer, "map_Ks %127s", mapFileName); - if (result != EOF) material.maps[MAP_SPECULAR].texture = LoadTexture(mapFileName); - } - else if (buffer[5] == 'a') // map_Ka string Ambient color texture map. - { - // Not supported... - } - } break; - case 'B': // map_Bump string Bump texture map. - { - result = sscanf(buffer, "map_Bump %127s", mapFileName); - if (result != EOF) material.maps[MAP_NORMAL].texture = LoadTexture(mapFileName); - } break; - case 'b': // map_bump string Bump texture map. - { - result = sscanf(buffer, "map_bump %127s", mapFileName); - if (result != EOF) material.maps[MAP_NORMAL].texture = LoadTexture(mapFileName); - } break; - case 'd': // map_d string Opacity texture map. - { - // Not supported... - } break; - default: break; - } - } break; - case 'd': // d, disp - { - if (buffer[1] == ' ') // d float Dissolve factor. d is inverse of Tr - { - float alpha = 1.0f; - sscanf(buffer, "d %f", &alpha); - material.maps[MAP_DIFFUSE].color.a = (unsigned char)(alpha*255); - } - else if (buffer[1] == 'i') // disp string Displacement map - { - // Not supported... - } - } break; - case 'b': // bump string Bump texture map - { - result = sscanf(buffer, "bump %127s", mapFileName); - if (result != EOF) material.maps[MAP_NORMAL].texture = LoadTexture(mapFileName); - } break; - case 'T': // Tr float Transparency Tr (alpha). Tr is inverse of d - { - float ialpha = 0.0f; - sscanf(buffer, "Tr %f", &ialpha); - material.maps[MAP_DIFFUSE].color.a = (unsigned char)((1.0f - ialpha)*255); - - } break; - case 'r': // refl string Reflection texture map - default: break; - } - } - - fclose(mtlFile); - - // NOTE: At this point we have all material data - TraceLog(LOG_INFO, "[%s] Material loaded successfully", fileName); - - return material; -} -#endif - #if defined(SUPPORT_FILEFORMAT_GLTF) // Load IQM mesh data static Model LoadIQM(const char *fileName) From 19debd2b4e44cee494c882cb8d338dc1847ae5b5 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 29 Mar 2019 17:28:10 +0100 Subject: [PATCH 16/74] Review some warnings --- src/models.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/models.c b/src/models.c index ec2713eda..6f3fa73fa 100644 --- a/src/models.c +++ b/src/models.c @@ -1811,7 +1811,7 @@ Material LoadMaterial(const char *fileName) if (IsFileExtension(fileName, ".mtl")) { tinyobj_material_t *materials; - int materialCount = 0; + unsigned int materialCount = 0; int result = tinyobj_parse_mtl_file(&materials, &materialCount, fileName); @@ -2369,14 +2369,29 @@ static Model LoadOBJ(const char *fileName) tinyobj_attrib_t attrib; tinyobj_shape_t *meshes = NULL; - int meshCount = 0; + unsigned int meshCount = 0; tinyobj_material_t *materials = NULL; - int materialCount = 0; + unsigned int materialCount = 0; int dataLength = 0; - const char *data = get_file_data(&dataLength, fileName); + char *data = NULL; + // Load model data + FILE *objFile = fopen(fileName, "rb"); + + if (objFile != NULL) + { + fseek(objFile, 0, SEEK_END); + long dataLength = ftell(objFile); // Get file size + fseek(objFile, 0, SEEK_SET); // Reset file pointer + + data = (char *)malloc(dataLength); + + fread(data, dataLength, 1, objFile); + fclose(objFile); + } + if (data != NULL) { unsigned int flags = TINYOBJ_FLAG_TRIANGULATE; From 6f371dab0807ccc81038b3eccfb9c6090ec07d16 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 29 Mar 2019 19:43:27 +0100 Subject: [PATCH 17/74] Some formatting review --- src/shapes.c | 42 +++++++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/src/shapes.c b/src/shapes.c index 7e8c0f4b3..8c1eb1f01 100644 --- a/src/shapes.c +++ b/src/shapes.c @@ -185,8 +185,8 @@ void DrawCircle(int centerX, int centerY, float radius, Color color) // Draw a piece of a circle void DrawCircleSector(Vector2 center, float radius, int startAngle, int endAngle, int segments, Color color) { - if(radius == 0) return; // Check this or we'll get a div by zero error otherwise - + if (radius <= 0.0f) radius = 0.1f; // Avoid div by zero + // Function expects (endAngle > startAngle) if (endAngle < startAngle) { @@ -277,7 +277,7 @@ void DrawCircleSector(Vector2 center, float radius, int startAngle, int endAngle void DrawCircleSectorLines(Vector2 center, float radius, int startAngle, int endAngle, int segments, Color color) { - if(radius == 0) return; // Check this or we'll get a div by zero error otherwise + if (radius <= 0.0f) radius = 0.1f; // Avoid div by zero issue // Function expects (endAngle > startAngle) if (endAngle < startAngle) @@ -308,12 +308,12 @@ void DrawCircleSectorLines(Vector2 center, float radius, int startAngle, int end // Hide the cap lines when the circle is full bool showCapLines = true; int limit = 2*(segments + 2); - if((endAngle - startAngle) % 360 == 0) { limit = 2*segments; showCapLines = false; } + if ((endAngle - startAngle)%360 == 0) { limit = 2*segments; showCapLines = false; } if (rlCheckBufferLimit(limit)) rlglDraw(); rlBegin(RL_LINES); - if(showCapLines) + if (showCapLines) { rlColor4ub(color.r, color.g, color.b, color.a); rlVertex2f(center.x, center.y); @@ -330,7 +330,7 @@ void DrawCircleSectorLines(Vector2 center, float radius, int startAngle, int end angle += stepLength; } - if(showCapLines) + if (showCapLines) { rlColor4ub(color.r, color.g, color.b, color.a); rlVertex2f(center.x, center.y); @@ -384,15 +384,16 @@ void DrawCircleLines(int centerX, int centerY, float radius, Color color) void DrawRing(Vector2 center, float innerRadius, float outerRadius, int startAngle, int endAngle, int segments, Color color) { - if(startAngle == endAngle) return; + if (startAngle == endAngle) return; // Function expects (outerRadius > innerRadius) - if(outerRadius < innerRadius) + if (outerRadius < innerRadius) { float tmp = outerRadius; outerRadius = innerRadius; innerRadius = tmp; - if(outerRadius == 0) return; // Check this or we'll get a div by zero error otherwise + + if (outerRadius <= 0.0f) outerRadius = 0.1f; } // Function expects (endAngle > startAngle) @@ -408,8 +409,9 @@ void DrawRing(Vector2 center, float innerRadius, float outerRadius, int startAng { // Calculate how many segments we need to draw a smooth circle, taken from https://stackoverflow.com/a/2244088 #ifndef CIRCLE_ERROR_RATE - #define CIRCLE_ERROR_RATE 0.5f + #define CIRCLE_ERROR_RATE 0.5f #endif + // Calculate the maximum angle between segments based on the error rate. float th = acosf(2*powf(1 - CIRCLE_ERROR_RATE/outerRadius, 2) - 1); segments = (endAngle - startAngle)*ceilf(2*PI/th)/360; @@ -418,7 +420,7 @@ void DrawRing(Vector2 center, float innerRadius, float outerRadius, int startAng } // Not a ring - if(innerRadius == 0) + if (innerRadius <= 0.0f) { DrawCircleSector(center, outerRadius, startAngle, endAngle, segments, color); return; @@ -478,15 +480,16 @@ void DrawRing(Vector2 center, float innerRadius, float outerRadius, int startAng void DrawRingLines(Vector2 center, float innerRadius, float outerRadius, int startAngle, int endAngle, int segments, Color color) { - if(startAngle == endAngle) return; + if (startAngle == endAngle) return; // Function expects (outerRadius > innerRadius) - if(outerRadius < innerRadius) + if (outerRadius < innerRadius) { float tmp = outerRadius; outerRadius = innerRadius; innerRadius = tmp; - if(outerRadius == 0) return; // Check this or we'll get a div by zero error otherwise + + if (outerRadius <= 0.0f) outerRadius = 0.1f; } // Function expects (endAngle > startAngle) @@ -502,8 +505,9 @@ void DrawRingLines(Vector2 center, float innerRadius, float outerRadius, int sta { // Calculate how many segments we need to draw a smooth circle, taken from https://stackoverflow.com/a/2244088 #ifndef CIRCLE_ERROR_RATE - #define CIRCLE_ERROR_RATE 0.5f + #define CIRCLE_ERROR_RATE 0.5f #endif + // Calculate the maximum angle between segments based on the error rate. float th = acosf(2*powf(1 - CIRCLE_ERROR_RATE/outerRadius, 2) - 1); segments = (endAngle - startAngle)*ceilf(2*PI/th)/360; @@ -511,7 +515,7 @@ void DrawRingLines(Vector2 center, float innerRadius, float outerRadius, int sta if (segments <= 0) segments = 4; } - if(innerRadius == 0) + if (innerRadius <= 0.0f) { DrawCircleSectorLines(center, outerRadius, startAngle, endAngle, segments, color); return; @@ -522,12 +526,12 @@ void DrawRingLines(Vector2 center, float innerRadius, float outerRadius, int sta bool showCapLines = true; int limit = 4*(segments + 1); - if((endAngle - startAngle) % 360 == 0) { limit = 4*segments; showCapLines = false; } + if ((endAngle - startAngle)%360 == 0) { limit = 4*segments; showCapLines = false; } if (rlCheckBufferLimit(limit)) rlglDraw(); rlBegin(RL_LINES); - if(showCapLines) + if (showCapLines) { rlColor4ub(color.r, color.g, color.b, color.a); rlVertex2f(center.x + sinf(DEG2RAD*angle)*outerRadius, center.y + cosf(DEG2RAD*angle)*outerRadius); @@ -547,7 +551,7 @@ void DrawRingLines(Vector2 center, float innerRadius, float outerRadius, int sta angle += stepLength; } - if(showCapLines) + if (showCapLines) { rlColor4ub(color.r, color.g, color.b, color.a); rlVertex2f(center.x + sinf(DEG2RAD*angle)*outerRadius, center.y + cosf(DEG2RAD*angle)*outerRadius); From a197f40bb4bd5a644ad54bef756d7f435977df9d Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 29 Mar 2019 20:22:30 +0100 Subject: [PATCH 18/74] Default to white cube mesh if not loaded --- src/models.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/models.c b/src/models.c index 6f3fa73fa..2893fd2f5 100644 --- a/src/models.c +++ b/src/models.c @@ -630,8 +630,25 @@ Model LoadModel(const char *fileName) if (IsFileExtension(fileName, ".iqm")) model = LoadIQM(fileName); #endif - if (model.meshCount == 0) TraceLog(LOG_WARNING, "[%s] No meshes can be loaded", fileName); - if (model.materialCount == 0) TraceLog(LOG_WARNING, "[%s] No materials can be loaded", fileName); + if (model.meshCount == 0) + { + TraceLog(LOG_WARNING, "[%s] No meshes can be loaded, default to cube mesh", fileName); + + model.meshCount = 1; + model.meshes = (Mesh *)malloc(model.meshCount*sizeof(Mesh)); + model.meshes[0] = GenMeshCube(1.0f, 1.0f, 1.0f); + } + + if (model.materialCount == 0) + { + TraceLog(LOG_WARNING, "[%s] No materials can be loaded, default to white material", fileName); + + model.materialCount = 1; + model.materials = (Material *)malloc(model.materialCount*sizeof(Material)); + model.materials[0] = LoadMaterialDefault(); + + model.meshMaterial = (int *)calloc(model.meshCount, sizeof(int)); + } return model; } From be6d237b9ebbe245de4384c17b84e75dab0f4981 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 29 Mar 2019 20:22:50 +0100 Subject: [PATCH 19/74] Review models examples --- examples/models/models_cubicmap.c | 2 +- examples/models/models_heightmap.c | 2 +- examples/models/models_material_pbr.c | 14 +++++++------- examples/models/models_mesh_generation.c | 13 ++++++++++++- examples/models/models_mesh_picking.c | 6 +++--- examples/models/models_obj_loading.c | 2 +- examples/models/models_obj_viewer.c | 18 +++++++++--------- examples/models/models_skybox.c | 6 +++--- examples/models/models_yaw_pitch_roll.c | 6 +++--- examples/shaders/shaders_custom_uniform.c | 2 +- examples/shaders/shaders_model_shader.c | 4 ++-- examples/shaders/shaders_postprocessing.c | 2 +- 12 files changed, 44 insertions(+), 33 deletions(-) diff --git a/examples/models/models_cubicmap.c b/examples/models/models_cubicmap.c index c8d62c46f..ac24188e9 100644 --- a/examples/models/models_cubicmap.c +++ b/examples/models/models_cubicmap.c @@ -31,7 +31,7 @@ int main() // NOTE: By default each cube is mapped to one part of texture atlas Texture2D texture = LoadTexture("resources/cubicmap_atlas.png"); // Load map texture - model.material.maps[MAP_DIFFUSE].texture = texture; // Set map diffuse texture + model.materials[0].maps[MAP_DIFFUSE].texture = texture; // Set map diffuse texture Vector3 mapPosition = { -16.0f, 0.0f, -8.0f }; // Set model position diff --git a/examples/models/models_heightmap.c b/examples/models/models_heightmap.c index d131b127d..e0475f182 100644 --- a/examples/models/models_heightmap.c +++ b/examples/models/models_heightmap.c @@ -29,7 +29,7 @@ int main() Mesh mesh = GenMeshHeightmap(image, (Vector3){ 16, 8, 16 }); // Generate heightmap mesh (RAM and VRAM) Model model = LoadModelFromMesh(mesh); // Load model from generated mesh - model.material.maps[MAP_DIFFUSE].texture = texture; // Set map diffuse texture + model.materials[0].maps[MAP_DIFFUSE].texture = texture; // Set map diffuse texture Vector3 mapPosition = { -8.0f, 0.0f, -8.0f }; // Define model position UnloadImage(image); // Unload heightmap image from RAM, already uploaded to VRAM diff --git a/examples/models/models_material_pbr.c b/examples/models/models_material_pbr.c index f93c7a687..adb4762b5 100644 --- a/examples/models/models_material_pbr.c +++ b/examples/models/models_material_pbr.c @@ -38,16 +38,16 @@ int main() // Load model and PBR material Model model = LoadModel("resources/pbr/trooper.obj"); - MeshTangents(&model.mesh); - model.material = LoadMaterialPBR((Color){ 255, 255, 255, 255 }, 1.0f, 1.0f); + MeshTangents(&model.meshes[0]); + model.materials[0] = LoadMaterialPBR((Color){ 255, 255, 255, 255 }, 1.0f, 1.0f); // Define lights attributes // NOTE: Shader is passed to every light on creation to define shader bindings internally Light lights[MAX_LIGHTS] = { - CreateLight(LIGHT_POINT, (Vector3){ LIGHT_DISTANCE, LIGHT_HEIGHT, 0.0f }, (Vector3){ 0.0f, 0.0f, 0.0f }, (Color){ 255, 0, 0, 255 }, model.material.shader), - CreateLight(LIGHT_POINT, (Vector3){ 0.0f, LIGHT_HEIGHT, LIGHT_DISTANCE }, (Vector3){ 0.0f, 0.0f, 0.0f }, (Color){ 0, 255, 0, 255 }, model.material.shader), - CreateLight(LIGHT_POINT, (Vector3){ -LIGHT_DISTANCE, LIGHT_HEIGHT, 0.0f }, (Vector3){ 0.0f, 0.0f, 0.0f }, (Color){ 0, 0, 255, 255 }, model.material.shader), - CreateLight(LIGHT_DIRECTIONAL, (Vector3){ 0.0f, LIGHT_HEIGHT*2.0f, -LIGHT_DISTANCE }, (Vector3){ 0.0f, 0.0f, 0.0f }, (Color){ 255, 0, 255, 255 }, model.material.shader) + CreateLight(LIGHT_POINT, (Vector3){ LIGHT_DISTANCE, LIGHT_HEIGHT, 0.0f }, (Vector3){ 0.0f, 0.0f, 0.0f }, (Color){ 255, 0, 0, 255 }, model.materials[0].shader), + CreateLight(LIGHT_POINT, (Vector3){ 0.0f, LIGHT_HEIGHT, LIGHT_DISTANCE }, (Vector3){ 0.0f, 0.0f, 0.0f }, (Color){ 0, 255, 0, 255 }, model.materials[0].shader), + CreateLight(LIGHT_POINT, (Vector3){ -LIGHT_DISTANCE, LIGHT_HEIGHT, 0.0f }, (Vector3){ 0.0f, 0.0f, 0.0f }, (Color){ 0, 0, 255, 255 }, model.materials[0].shader), + CreateLight(LIGHT_DIRECTIONAL, (Vector3){ 0.0f, LIGHT_HEIGHT*2.0f, -LIGHT_DISTANCE }, (Vector3){ 0.0f, 0.0f, 0.0f }, (Color){ 255, 0, 255, 255 }, model.materials[0].shader) }; SetCameraMode(camera, CAMERA_ORBITAL); // Set an orbital camera mode @@ -64,7 +64,7 @@ int main() // Send to material PBR shader camera view position float cameraPos[3] = { camera.position.x, camera.position.y, camera.position.z }; - SetShaderValue(model.material.shader, model.material.shader.locs[LOC_VECTOR_VIEW], cameraPos, UNIFORM_VEC3); + SetShaderValue(model.materials[0].shader, model.materials[0].shader.locs[LOC_VECTOR_VIEW], cameraPos, UNIFORM_VEC3); //---------------------------------------------------------------------------------- // Draw diff --git a/examples/models/models_mesh_generation.c b/examples/models/models_mesh_generation.c index d64889bdd..2b4b75abe 100644 --- a/examples/models/models_mesh_generation.c +++ b/examples/models/models_mesh_generation.c @@ -39,7 +39,7 @@ int main() models[7] = LoadModelFromMesh(GenMeshPoly(5, 2.0f)); // Set checked texture as default diffuse component for all models material - for (int i = 0; i < NUM_MODELS; i++) models[i].material.maps[MAP_DIFFUSE].texture = texture; + for (int i = 0; i < NUM_MODELS; i++) models[i].materials[0].maps[MAP_DIFFUSE].texture = texture; // Define the camera to look into our 3d world Camera camera = {{ 5.0f, 5.0f, 5.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 1.0f, 0.0f }, 45.0f, 0 }; @@ -65,6 +65,17 @@ int main() { currentModel = (currentModel + 1)%NUM_MODELS; // Cycle between the textures } + + if (IsKeyPressed(KEY_RIGHT)) + { + currentModel++; + if (currentModel >= NUM_MODELS) currentModel = 0; + } + else if (IsKeyPressed(KEY_LEFT)) + { + currentModel--; + if (currentModel < 0) currentModel = NUM_MODELS - 1; + } //---------------------------------------------------------------------------------- // Draw diff --git a/examples/models/models_mesh_picking.c b/examples/models/models_mesh_picking.c index 9b12e98c6..420288290 100644 --- a/examples/models/models_mesh_picking.c +++ b/examples/models/models_mesh_picking.c @@ -26,7 +26,7 @@ int main() // Define the camera to look into our 3d world Camera camera = { 0 }; - camera.position = (Vector3){ 20.0f, 20.0f, 20.0f }; // Camera position + camera.position = (Vector3){ 20.0f, 20.0f, 20.0f }; // Camera position camera.target = (Vector3){ 0.0f, 8.0f, 0.0f }; // Camera looking at point camera.up = (Vector3){ 0.0f, 1.6f, 0.0f }; // Camera up vector (rotation towards target) camera.fovy = 45.0f; // Camera field-of-view Y @@ -36,10 +36,10 @@ int main() Model tower = LoadModel("resources/models/turret.obj"); // Load OBJ model Texture2D texture = LoadTexture("resources/models/turret_diffuse.png"); // Load model texture - tower.material.maps[MAP_DIFFUSE].texture = texture; // Set model diffuse texture + tower.materials[0].maps[MAP_DIFFUSE].texture = texture; // Set model diffuse texture Vector3 towerPos = { 0.0f, 0.0f, 0.0f }; // Set model position - BoundingBox towerBBox = MeshBoundingBox(tower.mesh); // Get mesh bounding box + BoundingBox towerBBox = MeshBoundingBox(tower.meshes[0]); // Get mesh bounding box bool hitMeshBBox = false; bool hitTriangle = false; diff --git a/examples/models/models_obj_loading.c b/examples/models/models_obj_loading.c index 7ec2d3f05..0251fd3ee 100644 --- a/examples/models/models_obj_loading.c +++ b/examples/models/models_obj_loading.c @@ -30,7 +30,7 @@ int main() Model model = LoadModel("resources/models/castle.obj"); // Load OBJ model Texture2D texture = LoadTexture("resources/models/castle_diffuse.png"); // Load model texture - model.material.maps[MAP_DIFFUSE].texture = texture; // Set map diffuse texture + model.materials[0].maps[MAP_DIFFUSE].texture = texture; // Set map diffuse texture Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model position SetTargetFPS(60); // Set our game to run at 60 frames-per-second diff --git a/examples/models/models_obj_viewer.c b/examples/models/models_obj_viewer.c index 15f795497..ffa609e08 100644 --- a/examples/models/models_obj_viewer.c +++ b/examples/models/models_obj_viewer.c @@ -27,13 +27,13 @@ int main() Model model = LoadModel("resources/models/turret.obj"); // Load default model obj Texture2D texture = LoadTexture("resources/models/turret_diffuse.png"); // Load default model texture - model.material.maps[MAP_DIFFUSE].texture = texture; // Bind texture to model + model.materials[0].maps[MAP_DIFFUSE].texture = texture; // Bind texture to model - Vector3 position = { 0.0, 0.0, 0.0 }; // Set model position - BoundingBox bounds = MeshBoundingBox(model.mesh); // Set model bounds - bool selected = false; // Selected object flag + Vector3 position = { 0.0, 0.0, 0.0 }; // Set model position + BoundingBox bounds = MeshBoundingBox(model.meshes[0]); // Set model bounds + bool selected = false; // Selected object flag - SetCameraMode(camera, CAMERA_FREE); // Set a free camera mode + SetCameraMode(camera, CAMERA_FREE); // Set a free camera mode char objFilename[64] = "turret.obj"; @@ -54,15 +54,15 @@ int main() { if (IsFileExtension(droppedFiles[0], ".obj")) { - UnloadMesh(&model.mesh); - model.mesh = LoadMesh(droppedFiles[0]); - bounds = MeshBoundingBox(model.mesh); + UnloadMesh(&model.meshes[0]); + model.meshes[0] = LoadMesh(droppedFiles[0]); + bounds = MeshBoundingBox(model.meshes[0]); } else if (IsFileExtension(droppedFiles[0], ".png")) { UnloadTexture(texture); texture = LoadTexture(droppedFiles[0]); - model.material.maps[MAP_DIFFUSE].texture = texture; + model.materials[0].maps[MAP_DIFFUSE].texture = texture; } strcpy(objFilename, GetFileName(droppedFiles[0])); diff --git a/examples/models/models_skybox.c b/examples/models/models_skybox.c index c7f76ecf1..759c79c62 100644 --- a/examples/models/models_skybox.c +++ b/examples/models/models_skybox.c @@ -29,8 +29,8 @@ int main() // Load skybox shader and set required locations // NOTE: Some locations are automatically set at shader loading - skybox.material.shader = LoadShader("resources/shaders/skybox.vs", "resources/shaders/skybox.fs"); - SetShaderValue(skybox.material.shader, GetShaderLocation(skybox.material.shader, "environmentMap"), (int[1]){ MAP_CUBEMAP }, UNIFORM_INT); + skybox.materials[0].shader = LoadShader("resources/shaders/skybox.vs", "resources/shaders/skybox.fs"); + SetShaderValue(skybox.materials[0].shader, GetShaderLocation(skybox.materials[0].shader, "environmentMap"), (int[1]){ MAP_CUBEMAP }, UNIFORM_INT); // Load cubemap shader and setup required shader locations Shader shdrCubemap = LoadShader("resources/shaders/cubemap.vs", "resources/shaders/cubemap.fs"); @@ -41,7 +41,7 @@ int main() // Generate cubemap (texture with 6 quads-cube-mapping) from panorama HDR texture // NOTE: New texture is generated rendering to texture, shader computes the sphre->cube coordinates mapping - skybox.material.maps[MAP_CUBEMAP].texture = GenTextureCubemap(shdrCubemap, texHDR, 512); + skybox.materials[0].maps[MAP_CUBEMAP].texture = GenTextureCubemap(shdrCubemap, texHDR, 512); UnloadTexture(texHDR); // Texture not required anymore, cubemap already generated UnloadShader(shdrCubemap); // Unload cubemap generation shader, not required anymore diff --git a/examples/models/models_yaw_pitch_roll.c b/examples/models/models_yaw_pitch_roll.c index 88b0a6109..7eaaf0bea 100644 --- a/examples/models/models_yaw_pitch_roll.c +++ b/examples/models/models_yaw_pitch_roll.c @@ -37,10 +37,10 @@ int main() RenderTexture2D framebuffer = LoadRenderTexture(192, 192); // Model loading - Model model = LoadModel("resources/plane.obj"); // Load OBJ model - model.material.maps[MAP_DIFFUSE].texture = LoadTexture("resources/plane_diffuse.png"); // Set map diffuse texture + Model model = LoadModel("resources/plane.obj"); // Load OBJ model + model.materials[0].maps[MAP_DIFFUSE].texture = LoadTexture("resources/plane_diffuse.png"); // Set map diffuse texture - GenTextureMipmaps(&model.material.maps[MAP_DIFFUSE].texture); + GenTextureMipmaps(&model.materials[0].maps[MAP_DIFFUSE].texture); Camera camera = { 0 }; camera.position = (Vector3){ 0.0f, 60.0f, -120.0f };// Camera position perspective diff --git a/examples/shaders/shaders_custom_uniform.c b/examples/shaders/shaders_custom_uniform.c index fbfd82d07..74b9e7714 100644 --- a/examples/shaders/shaders_custom_uniform.c +++ b/examples/shaders/shaders_custom_uniform.c @@ -45,7 +45,7 @@ int main() Model model = LoadModel("resources/models/barracks.obj"); // Load OBJ model Texture2D texture = LoadTexture("resources/models/barracks_diffuse.png"); // Load model texture (diffuse map) - model.material.maps[MAP_DIFFUSE].texture = texture; // Set model diffuse texture + model.materials[0].maps[MAP_DIFFUSE].texture = texture; // Set model diffuse texture Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model position diff --git a/examples/shaders/shaders_model_shader.c b/examples/shaders/shaders_model_shader.c index 6c64f0efd..2717c1927 100644 --- a/examples/shaders/shaders_model_shader.c +++ b/examples/shaders/shaders_model_shader.c @@ -50,8 +50,8 @@ int main() // NOTE: Defining 0 (NULL) for vertex shader forces usage of internal default vertex shader Shader shader = LoadShader(0, FormatText("resources/shaders/glsl%i/grayscale.fs", GLSL_VERSION)); - model.material.shader = shader; // Set shader effect to 3d model - model.material.maps[MAP_DIFFUSE].texture = texture; // Bind texture to model + model.materials[0].shader = shader; // Set shader effect to 3d model + model.materials[0].maps[MAP_DIFFUSE].texture = texture; // Bind texture to model Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model position diff --git a/examples/shaders/shaders_postprocessing.c b/examples/shaders/shaders_postprocessing.c index f84835636..7c1464193 100644 --- a/examples/shaders/shaders_postprocessing.c +++ b/examples/shaders/shaders_postprocessing.c @@ -74,7 +74,7 @@ int main() Model model = LoadModel("resources/models/church.obj"); // Load OBJ model Texture2D texture = LoadTexture("resources/models/church_diffuse.png"); // Load model texture (diffuse map) - model.material.maps[MAP_DIFFUSE].texture = texture; // Set model diffuse texture + model.materials[0].maps[MAP_DIFFUSE].texture = texture; // Set model diffuse texture Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model position From 2217c04ecf426cc2011c864fa788354ffa1de307 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 29 Mar 2019 20:54:45 +0100 Subject: [PATCH 20/74] Update standard_lighting.c --- examples/others/standard_lighting.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/others/standard_lighting.c b/examples/others/standard_lighting.c index 7034aa35b..c37ae1512 100644 --- a/examples/others/standard_lighting.c +++ b/examples/others/standard_lighting.c @@ -113,7 +113,7 @@ int main() material.maps[MAP_DIFFUSE].color = WHITE; material.maps[MAP_SPECULAR].color = WHITE; - model.material = material; // Apply material to model + model.materials[0] = material; // Apply material to model Light spotLight = CreateLight(LIGHT_SPOT, (Vector3){3.0f, 5.0f, 2.0f}, (Color){255, 255, 255, 255}); spotLight->target = (Vector3){0.0f, 0.0f, 0.0f}; From a28023b58f172559228a07da29d8cac417d2e6bd Mon Sep 17 00:00:00 2001 From: Demizdor Date: Sat, 30 Mar 2019 22:18:29 +0200 Subject: [PATCH 21/74] Added DrawRoundedRect() --- src/raylib.h | 1 + src/shapes.c | 194 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 195 insertions(+) diff --git a/src/raylib.h b/src/raylib.h index 085f36f63..0a8351c09 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1056,6 +1056,7 @@ RLAPI void DrawRectangleGradientH(int posX, int posY, int width, int height, Col RLAPI void DrawRectangleGradientEx(Rectangle rec, Color col1, Color col2, Color col3, Color col4); // Draw a gradient-filled rectangle with custom vertex colors RLAPI void DrawRectangleLines(int posX, int posY, int width, int height, Color color); // Draw rectangle outline RLAPI void DrawRectangleLinesEx(Rectangle rec, int lineThick, Color color); // Draw rectangle outline with extended parameters +RLAPI void DrawRoundedRect(Rectangle rec, float roundness, int segments, Color color); // Draw rectangle with rounded edges RLAPI void DrawTriangle(Vector2 v1, Vector2 v2, Vector2 v3, Color color); // Draw a color-filled triangle RLAPI void DrawTriangleLines(Vector2 v1, Vector2 v2, Vector2 v3, Color color); // Draw triangle outline RLAPI void DrawPoly(Vector2 center, int sides, float radius, float rotation, Color color); // Draw a regular polygon (Vector version) diff --git a/src/shapes.c b/src/shapes.c index 8c1eb1f01..9eb690d3f 100644 --- a/src/shapes.c +++ b/src/shapes.c @@ -698,6 +698,200 @@ void DrawRectangleLinesEx(Rectangle rec, int lineThick, Color color) DrawRectangle( (int)rec.x, (int)(rec.y + lineThick), lineThick, (int)(rec.height - lineThick*2), color); } +// Draw rectangle with rounded edges. +void DrawRoundedRect(Rectangle rec, float roundness, int segments, Color color) +{ + // Not a rounded rectangle + // NOTE: Make sure we have at least 1px space left to render the rectangles near the corners + if(roundness <= 0.0f || rec.width <= 1 || rec.height <= 1 ) + { + DrawRectangleRec(rec, color); + return; + } + + if(roundness >= 1.0f) roundness = 1.0f; + + // Calculate corner radius + // NOTE: Make sure we have at least 1px space left to render the rectangles near the corners + float radius = rec.width > rec.height ? ((rec.height-1)*roundness)/2 : ((rec.width-1)*roundness)/2; + if(radius <= 0.0f) return; + + // Calculate number of segments to use for the corners + if (segments < 4) + { + // Calculate how many segments we need to draw a smooth circle, taken from https://stackoverflow.com/a/2244088 + #ifndef CIRCLE_ERROR_RATE + #define CIRCLE_ERROR_RATE 0.5f + #endif + // Calculate the maximum angle between segments based on the error rate. + float th = acosf(2*powf(1 - CIRCLE_ERROR_RATE/radius, 2) - 1); + segments = ceilf(2*PI/th)/4; + if (segments <= 0) segments = 4; + } + + float stepLength = 90.0f/(float)segments; + + /* Quick sketch to make sense of all of this (there are 9 parts to draw, also mark the 12 points we'll use below) + * Not my best attempt at ASCII art, just preted it's a rounded rectangle :) + * P0 P1 + * ____________________ + * /| |\ + * /1| 2 |3\ + *P7 /__|____________________|__\ P2 + * | |P8 P9| | + * | 8 | 9 | 4 | + * | __|____________________|__ | + *P6 \ |P11 P10| / P3 + * \7| 6 |5/ + * \|____________________|/ + * P5 P4 + */ + + const Vector2 point[12] = { // coordinates of the 12 points that define the rounded rect (the idea here is to make things easier) + {(float)rec.x + radius, rec.y}, {(float)(rec.x + rec.width) - radius, rec.y}, { rec.x + rec.width, (float)rec.y + radius }, // PO, P1, P2 + {rec.x + rec.width, (float)(rec.y + rec.height) - radius}, {(float)(rec.x + rec.width) - radius, rec.y + rec.height}, // P3, P4 + {(float)rec.x + radius, rec.y + rec.height}, { rec.x, (float)(rec.y + rec.height) - radius}, {rec.x, (float)rec.y + radius}, // P5, P6, P7 + {(float)rec.x + radius, (float)rec.y + radius}, {(float)(rec.x + rec.width) - radius, (float)rec.y + radius}, // P8, P9 + {(float)(rec.x + rec.width) - radius, (float)(rec.y + rec.height) - radius}, {(float)rec.x + radius, (float)(rec.y + rec.height) - radius} // P10, P11 + }; + +#if defined(SUPPORT_QUADS_DRAW_MODE) + if (rlCheckBufferLimit(16*segments/2 + 5*4)) rlglDraw(); + + rlBegin(RL_QUADS); + // Draw all of the 4 corners: [1] Upper Left Corner, [3] Upper Right Corner, [5] Lower Right Corner, [7] Lower Left Corner + const Vector2 centers[4] = { point[8], point[9], point[10], point[11] }; + const float angles[4] = {180.0f, 90.0f, 0.0f, 270.0f }; + for(int k = 0; k < 4; ++k) // Hope the compiler is smart enough to unroll this loop + { + float angle = angles[k]; + const Vector2 center = centers[k]; + // NOTE: Every QUAD actually represents two segments + for (int i = 0; i < segments/2; i++) + { + rlColor4ub(color.r, color.g, color.b, color.a); + rlVertex2f(center.x, center.y); + rlVertex2f(center.x + sinf(DEG2RAD*angle)*radius, center.y + cosf(DEG2RAD*angle)*radius); + rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*radius, center.y + cosf(DEG2RAD*(angle + stepLength))*radius); + rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength*2))*radius, center.y + cosf(DEG2RAD*(angle + stepLength*2))*radius); + angle += (stepLength*2); + } + // NOTE: In case number of segments is odd, we add one last piece to the cake + if (segments%2) + { + rlColor4ub(color.r, color.g, color.b, color.a); + rlVertex2f(center.x, center.y); + rlVertex2f(center.x + sinf(DEG2RAD*angle)*radius, center.y + cosf(DEG2RAD*angle)*radius); + rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*radius, center.y + cosf(DEG2RAD*(angle + stepLength))*radius); + rlVertex2f(center.x, center.y); + } + } + + // [2] Upper Rectangle + rlColor4ub(color.r, color.g, color.b, color.a); + rlVertex2f(point[0].x, point[0].y); + rlVertex2f(point[8].x, point[8].y); + rlVertex2f(point[9].x, point[9].y); + rlVertex2f(point[1].x, point[1].y); + + // [4] Right Rectangle + rlColor4ub(color.r, color.g, color.b, color.a); + rlVertex2f(point[2].x, point[2].y); + rlVertex2f(point[9].x, point[9].y); + rlVertex2f(point[10].x, point[10].y); + rlVertex2f(point[3].x, point[3].y); + + // [6] Bottom Rectangle + rlColor4ub(color.r, color.g, color.b, color.a); + rlVertex2f(point[11].x, point[11].y); + rlVertex2f(point[5].x, point[5].y); + rlVertex2f(point[4].x, point[4].y); + rlVertex2f(point[10].x, point[10].y); + + // [8] Left Rectangle + rlColor4ub(color.r, color.g, color.b, color.a); + rlVertex2f(point[7].x, point[7].y); + rlVertex2f(point[6].x, point[6].y); + rlVertex2f(point[11].x, point[11].y); + rlVertex2f(point[8].x, point[8].y); + + // [9] Middle Rectangle + rlColor4ub(color.r, color.g, color.b, color.a); + rlVertex2f(point[8].x, point[8].y); + rlVertex2f(point[11].x, point[11].y); + rlVertex2f(point[10].x, point[10].y); + rlVertex2f(point[9].x, point[9].y); + + rlEnd(); +#else + if (rlCheckBufferLimit(12*segments + 5*6)) rlglDraw(); // 4 corners with 3 vertices per segment + 5 rectangles with 6 vertices each + + rlBegin(RL_TRIANGLES); + // Draw all of the 4 corners: [1] Upper Left Corner, [3] Upper Right Corner, [5] Lower Right Corner, [7] Lower Left Corner + const Vector2 centers[4] = { point[8], point[9], point[10], point[11] }; + const float angles[4] = {180.0f, 90.0f, 0.0f, 270.0f }; + for(int k = 0; k < 4; ++k) // Hope the compiler is smart enough to unroll this loop + { + float angle = angles[k]; + const Vector2 center = centers[k]; + for (int i = 0; i < segments; i++) + { + rlColor4ub(color.r, color.g, color.b, color.a); + rlVertex2f(center.x, center.y); + rlVertex2f(center.x + sinf(DEG2RAD*angle)*radius, center.y + cosf(DEG2RAD*angle)*radius); + rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*radius, center.y + cosf(DEG2RAD*(angle + stepLength))*radius); + angle += stepLength; + } + } + + // [2] Upper Rectangle + rlColor4ub(color.r, color.g, color.b, color.a); + rlVertex2f(point[0].x, point[0].y); + rlVertex2f(point[8].x, point[8].y); + rlVertex2f(point[9].x, point[9].y); + rlVertex2f(point[1].x, point[1].y); + rlVertex2f(point[0].x, point[0].y); + rlVertex2f(point[9].x, point[9].y); + + // [4] Right Rectangle + rlColor4ub(color.r, color.g, color.b, color.a); + rlVertex2f(point[9].x, point[9].y); + rlVertex2f(point[10].x, point[10].y); + rlVertex2f(point[3].x, point[3].y); + rlVertex2f(point[2].x, point[2].y); + rlVertex2f(point[9].x, point[9].y); + rlVertex2f(point[3].x, point[3].y); + + // [6] Bottom Rectangle + rlColor4ub(color.r, color.g, color.b, color.a); + rlVertex2f(point[11].x, point[11].y); + rlVertex2f(point[5].x, point[5].y); + rlVertex2f(point[4].x, point[4].y); + rlVertex2f(point[10].x, point[10].y); + rlVertex2f(point[11].x, point[11].y); + rlVertex2f(point[4].x, point[4].y); + + // [8] Left Rectangle + rlColor4ub(color.r, color.g, color.b, color.a); + rlVertex2f(point[7].x, point[7].y); + rlVertex2f(point[6].x, point[6].y); + rlVertex2f(point[11].x, point[11].y); + rlVertex2f(point[8].x, point[8].y); + rlVertex2f(point[7].x, point[7].y); + rlVertex2f(point[11].x, point[11].y); + + // [9] Middle Rectangle + rlColor4ub(color.r, color.g, color.b, color.a); + rlVertex2f(point[8].x, point[8].y); + rlVertex2f(point[11].x, point[11].y); + rlVertex2f(point[10].x, point[10].y); + rlVertex2f(point[9].x, point[9].y); + rlVertex2f(point[8].x, point[8].y); + rlVertex2f(point[10].x, point[10].y); + rlEnd(); +#endif +} + // Draw a triangle void DrawTriangle(Vector2 v1, Vector2 v2, Vector2 v3, Color color) { From 1415d514ba9fc2b21ce99ceb2f21e7d6dc492219 Mon Sep 17 00:00:00 2001 From: Vlad Adrian Date: Sun, 31 Mar 2019 13:20:45 +0300 Subject: [PATCH 22/74] Update raylib.h --- src/raylib.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/raylib.h b/src/raylib.h index 0a8351c09..1ad719bda 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1056,7 +1056,8 @@ RLAPI void DrawRectangleGradientH(int posX, int posY, int width, int height, Col RLAPI void DrawRectangleGradientEx(Rectangle rec, Color col1, Color col2, Color col3, Color col4); // Draw a gradient-filled rectangle with custom vertex colors RLAPI void DrawRectangleLines(int posX, int posY, int width, int height, Color color); // Draw rectangle outline RLAPI void DrawRectangleLinesEx(Rectangle rec, int lineThick, Color color); // Draw rectangle outline with extended parameters -RLAPI void DrawRoundedRect(Rectangle rec, float roundness, int segments, Color color); // Draw rectangle with rounded edges +RLAPI void DrawRoundedRect(Rectangle rec, float roundness, int segments, Color color); // Draw rectangle with rounded edges +RLAPI void DrawRoundedRectLines(Rectangle rec, float roundness, int segments, int lineThick, Color color); // Draw rounded rectangle outline RLAPI void DrawTriangle(Vector2 v1, Vector2 v2, Vector2 v3, Color color); // Draw a color-filled triangle RLAPI void DrawTriangleLines(Vector2 v1, Vector2 v2, Vector2 v3, Color color); // Draw triangle outline RLAPI void DrawPoly(Vector2 center, int sides, float radius, float rotation, Color color); // Draw a regular polygon (Vector version) From ecbd17910d7769ec9628576b7e49afac6c56722b Mon Sep 17 00:00:00 2001 From: Vlad Adrian Date: Sun, 31 Mar 2019 13:22:50 +0300 Subject: [PATCH 23/74] Added `DrawRoundedRectLines()` --- src/shapes.c | 207 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 207 insertions(+) diff --git a/src/shapes.c b/src/shapes.c index 9eb690d3f..f25ed76fe 100644 --- a/src/shapes.c +++ b/src/shapes.c @@ -892,6 +892,213 @@ void DrawRoundedRect(Rectangle rec, float roundness, int segments, Color color) #endif } +// Draw rounded rectangle outline +void DrawRoundedRectLines(Rectangle rec, float roundness, int segments, int lineThick, Color color) +{ + // Not a rounded rectangle + // NOTE: Make sure we have at least 1px space left to render the rectangles near the corners + if(roundness <= 0.0f || rec.width <= 1 || rec.height <= 1 ) + { + DrawRectangleLinesEx(rec, lineThick, color); + return; + } + + if(roundness >= 1.0f) roundness = 1.0f; + + // Calculate corner radius + // NOTE: Make sure we have at least 1px space left to render the rectangles near the corners + float radius = rec.width > rec.height ? ((rec.height-1)*roundness)/2 : ((rec.width-1)*roundness)/2; + if(radius <= 0.0f) return; + if(lineThick > radius-1) lineThick = radius - 1; + + // Calculate number of segments to use for the corners + if (segments < 4) + { + // Calculate how many segments we need to draw a smooth circle, taken from https://stackoverflow.com/a/2244088 + #ifndef CIRCLE_ERROR_RATE + #define CIRCLE_ERROR_RATE 0.5f + #endif + // Calculate the maximum angle between segments based on the error rate. + float th = acosf(2*powf(1 - CIRCLE_ERROR_RATE/radius, 2) - 1); + segments = ceilf(2*PI/th)/4; + if (segments <= 0) segments = 4; + } + + float stepLength = 90.0f/(float)segments; + const float outerRadius = radius, innerRadius = radius - (float)lineThick; + + /* Quick sketch to make sense of all of this (mark the 16 + 4(corner centers P16-19) points we'll use below) + * Not my best attempt at ASCII art, just preted it's rounded rectangle :) + * P0 P1 + * ==================== + * // P8 P9 \\ + * // \\ + *P7 // P15 P10 \\ P2 + * || *P16 P17* || + * || || + * || P14 P11 || + *P6 \\ *P19 P18* // P3 + * \\ // + * \\ P13 P12 // + * ==================== + * P5 P4 + */ + const Vector2 point[16] = { + {(float)rec.x + outerRadius, rec.y}, {(float)(rec.x + rec.width) - outerRadius, rec.y}, { rec.x + rec.width, (float)rec.y + outerRadius }, // PO, P1, P2 + {rec.x + rec.width, (float)(rec.y + rec.height) - outerRadius}, {(float)(rec.x + rec.width) - outerRadius, rec.y + rec.height}, // P3, P4 + {(float)rec.x + outerRadius, rec.y + rec.height}, { rec.x, (float)(rec.y + rec.height) - outerRadius}, {rec.x, (float)rec.y + outerRadius}, // P5, P6, P7 + {(float)rec.x + outerRadius, rec.y + lineThick}, {(float)(rec.x + rec.width) - outerRadius, rec.y + lineThick}, // P8, P9 + { rec.x + rec.width - lineThick, (float)rec.y + outerRadius }, {rec.x + rec.width - lineThick, (float)(rec.y + rec.height) - outerRadius}, // P10, P11 + {(float)(rec.x + rec.width) - outerRadius, rec.y + rec.height - lineThick}, {(float)rec.x + outerRadius, rec.y + rec.height - lineThick}, // P12, P13 + { rec.x + lineThick, (float)(rec.y + rec.height) - outerRadius}, {rec.x + lineThick, (float)rec.y + outerRadius} // P14, P15 + }; + const Vector2 centers[4] = { + {(float)rec.x + outerRadius, (float)rec.y + outerRadius}, {(float)(rec.x + rec.width) - outerRadius, (float)rec.y + outerRadius}, // P16, P17 + {(float)(rec.x + rec.width) - outerRadius, (float)(rec.y + rec.height) - outerRadius}, {(float)rec.x + outerRadius, (float)(rec.y + rec.height) - outerRadius} // P18, P19 + }; + const float angles[4] = {180.0f, 90.0f, 0.0f, 270.0f }; + + if(lineThick > 1) + { +#if defined(SUPPORT_QUADS_DRAW_MODE) + if (rlCheckBufferLimit(4*4*segments + 4*4)) rlglDraw(); // 4 corners with 4 vertices for each segment + 4 rectangles with 4 vertices each + rlBegin(RL_QUADS); + // Draw all of the 4 corners first: Upper Left Corner, Upper Right Corner, Lower Right Corner, Lower Left Corner + for(int k = 0; k < 4; ++k) // Hope the compiler is smart enough to unroll this loop + { + float angle = angles[k]; + const Vector2 center = centers[k]; + for (int i = 0; i < segments; i++) + { + rlColor4ub(color.r, color.g, color.b, color.a); + rlVertex2f(center.x + sinf(DEG2RAD*angle)*innerRadius, center.y + cosf(DEG2RAD*angle)*innerRadius); + rlVertex2f(center.x + sinf(DEG2RAD*angle)*outerRadius, center.y + cosf(DEG2RAD*angle)*outerRadius); + rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*outerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*outerRadius); + rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*innerRadius); + + angle += stepLength; + } + } + // Upper rectangle + rlColor4ub(color.r, color.g, color.b, color.a); + rlVertex2f(point[0].x, point[0].y); + rlVertex2f(point[8].x, point[8].y); + rlVertex2f(point[9].x, point[9].y); + rlVertex2f(point[1].x, point[1].y); + + // Right rectangle + rlColor4ub(color.r, color.g, color.b, color.a); + rlVertex2f(point[2].x, point[2].y); + rlVertex2f(point[10].x, point[10].y); + rlVertex2f(point[11].x, point[11].y); + rlVertex2f(point[3].x, point[3].y); + + // Lower rectangle + rlColor4ub(color.r, color.g, color.b, color.a); + rlVertex2f(point[13].x, point[13].y); + rlVertex2f(point[5].x, point[5].y); + rlVertex2f(point[4].x, point[4].y); + rlVertex2f(point[12].x, point[12].y); + + // Left rectangle + rlColor4ub(color.r, color.g, color.b, color.a); + rlVertex2f(point[15].x, point[15].y); + rlVertex2f(point[7].x, point[7].y); + rlVertex2f(point[6].x, point[6].y); + rlVertex2f(point[14].x, point[14].y); + + rlEnd(); +#else + if (rlCheckBufferLimit(4*6*segments + 4*6)) rlglDraw(); // 4 corners with 6(2*3) vertices for each segment + 4 rectangles with 6 vertices each + rlBegin(RL_TRIANGLES); + // Draw all of the 4 corners first: Upper Left Corner, Upper Right Corner, Lower Right Corner, Lower Left Corner + for(int k = 0; k < 4; ++k) // Hope the compiler is smart enough to unroll this loop + { + float angle = angles[k]; + const Vector2 center = centers[k]; + for (int i = 0; i < segments; i++) + { + rlColor4ub(color.r, color.g, color.b, color.a); + + rlVertex2f(center.x + sinf(DEG2RAD*angle)*innerRadius, center.y + cosf(DEG2RAD*angle)*innerRadius); + rlVertex2f(center.x + sinf(DEG2RAD*angle)*outerRadius, center.y + cosf(DEG2RAD*angle)*outerRadius); + rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*innerRadius); + + rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*innerRadius); + rlVertex2f(center.x + sinf(DEG2RAD*angle)*outerRadius, center.y + cosf(DEG2RAD*angle)*outerRadius); + rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*outerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*outerRadius); + + angle += stepLength; + } + } + + // Upper rectangle + rlColor4ub(color.r, color.g, color.b, color.a); + rlVertex2f(point[0].x, point[0].y); + rlVertex2f(point[8].x, point[8].y); + rlVertex2f(point[9].x, point[9].y); + rlVertex2f(point[1].x, point[1].y); + rlVertex2f(point[0].x, point[0].y); + rlVertex2f(point[9].x, point[9].y); + + // Right rectangle + rlColor4ub(color.r, color.g, color.b, color.a); + rlVertex2f(point[10].x, point[10].y); + rlVertex2f(point[11].x, point[11].y); + rlVertex2f(point[3].x, point[3].y); + rlVertex2f(point[2].x, point[2].y); + rlVertex2f(point[10].x, point[10].y); + rlVertex2f(point[3].x, point[3].y); + + // Lower rectangle + rlColor4ub(color.r, color.g, color.b, color.a); + rlVertex2f(point[13].x, point[13].y); + rlVertex2f(point[5].x, point[5].y); + rlVertex2f(point[4].x, point[4].y); + rlVertex2f(point[12].x, point[12].y); + rlVertex2f(point[13].x, point[13].y); + rlVertex2f(point[4].x, point[4].y); + + // Left rectangle + rlColor4ub(color.r, color.g, color.b, color.a); + rlVertex2f(point[7].x, point[7].y); + rlVertex2f(point[6].x, point[6].y); + rlVertex2f(point[14].x, point[14].y); + rlVertex2f(point[15].x, point[15].y); + rlVertex2f(point[7].x, point[7].y); + rlVertex2f(point[14].x, point[14].y); + rlEnd(); +#endif + } + else + { + // Use LINES to draw the outline + if (rlCheckBufferLimit(8*segments + 4*2)) rlglDraw(); // 4 corners with 2 vertices for each segment + 4 rectangles with 2 vertices each + rlBegin(RL_LINES); + // Draw all of the 4 corners first: Upper Left Corner, Upper Right Corner, Lower Right Corner, Lower Left Corner + for(int k = 0; k < 4; ++k) // Hope the compiler is smart enough to unroll this loop + { + float angle = angles[k]; + const Vector2 center = centers[k]; + for (int i = 0; i < segments; i++) + { + rlColor4ub(color.r, color.g, color.b, color.a); + rlVertex2f(center.x + sinf(DEG2RAD*angle)*outerRadius, center.y + cosf(DEG2RAD*angle)*outerRadius); + rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*outerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*outerRadius); + angle += stepLength; + } + } + // And now the remaining 4 lines + for(int i=0; i<8; i+=2) + { + rlColor4ub(color.r, color.g, color.b, color.a); + rlVertex2f(point[i].x, point[i].y); + rlVertex2f(point[i+1].x, point[i+1].y); + } + rlEnd(); + } +} + // Draw a triangle void DrawTriangle(Vector2 v1, Vector2 v2, Vector2 v3, Color color) { From eda982e2612fb08bef951692e9e80cb3a36d9731 Mon Sep 17 00:00:00 2001 From: Demizdor Date: Sun, 31 Mar 2019 16:15:40 +0300 Subject: [PATCH 24/74] Reimplemented DrawRoundedRectLines() --- src/shapes.c | 46 ++++++++++++++++++++-------------------------- 1 file changed, 20 insertions(+), 26 deletions(-) diff --git a/src/shapes.c b/src/shapes.c index f25ed76fe..879d2a107 100644 --- a/src/shapes.c +++ b/src/shapes.c @@ -702,8 +702,7 @@ void DrawRectangleLinesEx(Rectangle rec, int lineThick, Color color) void DrawRoundedRect(Rectangle rec, float roundness, int segments, Color color) { // Not a rounded rectangle - // NOTE: Make sure we have at least 1px space left to render the rectangles near the corners - if(roundness <= 0.0f || rec.width <= 1 || rec.height <= 1 ) + if(roundness <= 0.0f || rec.width < 1 || rec.height < 1 ) { DrawRectangleRec(rec, color); return; @@ -712,8 +711,7 @@ void DrawRoundedRect(Rectangle rec, float roundness, int segments, Color color) if(roundness >= 1.0f) roundness = 1.0f; // Calculate corner radius - // NOTE: Make sure we have at least 1px space left to render the rectangles near the corners - float radius = rec.width > rec.height ? ((rec.height-1)*roundness)/2 : ((rec.width-1)*roundness)/2; + float radius = rec.width > rec.height ? (rec.height*roundness)/2 : (rec.width*roundness)/2; if(radius <= 0.0f) return; // Calculate number of segments to use for the corners @@ -732,7 +730,7 @@ void DrawRoundedRect(Rectangle rec, float roundness, int segments, Color color) float stepLength = 90.0f/(float)segments; /* Quick sketch to make sense of all of this (there are 9 parts to draw, also mark the 12 points we'll use below) - * Not my best attempt at ASCII art, just preted it's a rounded rectangle :) + * Not my best attempt at ASCII art, just preted it's rounded rectangle :) * P0 P1 * ____________________ * /| |\ @@ -754,14 +752,14 @@ void DrawRoundedRect(Rectangle rec, float roundness, int segments, Color color) {(float)rec.x + radius, (float)rec.y + radius}, {(float)(rec.x + rec.width) - radius, (float)rec.y + radius}, // P8, P9 {(float)(rec.x + rec.width) - radius, (float)(rec.y + rec.height) - radius}, {(float)rec.x + radius, (float)(rec.y + rec.height) - radius} // P10, P11 }; + const Vector2 centers[4] = { point[8], point[9], point[10], point[11] }; + const float angles[4] = {180.0f, 90.0f, 0.0f, 270.0f }; #if defined(SUPPORT_QUADS_DRAW_MODE) if (rlCheckBufferLimit(16*segments/2 + 5*4)) rlglDraw(); rlBegin(RL_QUADS); // Draw all of the 4 corners: [1] Upper Left Corner, [3] Upper Right Corner, [5] Lower Right Corner, [7] Lower Left Corner - const Vector2 centers[4] = { point[8], point[9], point[10], point[11] }; - const float angles[4] = {180.0f, 90.0f, 0.0f, 270.0f }; for(int k = 0; k < 4; ++k) // Hope the compiler is smart enough to unroll this loop { float angle = angles[k]; @@ -828,8 +826,6 @@ void DrawRoundedRect(Rectangle rec, float roundness, int segments, Color color) rlBegin(RL_TRIANGLES); // Draw all of the 4 corners: [1] Upper Left Corner, [3] Upper Right Corner, [5] Lower Right Corner, [7] Lower Left Corner - const Vector2 centers[4] = { point[8], point[9], point[10], point[11] }; - const float angles[4] = {180.0f, 90.0f, 0.0f, 270.0f }; for(int k = 0; k < 4; ++k) // Hope the compiler is smart enough to unroll this loop { float angle = angles[k]; @@ -895,21 +891,19 @@ void DrawRoundedRect(Rectangle rec, float roundness, int segments, Color color) // Draw rounded rectangle outline void DrawRoundedRectLines(Rectangle rec, float roundness, int segments, int lineThick, Color color) { + if(lineThick < 0) lineThick = 0; // Not a rounded rectangle - // NOTE: Make sure we have at least 1px space left to render the rectangles near the corners - if(roundness <= 0.0f || rec.width <= 1 || rec.height <= 1 ) + if(roundness <= 0.0f ) { - DrawRectangleLinesEx(rec, lineThick, color); + DrawRectangleLinesEx((Rectangle){rec.x-lineThick, rec.y-lineThick, rec.width+2*lineThick, rec.height+2*lineThick}, lineThick, color); return; } if(roundness >= 1.0f) roundness = 1.0f; // Calculate corner radius - // NOTE: Make sure we have at least 1px space left to render the rectangles near the corners - float radius = rec.width > rec.height ? ((rec.height-1)*roundness)/2 : ((rec.width-1)*roundness)/2; + float radius = rec.width > rec.height ? (rec.height*roundness)/2 : (rec.width*roundness)/2; if(radius <= 0.0f) return; - if(lineThick > radius-1) lineThick = radius - 1; // Calculate number of segments to use for the corners if (segments < 4) @@ -920,12 +914,12 @@ void DrawRoundedRectLines(Rectangle rec, float roundness, int segments, int line #endif // Calculate the maximum angle between segments based on the error rate. float th = acosf(2*powf(1 - CIRCLE_ERROR_RATE/radius, 2) - 1); - segments = ceilf(2*PI/th)/4; + segments = ceilf(2*PI/th)/2; if (segments <= 0) segments = 4; } float stepLength = 90.0f/(float)segments; - const float outerRadius = radius, innerRadius = radius - (float)lineThick; + const float outerRadius = radius + (float)lineThick, innerRadius = radius; /* Quick sketch to make sense of all of this (mark the 16 + 4(corner centers P16-19) points we'll use below) * Not my best attempt at ASCII art, just preted it's rounded rectangle :) @@ -944,17 +938,17 @@ void DrawRoundedRectLines(Rectangle rec, float roundness, int segments, int line * P5 P4 */ const Vector2 point[16] = { - {(float)rec.x + outerRadius, rec.y}, {(float)(rec.x + rec.width) - outerRadius, rec.y}, { rec.x + rec.width, (float)rec.y + outerRadius }, // PO, P1, P2 - {rec.x + rec.width, (float)(rec.y + rec.height) - outerRadius}, {(float)(rec.x + rec.width) - outerRadius, rec.y + rec.height}, // P3, P4 - {(float)rec.x + outerRadius, rec.y + rec.height}, { rec.x, (float)(rec.y + rec.height) - outerRadius}, {rec.x, (float)rec.y + outerRadius}, // P5, P6, P7 - {(float)rec.x + outerRadius, rec.y + lineThick}, {(float)(rec.x + rec.width) - outerRadius, rec.y + lineThick}, // P8, P9 - { rec.x + rec.width - lineThick, (float)rec.y + outerRadius }, {rec.x + rec.width - lineThick, (float)(rec.y + rec.height) - outerRadius}, // P10, P11 - {(float)(rec.x + rec.width) - outerRadius, rec.y + rec.height - lineThick}, {(float)rec.x + outerRadius, rec.y + rec.height - lineThick}, // P12, P13 - { rec.x + lineThick, (float)(rec.y + rec.height) - outerRadius}, {rec.x + lineThick, (float)rec.y + outerRadius} // P14, P15 + {(float)rec.x + innerRadius, rec.y - lineThick}, {(float)(rec.x + rec.width) - innerRadius, rec.y - lineThick}, { rec.x + rec.width + lineThick, (float)rec.y + innerRadius }, // PO, P1, P2 + {rec.x + rec.width + lineThick, (float)(rec.y + rec.height) - innerRadius}, {(float)(rec.x + rec.width) - innerRadius, rec.y + rec.height + lineThick}, // P3, P4 + {(float)rec.x + innerRadius, rec.y + rec.height + lineThick}, { rec.x - lineThick, (float)(rec.y + rec.height) - innerRadius}, {rec.x - lineThick, (float)rec.y + innerRadius}, // P5, P6, P7 + {(float)rec.x + innerRadius, rec.y}, {(float)(rec.x + rec.width) - innerRadius, rec.y}, // P8, P9 + { rec.x + rec.width, (float)rec.y + innerRadius }, {rec.x + rec.width, (float)(rec.y + rec.height) - innerRadius}, // P10, P11 + {(float)(rec.x + rec.width) - innerRadius, rec.y + rec.height}, {(float)rec.x + innerRadius, rec.y + rec.height}, // P12, P13 + { rec.x, (float)(rec.y + rec.height) - innerRadius}, {rec.x, (float)rec.y + innerRadius} // P14, P15 }; const Vector2 centers[4] = { - {(float)rec.x + outerRadius, (float)rec.y + outerRadius}, {(float)(rec.x + rec.width) - outerRadius, (float)rec.y + outerRadius}, // P16, P17 - {(float)(rec.x + rec.width) - outerRadius, (float)(rec.y + rec.height) - outerRadius}, {(float)rec.x + outerRadius, (float)(rec.y + rec.height) - outerRadius} // P18, P19 + {(float)rec.x + innerRadius, (float)rec.y + innerRadius}, {(float)(rec.x + rec.width) - innerRadius, (float)rec.y + innerRadius}, // P16, P17 + {(float)(rec.x + rec.width) - innerRadius, (float)(rec.y + rec.height) - innerRadius}, {(float)rec.x + innerRadius, (float)(rec.y + rec.height) - innerRadius} // P18, P19 }; const float angles[4] = {180.0f, 90.0f, 0.0f, 270.0f }; From 26fb2e0f3a5047dd77bfea82543a341aa25dda7a Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 1 Apr 2019 00:15:14 +0200 Subject: [PATCH 25/74] Update cube.obj --- examples/models/resources/models/cube.obj | 44 ++++------------------- 1 file changed, 7 insertions(+), 37 deletions(-) diff --git a/examples/models/resources/models/cube.obj b/examples/models/resources/models/cube.obj index 0e9d65979..bf7e3bee4 100644 --- a/examples/models/resources/models/cube.obj +++ b/examples/models/resources/models/cube.obj @@ -1,7 +1,7 @@ # reference material -mtllib myCube.mtl +#mtllib cube.mtl -# object Pau_Box +# object box # vertex (XZY) v 5.5 0 1.5 @@ -14,85 +14,55 @@ v 5.5 3 -1.5 v 8.5 3 -1.5 # normals (XYZ) -# red vn 0 -1 0 -#blue vn 0 1 0 -#top vn 0 0 1 -#yellow vn 1 0 0 -#bottom vn 0 0 -1 -#green vn -1 0 0 - # UVs (XY) -# yellow (1234) vt 0.5 0 0 vt 1 0 0 vt 1 0.5 0 vt 0.5 0.5 0 -# red (5678) vt 0.5 0.5 0 vt 1 0.5 0 vt 0.5 1 0 vt 1 1 0 -#bottom (9101112) vt 0 0.5 0 vt 1 0.5 0 vt 1 0 0 vt 0 0 0 -#top (13141516) vt 0 0.5 0 vt 1 0.5 0 vt 1 1 0 vt 0 1 0 -#green (17181920) vt 0.5 0 0 vt 0 0 0 vt 0 0.5 0 vt 0.5 0.5 0 -#blue (21222324) vt 0 0.5 0 vt 0.5 0.5 0 vt 0.5 1 0 vt 0 1 0 # merger -g Pau_Box +g box # reference material -usemtl Material_1 +#usemtl mat01 -# bottom -f 1/9/1 3/10/1 4/11/1 -f 4/11/1 2/12/1 1/9/1 - -# top +# faces +f 1/9/1 3/10/1 4/11/1 +f 4/11/1 2/12/1 1/9/1 f 5/13/2 6/14/2 8/15/2 f 8/15/2 7/16/2 5/13/2 - -# front-yellow f 1/17/6 2/18/6 6/19/6 f 6/19/6 5/20/6 1/17/6 - -# right-blue f 2/6/1 4/5/1 8/7/1 f 8/7/1 6/8/1 2/6/1 - -# back-green f 4/2/3 3/1/3 7/4/3 f 7/4/3 8/3/3 4/2/3 - -# left-red f 3/22/5 1/21/5 5/24/5 f 5/24/5 7/23/5 3/22/5 - - - - - - - From 3e806ad9d4bc2131b2d8bf9c68e5e30a2b9b8e64 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 1 Apr 2019 00:15:45 +0200 Subject: [PATCH 26/74] Reviewed data assignation --- src/external/tinyobj_loader_c.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/external/tinyobj_loader_c.h b/src/external/tinyobj_loader_c.h index e9d015fff..ae975829c 100644 --- a/src/external/tinyobj_loader_c.h +++ b/src/external/tinyobj_loader_c.h @@ -1342,12 +1342,13 @@ int tinyobj_parse_obj(tinyobj_attrib_t *attrib, tinyobj_shape_t **shapes, attrib->num_normals = (unsigned int)num_vn; attrib->texcoords = (float *)TINYOBJ_MALLOC(sizeof(float) * num_vt * 2); attrib->num_texcoords = (unsigned int)num_vt; - attrib->faces = (tinyobj_vertex_index_t *)TINYOBJ_MALLOC( - sizeof(tinyobj_vertex_index_t) * num_f); - attrib->num_faces = (unsigned int)num_f; + attrib->faces = (tinyobj_vertex_index_t *)TINYOBJ_MALLOC(sizeof(tinyobj_vertex_index_t) * num_f); attrib->face_num_verts = (int *)TINYOBJ_MALLOC(sizeof(int) * num_faces); + + attrib->num_faces = (unsigned int)num_faces; + attrib->num_face_num_verts = (unsigned int)num_f; + attrib->material_ids = (int *)TINYOBJ_MALLOC(sizeof(int) * num_faces); - attrib->num_face_num_verts = (unsigned int)num_faces; for (i = 0; i < num_lines; i++) { if (commands[i].type == COMMAND_EMPTY) { From fe702cd6a2d1aeb23c47c3db412311e2b04c9b7c Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 1 Apr 2019 00:16:56 +0200 Subject: [PATCH 27/74] Implementing LoadOBJ() -WIP- It seems obj loading is working ok but there is some problem with drawing... --- src/models.c | 173 +++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 140 insertions(+), 33 deletions(-) diff --git a/src/models.c b/src/models.c index 2893fd2f5..68f5f6be1 100644 --- a/src/models.c +++ b/src/models.c @@ -2400,55 +2400,113 @@ static Model LoadOBJ(const char *fileName) if (objFile != NULL) { fseek(objFile, 0, SEEK_END); - long dataLength = ftell(objFile); // Get file size - fseek(objFile, 0, SEEK_SET); // Reset file pointer + long length = ftell(objFile); // Get file size + fseek(objFile, 0, SEEK_SET); // Reset file pointer - data = (char *)malloc(dataLength); + data = (char *)malloc(length); - fread(data, dataLength, 1, objFile); + fread(data, length, 1, objFile); + dataLength = length; fclose(objFile); } - if (data != NULL) + if (data != NULL) { unsigned int flags = TINYOBJ_FLAG_TRIANGULATE; int ret = tinyobj_parse_obj(&attrib, &meshes, &meshCount, &materials, &materialCount, data, dataLength, flags); if (ret != TINYOBJ_SUCCESS) TraceLog(LOG_WARNING, "[%s] Model data could not be loaded", fileName); else TraceLog(LOG_INFO, "[%s] Model data loaded successfully: %i meshes / %i materials", fileName, meshCount, materialCount); - - for (int i = 0; i < meshCount; i++) + + // Init model meshes array + model.meshCount = meshCount; + model.meshes = (Mesh *)malloc(model.meshCount*sizeof(Mesh)); + + // Init model materials array + model.materialCount = materialCount; + model.materials = (Material *)malloc(model.materialCount*sizeof(Material)); + + // Init model meshes + for (int m = 0; m < 1; m++) { - printf("shape[%d] name = %s\n", i, meshes[i].name); + printf("num_vertices: %i\n", attrib.num_vertices); + printf("num_normals: %i\n", attrib.num_normals); + printf("num_texcoords: %i\n", attrib.num_texcoords); + printf("num_faces: %i\n", attrib.num_faces); + printf("num_face_num_verts: %i\n", attrib.num_face_num_verts); + + Mesh mesh = { 0 }; + memset(&mesh, 0, sizeof(Mesh)); + mesh.vertexCount = attrib.num_faces*3; + mesh.triangleCount = attrib.num_faces; + mesh.vertices = (float *)malloc(mesh.vertexCount*3*sizeof(float)); + mesh.texcoords = (float *)malloc(mesh.vertexCount*2*sizeof(float)); + mesh.normals = (float *)malloc(mesh.vertexCount*3*sizeof(float)); + + int faceOffset = 0; + + int vCount = 0; + int vtCount = 0; + int vnCount = 0; + + /* + for (int i = 0; i < attrib.num_vertices*3; i++) printf("%2.2f, ", attrib.vertices[i]); + printf("\n"); + for (int i = 0; i < attrib.num_texcoords*2; i++) printf("%2.2f, ", attrib.texcoords[i]); + printf("\n"); + for (int i = 0; i < attrib.num_normals*3; i++) printf("%2.2f, ", attrib.normals[i]); + printf("\n"); + + tinyobj_vertex_index_t idx0 = attrib.faces[0]; + tinyobj_vertex_index_t idx1 = attrib.faces[1]; + tinyobj_vertex_index_t idx2 = attrib.faces[2]; + + for (int v = 0; v < 3; v++) { printf("%2.2f, ", attrib.vertices[idx0.v_idx*3 + v]); } printf("\n"); + for (int v = 0; v < 3; v++) { printf("%2.2f, ", attrib.vertices[idx1.v_idx*3 + v]); } printf("\n"); + for (int v = 0; v < 3; v++) { printf("%2.2f, ", attrib.vertices[idx2.v_idx*3 + v]); } printf("\n\n"); + + idx0 = attrib.faces[3 + 0]; + idx1 = attrib.faces[3 + 1]; + idx2 = attrib.faces[3 + 2]; + + for (int v = 0; v < 3; v++) { printf("%2.2f, ", attrib.vertices[idx0.v_idx*3 + v]); } printf("\n"); + for (int v = 0; v < 3; v++) { printf("%2.2f, ", attrib.vertices[idx1.v_idx*3 + v]); } printf("\n"); + for (int v = 0; v < 3; v++) { printf("%2.2f, ", attrib.vertices[idx2.v_idx*3 + v]); } printf("\n\n"); +*/ + for (int f = 0; f < attrib.num_faces; f++) + { + tinyobj_vertex_index_t idx0 = attrib.faces[3*f + 0]; + tinyobj_vertex_index_t idx1 = attrib.faces[3*f + 1]; + tinyobj_vertex_index_t idx2 = attrib.faces[3*f + 2]; + + // printf("Face index: v %i/%i/%i . vt %i/%i/%i . vn %i/%i/%i\n", + // idx0.v_idx, idx1.v_idx, idx2.v_idx, + // idx0.vt_idx, idx1.vt_idx, idx2.vt_idx, + // idx0.vn_idx, idx1.vn_idx, idx2.vn_idx); + + for (int v = 0; v < 3; v++) { mesh.vertices[vCount + v] = attrib.vertices[idx0.v_idx*3 + v]; } vCount +=3; + for (int v = 0; v < 3; v++) { mesh.vertices[vCount + v] = attrib.vertices[idx1.v_idx*3 + v]; } vCount +=3; + for (int v = 0; v < 3; v++) { mesh.vertices[vCount + v] = attrib.vertices[idx2.v_idx*3 + v]; } vCount +=3; + + for (int v = 0; v < 2; v++) { mesh.texcoords[vtCount + v] = attrib.texcoords[idx0.vt_idx*2 + v]; } vtCount += 2; + for (int v = 0; v < 2; v++) { mesh.texcoords[vtCount + v] = attrib.texcoords[idx1.vt_idx*2 + v]; } vtCount += 2; + for (int v = 0; v < 2; v++) { mesh.texcoords[vtCount + v] = attrib.texcoords[idx2.vt_idx*2 + v]; } vtCount += 2; + + for (int v = 0; v < 3; v++) { mesh.normals[vnCount + v] = attrib.normals[idx0.vn_idx*3 + v]; } vnCount +=3; + for (int v = 0; v < 3; v++) { mesh.normals[vnCount + v] = attrib.normals[idx1.vn_idx*3 + v]; } vnCount +=3; + for (int v = 0; v < 3; v++) { mesh.normals[vnCount + v] = attrib.normals[idx2.vn_idx*3 + v]; } vnCount +=3; + } + + printf("vCount: %i\n", vCount); + printf("vtCount: %i\n", vtCount); + printf("vnCount: %i\n", vnCount); + + model.meshes[m] = mesh; // Assign mesh data to model + rlLoadMesh(&model.meshes[m], false); // Upload vertex data to GPU (static mesh) } /* // Data reference to process - typedef struct { - char *name; - - float ambient[3]; - float diffuse[3]; - float specular[3]; - float transmittance[3]; - float emission[3]; - float shininess; - float ior; // index of refraction - float dissolve; // 1 == opaque; 0 == fully transparent - // illumination model (see http://www.fileformat.info/format/material/) - int illum; - - int pad0; - - char *ambient_texname; // map_Ka - char *diffuse_texname; // map_Kd - char *specular_texname; // map_Ks - char *specular_highlight_texname; // map_Ns - char *bump_texname; // map_bump, bump - char *displacement_texname; // disp - char *alpha_texname; // map_d - } tinyobj_material_t; - typedef struct { char *name; // group name or object name unsigned int face_offset; @@ -2469,11 +2527,60 @@ static Model LoadOBJ(const char *fileName) float *vertices; float *normals; float *texcoords; + tinyobj_vertex_index_t *faces; int *face_num_verts; + int *material_ids; } tinyobj_attrib_t; */ + + // Init model materials + for (int m = 0; m < materialCount; m++) + { + /* + typedef struct { + char *name; + + float ambient[3]; + float diffuse[3]; + float specular[3]; + float transmittance[3]; + float emission[3]; + float shininess; + float ior; // index of refraction + float dissolve; // 1 == opaque; 0 == fully transparent + // illumination model (see http://www.fileformat.info/format/material/) + int illum; + + int pad0; + + char *ambient_texname; // map_Ka + char *diffuse_texname; // map_Kd + char *specular_texname; // map_Ks + char *specular_highlight_texname; // map_Ns + char *bump_texname; // map_bump, bump + char *displacement_texname; // disp + char *alpha_texname; // map_d + } tinyobj_material_t; + */ + + /* + // Material texture map + typedef struct MaterialMap { + Texture2D texture; // Material map texture + Color color; // Material map color + float value; // Material map value + } MaterialMap; + + // Material type (generic) + typedef struct Material { + Shader shader; // Material shader + MaterialMap maps[MAX_MATERIAL_MAPS]; // Material maps + float *params; // Material generic parameters (if required) + } Material; + */ + } tinyobj_attrib_free(&attrib); tinyobj_shapes_free(meshes, meshCount); From 0a492b5dead96619b705ccf2d173f2d92df01657 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 1 Apr 2019 12:09:00 +0200 Subject: [PATCH 28/74] Retarget VS2017 solution --- projects/VS2017/examples/core_basic_window.vcxproj | 2 +- projects/VS2017/examples/core_basic_window_cpp.vcxproj | 2 +- projects/VS2017/raylib/raylib.vcxproj | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/projects/VS2017/examples/core_basic_window.vcxproj b/projects/VS2017/examples/core_basic_window.vcxproj index 48e06e448..87bef8d82 100644 --- a/projects/VS2017/examples/core_basic_window.vcxproj +++ b/projects/VS2017/examples/core_basic_window.vcxproj @@ -22,7 +22,7 @@ {0981CA98-E4A5-4DF1-987F-A41D09131EFC} Win32Proj core_basic_window - 10.0.14393.0 + 10.0.17763.0 core_basic_window diff --git a/projects/VS2017/examples/core_basic_window_cpp.vcxproj b/projects/VS2017/examples/core_basic_window_cpp.vcxproj index 39a2aee77..cfe085241 100644 --- a/projects/VS2017/examples/core_basic_window_cpp.vcxproj +++ b/projects/VS2017/examples/core_basic_window_cpp.vcxproj @@ -22,7 +22,7 @@ {B655E850-3322-42F7-941D-6AC18FD66CA1} Win32Proj raylib_example_cpp - 10.0.14393.0 + 10.0.17763.0 core_basic_window_cpp diff --git a/projects/VS2017/raylib/raylib.vcxproj b/projects/VS2017/raylib/raylib.vcxproj index 4c6200533..3ff77104e 100644 --- a/projects/VS2017/raylib/raylib.vcxproj +++ b/projects/VS2017/raylib/raylib.vcxproj @@ -22,7 +22,7 @@ {E89D61AC-55DE-4482-AFD4-DF7242EBC859} Win32Proj raylib - 10.0.14393.0 + 10.0.17763.0 From e5edbb7104795e9936a275a8c53caf5026ab497f Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 1 Apr 2019 12:17:29 +0200 Subject: [PATCH 29/74] Reviewed OBJ loading implementation -WIP- One mesh files can be loaded correctly MISSING: - Multimesh OBJ loading - Materials loading --- src/models.c | 131 ++++++++++++++++----------------------------------- 1 file changed, 41 insertions(+), 90 deletions(-) diff --git a/src/models.c b/src/models.c index 68f5f6be1..b1c56e482 100644 --- a/src/models.c +++ b/src/models.c @@ -635,7 +635,7 @@ Model LoadModel(const char *fileName) TraceLog(LOG_WARNING, "[%s] No meshes can be loaded, default to cube mesh", fileName); model.meshCount = 1; - model.meshes = (Mesh *)malloc(model.meshCount*sizeof(Mesh)); + model.meshes = (Mesh *)calloc(model.meshCount, sizeof(Mesh)); model.meshes[0] = GenMeshCube(1.0f, 1.0f, 1.0f); } @@ -644,7 +644,7 @@ Model LoadModel(const char *fileName) TraceLog(LOG_WARNING, "[%s] No materials can be loaded, default to white material", fileName); model.materialCount = 1; - model.materials = (Material *)malloc(model.materialCount*sizeof(Material)); + model.materials = (Material *)calloc(model.materialCount, sizeof(Material)); model.materials[0] = LoadMaterialDefault(); model.meshMaterial = (int *)calloc(model.meshCount, sizeof(int)); @@ -1879,7 +1879,7 @@ void UnloadMaterial(Material material) void DrawModel(Model model, Vector3 position, float scale, Color tint) { Vector3 vScale = { scale, scale, scale }; - Vector3 rotationAxis = { 0.0f, 0.0f, 0.0f }; + Vector3 rotationAxis = { 0.0f, 1.0f, 0.0f }; DrawModelEx(model, position, rotationAxis, 0.0f, vScale, tint); } @@ -1896,8 +1896,6 @@ void DrawModelEx(Model model, Vector3 position, Vector3 rotationAxis, float rota Matrix matTransform = MatrixMultiply(MatrixMultiply(matScale, matRotation), matTranslation); // Combine model transformation matrix (model.transform) with matrix generated by function parameters (matTransform) - //Matrix matModel = MatrixMultiply(model.transform, matTransform); // Transform to world-space coordinates - model.transform = MatrixMultiply(model.transform, matTransform); for (int i = 0; i < model.meshCount; i++) @@ -2126,25 +2124,25 @@ RayHitInfo GetCollisionRayModel(Ray ray, Model *model) { RayHitInfo result = { 0 }; - for (int i = 0; i < model->meshCount; i++) + for (int m = 0; m < model->meshCount; m++) { // Check if meshhas vertex data on CPU for testing - if (model->meshes[i].vertices != NULL) + if (model->meshes[m].vertices != NULL) { // model->mesh.triangleCount may not be set, vertexCount is more reliable - int triangleCount = model->meshes[i].vertexCount/3; + int triangleCount = model->meshes[m].vertexCount/3; // Test against all triangles in mesh for (int i = 0; i < triangleCount; i++) { Vector3 a, b, c; - Vector3 *vertdata = (Vector3 *)model->meshes[i].vertices; + Vector3 *vertdata = (Vector3 *)model->meshes[m].vertices; - if (model->meshes[i].indices) + if (model->meshes[m].indices) { - a = vertdata[model->meshes[i].indices[i*3 + 0]]; - b = vertdata[model->meshes[i].indices[i*3 + 1]]; - c = vertdata[model->meshes[i].indices[i*3 + 2]]; + a = vertdata[model->meshes[m].indices[i*3 + 0]]; + b = vertdata[model->meshes[m].indices[i*3 + 1]]; + c = vertdata[model->meshes[m].indices[i*3 + 2]]; } else { @@ -2260,6 +2258,8 @@ BoundingBox MeshBoundingBox(Mesh mesh) // Get min and max vertex to construct bounds (AABB) Vector3 minVertex = { 0 }; Vector3 maxVertex = { 0 }; + + printf("Mesh vertex count: %i\n", mesh.vertexCount); if (mesh.vertices != NULL) { @@ -2274,7 +2274,7 @@ BoundingBox MeshBoundingBox(Mesh mesh) } // Create the bounding box - BoundingBox box; + BoundingBox box = { 0 }; box.min = minVertex; box.max = maxVertex; @@ -2383,17 +2383,18 @@ void MeshBinormals(Mesh *mesh) static Model LoadOBJ(const char *fileName) { Model model = { 0 }; - + model.transform = MatrixIdentity(); + tinyobj_attrib_t attrib; tinyobj_shape_t *meshes = NULL; unsigned int meshCount = 0; - + tinyobj_material_t *materials = NULL; unsigned int materialCount = 0; int dataLength = 0; char *data = NULL; - + // Load model data FILE *objFile = fopen(fileName, "rb"); @@ -2425,16 +2426,21 @@ static Model LoadOBJ(const char *fileName) // Init model materials array model.materialCount = materialCount; model.materials = (Material *)malloc(model.materialCount*sizeof(Material)); + model.meshMaterial = (int *)calloc(model.meshCount, sizeof(int)); + + /* + // Multiple meshes data reference + // NOTE: They are provided as a faces offset + typedef struct { + char *name; // group name or object name + unsigned int face_offset; + unsigned int length; + } tinyobj_shape_t; + */ // Init model meshes for (int m = 0; m < 1; m++) { - printf("num_vertices: %i\n", attrib.num_vertices); - printf("num_normals: %i\n", attrib.num_normals); - printf("num_texcoords: %i\n", attrib.num_texcoords); - printf("num_faces: %i\n", attrib.num_faces); - printf("num_face_num_verts: %i\n", attrib.num_face_num_verts); - Mesh mesh = { 0 }; memset(&mesh, 0, sizeof(Mesh)); mesh.vertexCount = attrib.num_faces*3; @@ -2443,98 +2449,43 @@ static Model LoadOBJ(const char *fileName) mesh.texcoords = (float *)malloc(mesh.vertexCount*2*sizeof(float)); mesh.normals = (float *)malloc(mesh.vertexCount*3*sizeof(float)); - int faceOffset = 0; - int vCount = 0; int vtCount = 0; int vnCount = 0; - /* - for (int i = 0; i < attrib.num_vertices*3; i++) printf("%2.2f, ", attrib.vertices[i]); - printf("\n"); - for (int i = 0; i < attrib.num_texcoords*2; i++) printf("%2.2f, ", attrib.texcoords[i]); - printf("\n"); - for (int i = 0; i < attrib.num_normals*3; i++) printf("%2.2f, ", attrib.normals[i]); - printf("\n"); - - tinyobj_vertex_index_t idx0 = attrib.faces[0]; - tinyobj_vertex_index_t idx1 = attrib.faces[1]; - tinyobj_vertex_index_t idx2 = attrib.faces[2]; - - for (int v = 0; v < 3; v++) { printf("%2.2f, ", attrib.vertices[idx0.v_idx*3 + v]); } printf("\n"); - for (int v = 0; v < 3; v++) { printf("%2.2f, ", attrib.vertices[idx1.v_idx*3 + v]); } printf("\n"); - for (int v = 0; v < 3; v++) { printf("%2.2f, ", attrib.vertices[idx2.v_idx*3 + v]); } printf("\n\n"); - - idx0 = attrib.faces[3 + 0]; - idx1 = attrib.faces[3 + 1]; - idx2 = attrib.faces[3 + 2]; - - for (int v = 0; v < 3; v++) { printf("%2.2f, ", attrib.vertices[idx0.v_idx*3 + v]); } printf("\n"); - for (int v = 0; v < 3; v++) { printf("%2.2f, ", attrib.vertices[idx1.v_idx*3 + v]); } printf("\n"); - for (int v = 0; v < 3; v++) { printf("%2.2f, ", attrib.vertices[idx2.v_idx*3 + v]); } printf("\n\n"); -*/ for (int f = 0; f < attrib.num_faces; f++) { + // Get indices for the face tinyobj_vertex_index_t idx0 = attrib.faces[3*f + 0]; tinyobj_vertex_index_t idx1 = attrib.faces[3*f + 1]; tinyobj_vertex_index_t idx2 = attrib.faces[3*f + 2]; - // printf("Face index: v %i/%i/%i . vt %i/%i/%i . vn %i/%i/%i\n", - // idx0.v_idx, idx1.v_idx, idx2.v_idx, - // idx0.vt_idx, idx1.vt_idx, idx2.vt_idx, - // idx0.vn_idx, idx1.vn_idx, idx2.vn_idx); + // TraceLog(LOG_DEBUG, "Face %i index: v %i/%i/%i . vt %i/%i/%i . vn %i/%i/%i\n", f, idx0.v_idx, idx1.v_idx, idx2.v_idx, idx0.vt_idx, idx1.vt_idx, idx2.vt_idx, idx0.vn_idx, idx1.vn_idx, idx2.vn_idx); + // Fill vertices buffer (float) using vertex index of the face for (int v = 0; v < 3; v++) { mesh.vertices[vCount + v] = attrib.vertices[idx0.v_idx*3 + v]; } vCount +=3; for (int v = 0; v < 3; v++) { mesh.vertices[vCount + v] = attrib.vertices[idx1.v_idx*3 + v]; } vCount +=3; for (int v = 0; v < 3; v++) { mesh.vertices[vCount + v] = attrib.vertices[idx2.v_idx*3 + v]; } vCount +=3; - for (int v = 0; v < 2; v++) { mesh.texcoords[vtCount + v] = attrib.texcoords[idx0.vt_idx*2 + v]; } vtCount += 2; - for (int v = 0; v < 2; v++) { mesh.texcoords[vtCount + v] = attrib.texcoords[idx1.vt_idx*2 + v]; } vtCount += 2; - for (int v = 0; v < 2; v++) { mesh.texcoords[vtCount + v] = attrib.texcoords[idx2.vt_idx*2 + v]; } vtCount += 2; + // Fill texcoords buffer (float) using vertex index of the face + // NOTE: Y-coordinate must be flipped upside-down + mesh.texcoords[vtCount + 0] = attrib.texcoords[idx0.vt_idx*2 + 0]; + mesh.texcoords[vtCount + 1] = 1.0f - attrib.texcoords[idx0.vt_idx*2 + 1]; vtCount += 2; + mesh.texcoords[vtCount + 0] = attrib.texcoords[idx1.vt_idx*2 + 0]; + mesh.texcoords[vtCount + 1] = 1.0f - attrib.texcoords[idx1.vt_idx*2 + 1]; vtCount += 2; + mesh.texcoords[vtCount + 0] = attrib.texcoords[idx2.vt_idx*2 + 0]; + mesh.texcoords[vtCount + 1] = 1.0f - attrib.texcoords[idx2.vt_idx*2 + 1]; vtCount += 2; + // Fill normals buffer (float) using vertex index of the face for (int v = 0; v < 3; v++) { mesh.normals[vnCount + v] = attrib.normals[idx0.vn_idx*3 + v]; } vnCount +=3; for (int v = 0; v < 3; v++) { mesh.normals[vnCount + v] = attrib.normals[idx1.vn_idx*3 + v]; } vnCount +=3; for (int v = 0; v < 3; v++) { mesh.normals[vnCount + v] = attrib.normals[idx2.vn_idx*3 + v]; } vnCount +=3; } - - printf("vCount: %i\n", vCount); - printf("vtCount: %i\n", vtCount); - printf("vnCount: %i\n", vnCount); model.meshes[m] = mesh; // Assign mesh data to model rlLoadMesh(&model.meshes[m], false); // Upload vertex data to GPU (static mesh) } - - /* - // Data reference to process - typedef struct { - char *name; // group name or object name - unsigned int face_offset; - unsigned int length; - } tinyobj_shape_t; - typedef struct { int v_idx, vt_idx, vn_idx; } tinyobj_vertex_index_t; - - typedef struct { - unsigned int num_vertices; - unsigned int num_normals; - unsigned int num_texcoords; - unsigned int num_faces; - unsigned int num_face_num_verts; - - int pad0; - - float *vertices; - float *normals; - float *texcoords; - - tinyobj_vertex_index_t *faces; - int *face_num_verts; - - int *material_ids; - } tinyobj_attrib_t; - */ - // Init model materials for (int m = 0; m < materialCount; m++) { From 86212e84628e589701c7934affd83685ff3e8ae9 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 1 Apr 2019 12:41:32 +0200 Subject: [PATCH 30/74] Support material loading from OBJ/MTL --- src/models.c | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/src/models.c b/src/models.c index b1c56e482..9fb08d82e 100644 --- a/src/models.c +++ b/src/models.c @@ -2484,11 +2484,18 @@ static Model LoadOBJ(const char *fileName) model.meshes[m] = mesh; // Assign mesh data to model rlLoadMesh(&model.meshes[m], false); // Upload vertex data to GPU (static mesh) + + // Assign mesh material for current mesh + model.meshMaterial[m] = attrib.material_ids[m]; } // Init model materials for (int m = 0; m < materialCount; m++) { + // Init material to default + // NOTE: Uses default shader, only MAP_DIFFUSE supported + model.materials[m] = LoadMaterialDefault(); + /* typedef struct { char *name; @@ -2516,21 +2523,21 @@ static Model LoadOBJ(const char *fileName) } tinyobj_material_t; */ - /* - // Material texture map - typedef struct MaterialMap { - Texture2D texture; // Material map texture - Color color; // Material map color - float value; // Material map value - } MaterialMap; + model.materials[m].maps[MAP_DIFFUSE].texture = LoadTexture(materials[m].diffuse_texname); //char *diffuse_texname; // map_Kd + model.materials[m].maps[MAP_DIFFUSE].color = (Color){ (float)(materials[m].diffuse[0]*255.0f), (float)(materials[m].diffuse[1]*255.0f), (float)(materials[m].diffuse[2]*255.0f), 255 }; //float diffuse[3]; + model.materials[m].maps[MAP_DIFFUSE].value = 0.0f; + + model.materials[m].maps[MAP_SPECULAR].texture = LoadTexture(materials[m].specular_texname); //char *specular_texname; // map_Ks + model.materials[m].maps[MAP_SPECULAR].color = (Color){ (float)(materials[m].specular[0]*255.0f), (float)(materials[m].specular[1]*255.0f), (float)(materials[m].specular[2]*255.0f), 255 }; //float specular[3]; + model.materials[m].maps[MAP_SPECULAR].value = 0.0f; + + model.materials[m].maps[MAP_NORMAL].texture = LoadTexture(materials[m].bump_texname); //char *bump_texname; // map_bump, bump + model.materials[m].maps[MAP_NORMAL].color = WHITE; + model.materials[m].maps[MAP_NORMAL].value = materials[m].shininess; + + model.materials[m].maps[MAP_EMISSION].color = (Color){ (float)(materials[m].emission[0]*255.0f), (float)(materials[m].emission[1]*255.0f), (float)(materials[m].emission[2]*255.0f), 255 }; //float emission[3]; - // Material type (generic) - typedef struct Material { - Shader shader; // Material shader - MaterialMap maps[MAX_MATERIAL_MAPS]; // Material maps - float *params; // Material generic parameters (if required) - } Material; - */ + model.materials[m].maps[MAP_HEIGHT].texture = LoadTexture(materials[m].displacement_texname); //char *displacement_texname; // disp } tinyobj_attrib_free(&attrib); From f1cbdd6b3af5dc51cef306cbbc4619c7b6ed548a Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 1 Apr 2019 18:22:56 +0200 Subject: [PATCH 31/74] Corrected some issues - Support compiling for OpenGL 1.1 - Free meshes/materials memory after usage... --- src/models.c | 3 +++ src/rlgl.h | 19 ++++++++++++++++--- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/models.c b/src/models.c index 9fb08d82e..7c266843b 100644 --- a/src/models.c +++ b/src/models.c @@ -682,6 +682,9 @@ void UnloadModel(Model model) { for (int i = 0; i < model.meshCount; i++) UnloadMesh(&model.meshes[i]); for (int i = 0; i < model.materialCount; i++) UnloadMaterial(model.materials[i]); + + free(model.meshes); + free(model.materials); free(model.meshMaterial); TraceLog(LOG_INFO, "Unloaded model data from RAM and VRAM"); diff --git a/src/rlgl.h b/src/rlgl.h index 49ab1de54..9e9394db4 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -2036,6 +2036,8 @@ unsigned int rlLoadTexture(void *data, int width, int height, int format, int mi unsigned int rlLoadTextureDepth(int width, int height, int bits, bool useRenderBuffer) { unsigned int id = 0; + +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) unsigned int glInternalFormat = GL_DEPTH_COMPONENT16; if ((bits != 16) && (bits != 24) && (bits != 32)) bits = 16; @@ -2081,6 +2083,7 @@ unsigned int rlLoadTextureDepth(int width, int height, int bits, bool useRenderB glBindRenderbuffer(GL_RENDERBUFFER, 0); } +#endif return id; } @@ -2092,7 +2095,8 @@ unsigned int rlLoadTextureCubemap(void *data, int size, int format) { unsigned int cubemapId = 0; unsigned int dataSize = GetPixelDataSize(size, size, format); - + +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) glGenTextures(1, &cubemapId); glBindTexture(GL_TEXTURE_CUBE_MAP, cubemapId); @@ -2137,6 +2141,7 @@ unsigned int rlLoadTextureCubemap(void *data, int size, int format) #endif glBindTexture(GL_TEXTURE_CUBE_MAP, 0); +#endif return cubemapId; } @@ -2221,9 +2226,9 @@ RenderTexture2D rlLoadRenderTexture(int width, int height, int format, int depth { RenderTexture2D target = { 0 }; +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) if (useDepthTexture && texDepthSupported) target.depthTexture = true; -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) // Create the framebuffer object glGenFramebuffers(1, &target.id); glBindFramebuffer(GL_FRAMEBUFFER, target.id); @@ -2274,6 +2279,7 @@ RenderTexture2D rlLoadRenderTexture(int width, int height, int format, int depth // NOTE: Attach type: 0-Color, 1-Depth renderbuffer, 2-Depth texture void rlRenderTextureAttach(RenderTexture2D target, unsigned int id, int attachType) { +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) glBindFramebuffer(GL_FRAMEBUFFER, target.id); if (attachType == 0) glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, id, 0); @@ -2284,11 +2290,15 @@ void rlRenderTextureAttach(RenderTexture2D target, unsigned int id, int attachTy } glBindFramebuffer(GL_FRAMEBUFFER, 0); +#endif } // Verify render texture is complete bool rlRenderTextureComplete(RenderTexture target) { + bool result = false; + +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) glBindFramebuffer(GL_FRAMEBUFFER, target.id); GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); @@ -2309,7 +2319,10 @@ bool rlRenderTextureComplete(RenderTexture target) glBindFramebuffer(GL_FRAMEBUFFER, 0); - return (status == GL_FRAMEBUFFER_COMPLETE); + result = (status == GL_FRAMEBUFFER_COMPLETE); +#endif + + return result; } // Generate mipmap data for selected texture From 22dece2935ae0fcd67f0d040bdb3f95318495908 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 4 Apr 2019 13:32:17 +0200 Subject: [PATCH 32/74] Animated vertex renaming --- src/rlgl.h | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/rlgl.h b/src/rlgl.h index 9e9394db4..8cc4c5900 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -205,8 +205,8 @@ typedef unsigned char byte; unsigned short *indices;// vertex indices (in case vertex data comes indexed) // Animation vertex data - float *baseVertices; // Vertex base position (required to apply bones transformations) - float *baseNormals; // Vertex base normals (required to apply bones transformations) + float *animVertices; // Animated vertex positions (after bones transformations) + float *animNormals; // Animated normals (after bones transformations) float *weightBias; // Vertex weight bias int *weightId; // Vertex weight id @@ -455,6 +455,7 @@ void rlDeleteVertexArrays(unsigned int id); // Unload vertex data (V void rlDeleteBuffers(unsigned int id); // Unload vertex data (VBO) from GPU memory void rlClearColor(byte r, byte g, byte b, byte a); // Clear color buffer with color void rlClearScreenBuffers(void); // Clear used screen buffers (color and depth) +void rlUpdateBuffer(int bufferId, void *data, int dataSize); // Update GPU buffer with new data //------------------------------------------------------------------------------------ // Functions Declaration - rlgl functionality @@ -1501,6 +1502,13 @@ void rlClearScreenBuffers(void) //glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); // Stencil buffer not used... } +// Update GPU buffer with new data +void rlUpdateBuffer(int bufferId, void *data, int dataSize) +{ + glBindBuffer(GL_ARRAY_BUFFER, bufferId); + glBufferSubData(GL_ARRAY_BUFFER, 0, dataSize, data); +} + //---------------------------------------------------------------------------------- // Module Functions Definition - rlgl Functions //---------------------------------------------------------------------------------- @@ -2788,10 +2796,10 @@ void rlUnloadMesh(Mesh *mesh) free(mesh->texcoords2); free(mesh->indices); - free(mesh->baseVertices); - free(mesh->baseNormals); - free(mesh->weightBias); - free(mesh->weightId); + free(mesh->animVertices); + free(mesh->animNormals); + free(mesh->boneWeights); + free(mesh->boneIds); rlDeleteBuffers(mesh->vboId[0]); // vertex rlDeleteBuffers(mesh->vboId[1]); // texcoords From d89d24c5e8930c18a93a6403e26914a9e2b23b84 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 4 Apr 2019 13:33:54 +0200 Subject: [PATCH 33/74] BIG UPDATE: Support model animations! --- src/config.h | 6 +- src/models.c | 382 ++++++++++++++++++++++++++++++++++++++++++++++----- src/raylib.h | 45 ++++-- 3 files changed, 383 insertions(+), 50 deletions(-) diff --git a/src/config.h b/src/config.h index d45ff7078..4174ed717 100644 --- a/src/config.h +++ b/src/config.h @@ -66,10 +66,10 @@ //------------------------------------------------------------------------------------ // Draw rectangle shapes using font texture white character instead of default white texture // Allows drawing rectangles and text with a single draw call, very useful for GUI systems! -#define SUPPORT_FONT_TEXTURE +#define SUPPORT_FONT_TEXTURE 1 // Use QUADS instead of TRIANGLES for drawing when possible // Some lines-based shapes could still use lines -#define SUPPORT_QUADS_DRAW_MODE +#define SUPPORT_QUADS_DRAW_MODE 1 //------------------------------------------------------------------------------------ // Module: textures - Configuration Flags @@ -114,6 +114,8 @@ // Selected desired model fileformats to be supported for loading #define SUPPORT_FILEFORMAT_OBJ 1 #define SUPPORT_FILEFORMAT_MTL 1 +#define SUPPORT_FILEFORMAT_IQM 1 +#define SUPPORT_FILEFORMAT_GLTF 1 // Support procedural mesh generation functions, uses external par_shapes.h library // NOTE: Some generated meshes DO NOT include generated texture coordinates #define SUPPORT_MESH_GENERATION 1 diff --git a/src/models.c b/src/models.c index 7c266843b..632aca2bd 100644 --- a/src/models.c +++ b/src/models.c @@ -57,11 +57,6 @@ #include "external/tinyobj_loader_c.h" // OBJ/MTL file formats loading #endif -#if defined(SUPPORT_FILEFORMAT_IQM) - #define RIQM_IMPLEMENTATION - #include "external/riqm.h" // IQM file format loading -#endif - #if defined(SUPPORT_FILEFORMAT_GLTF) #define CGLTF_IMPLEMENTATION #include "external/cgltf.h" // glTF file format loading @@ -629,6 +624,9 @@ Model LoadModel(const char *fileName) #if defined(SUPPORT_FILEFORMAT_IQM) if (IsFileExtension(fileName, ".iqm")) model = LoadIQM(fileName); #endif + + // Make sure model transform is set to identity matrix! + model.transform = MatrixIdentity(); if (model.meshCount == 0) { @@ -638,7 +636,12 @@ Model LoadModel(const char *fileName) model.meshes = (Mesh *)calloc(model.meshCount, sizeof(Mesh)); model.meshes[0] = GenMeshCube(1.0f, 1.0f, 1.0f); } - + else + { + // Upload vertex data to GPU (static mesh) + for (int i = 0; i < model.meshCount; i++) rlLoadMesh(&model.meshes[i], false); + } + if (model.materialCount == 0) { TraceLog(LOG_WARNING, "[%s] No materials can be loaded, default to white material", fileName); @@ -686,32 +689,14 @@ void UnloadModel(Model model) free(model.meshes); free(model.materials); free(model.meshMaterial); + + // Unload animation data + free(model.bones); + free(model.bindPose); TraceLog(LOG_INFO, "Unloaded model data from RAM and VRAM"); } -// Load mesh from file -// NOTE: Mesh data loaded in CPU and GPU -Mesh LoadMesh(const char *fileName) -{ - Mesh mesh = { 0 }; - - // TODO: Review this function, should still exist? - -#if defined(SUPPORT_MESH_GENERATION) - if (mesh.vertexCount == 0) - { - TraceLog(LOG_WARNING, "Mesh could not be loaded! Let's load a cube to replace it!"); - mesh = GenMeshCube(1.0f, 1.0f, 1.0f); - } - else rlLoadMesh(&mesh, false); // Upload vertex data to GPU (static mesh) -#else - rlLoadMesh(&mesh, false); // Upload vertex data to GPU (static mesh) -#endif - - return mesh; -} - // Unload mesh from memory (RAM and/or VRAM) void UnloadMesh(Mesh *mesh) { @@ -2386,7 +2371,6 @@ void MeshBinormals(Mesh *mesh) static Model LoadOBJ(const char *fileName) { Model model = { 0 }; - model.transform = MatrixIdentity(); tinyobj_attrib_t attrib; tinyobj_shape_t *meshes = NULL; @@ -2486,8 +2470,7 @@ static Model LoadOBJ(const char *fileName) } model.meshes[m] = mesh; // Assign mesh data to model - rlLoadMesh(&model.meshes[m], false); // Upload vertex data to GPU (static mesh) - + // Assign mesh material for current mesh model.meshMaterial[m] = attrib.material_ids[m]; } @@ -2555,13 +2538,336 @@ static Model LoadOBJ(const char *fileName) } #endif -#if defined(SUPPORT_FILEFORMAT_GLTF) +#if defined(SUPPORT_FILEFORMAT_IQM) // Load IQM mesh data static Model LoadIQM(const char *fileName) { + #define IQM_MAGIC "INTERQUAKEMODEL" // IQM file magic number + #define IQM_VERSION 2 // only IQM version 2 supported + + #define BONE_NAME_LENGTH 32 // BoneInfo name string length + #define MESH_NAME_LENGTH 32 // Mesh name string length + + // IQM file structs + //----------------------------------------------------------------------------------- + typedef struct IQMHeader { + char magic[16]; + unsigned int version; + unsigned int filesize; + unsigned int flags; + unsigned int num_text, ofs_text; + unsigned int num_meshes, ofs_meshes; + unsigned int num_vertexarrays, num_vertexes, ofs_vertexarrays; + unsigned int num_triangles, ofs_triangles, ofs_adjacency; + unsigned int num_joints, ofs_joints; + unsigned int num_poses, ofs_poses; + unsigned int num_anims, ofs_anims; + unsigned int num_frames, num_framechannels, ofs_frames, ofs_bounds; + unsigned int num_comment, ofs_comment; + unsigned int num_extensions, ofs_extensions; + } IQMHeader; + + typedef struct IQMMesh { + unsigned int name; + unsigned int material; + unsigned int first_vertex, num_vertexes; + unsigned int first_triangle, num_triangles; + } IQMMesh; + + typedef struct IQMTriangle { + unsigned int vertex[3]; + } IQMTriangle; + + // NOTE: Adjacency unused by default + typedef struct IQMAdjacency { + unsigned int triangle[3]; + } IQMAdjacency; + + typedef struct IQMJoint { + unsigned int name; + int parent; + float translate[3], rotate[4], scale[3]; + } IQMJoint; + + typedef struct IQMPose { + int parent; + unsigned int mask; + float channeloffset[10]; + float channelscale[10]; + } IQMPose; + + typedef struct IQMAnim { + unsigned int name; + unsigned int first_frame, num_frames; + float framerate; + unsigned int flags; + } IQMAnim; + + typedef struct IQMVertexArray { + unsigned int type; + unsigned int flags; + unsigned int format; + unsigned int size; + unsigned int offset; + } IQMVertexArray; + + // NOTE: Bounds unused by default + typedef struct IQMBounds { + float bbmin[3], bbmax[3]; + float xyradius, radius; + } IQMBounds; + //----------------------------------------------------------------------------------- + + // IQM vertex data types + typedef enum { + IQM_POSITION = 0, + IQM_TEXCOORD = 1, + IQM_NORMAL = 2, + IQM_TANGENT = 3, // NOTE: Tangents unused by default + IQM_BLENDINDEXES = 4, + IQM_BLENDWEIGHTS = 5, + IQM_COLOR = 6, // NOTE: Vertex colors unused by default + IQM_CUSTOM = 0x10 // NOTE: Custom vertex values unused by default + } IQMVertexType; + Model model = { 0 }; - // TODO: Load IQM file + FILE *iqmFile; + IQMHeader iqm; + + IQMMesh *imesh; + IQMTriangle *tri; + IQMVertexArray *va; + IQMJoint *ijoint; + + float *vertex = NULL; + float *normal = NULL; + float *text = NULL; + char *blendi = NULL; + unsigned char *blendw = NULL; + + iqmFile = fopen(fileName, "rb"); + + if (iqmFile == NULL) + { + TraceLog(LOG_WARNING, "[%s] IQM file could not be opened", fileName); + return model; + } + + fread(&iqm,sizeof(IQMHeader), 1, iqmFile); // Read IQM header + + if (strncmp(iqm.magic, IQM_MAGIC, sizeof(IQM_MAGIC))) + { + TraceLog(LOG_WARNING, "[%s] IQM file does not seem to be valid", fileName); + fclose(iqmFile); + return model; + } + + if (iqm.version != IQM_VERSION) + { + TraceLog(LOG_WARNING, "[%s] IQM file version is not supported (%i).", fileName, iqm.version); + fclose(iqmFile); + return model; + } + + // Meshes data processing + imesh = malloc(sizeof(IQMMesh)*iqm.num_meshes); + fseek(iqmFile, iqm.ofs_meshes, SEEK_SET); + fread(imesh, sizeof(IQMMesh)*iqm.num_meshes, 1, iqmFile); + + model.meshCount = iqm.num_meshes; + model.meshes = malloc(sizeof(Mesh)*iqm.num_meshes); + + char name[MESH_NAME_LENGTH]; + + for (int i = 0; i < iqm.num_meshes; i++) + { + fseek(iqmFile,iqm.ofs_text+imesh[i].name,SEEK_SET); + fread(name, sizeof(char)*MESH_NAME_LENGTH, 1, iqmFile); // Mesh name not used... + model.meshes[i].vertexCount = imesh[i].num_vertexes; + + model.meshes[i].vertices = malloc(sizeof(float)*imesh[i].num_vertexes*3); // Default vertex positions + model.meshes[i].normals = malloc(sizeof(float)*imesh[i].num_vertexes*3); // Default vertex normals + model.meshes[i].texcoords = malloc(sizeof(float)*imesh[i].num_vertexes*2); // Default vertex texcoords + + model.meshes[i].boneIds = malloc(sizeof(int)*imesh[i].num_vertexes*4); // Up-to 4 bones supported! + model.meshes[i].boneWeights = malloc(sizeof(float)*imesh[i].num_vertexes*4); // Up-to 4 bones supported! + + model.meshes[i].triangleCount = imesh[i].num_triangles; + model.meshes[i].indices = malloc(sizeof(unsigned short)*imesh[i].num_triangles*3); + + // Animated verted data, what we actually process for rendering + // NOTE: Animated vertex should be re-uploaded to GPU (if not using GPU skinning) + model.meshes[i].animVertices = malloc(sizeof(float)*imesh[i].num_vertexes*3); + model.meshes[i].animNormals = malloc(sizeof(float)*imesh[i].num_vertexes*3); + } + + // Triangles data processing + tri = malloc(sizeof(IQMTriangle)*iqm.num_triangles); + fseek(iqmFile, iqm.ofs_triangles, SEEK_SET); + fread(tri, sizeof(IQMTriangle)*iqm.num_triangles, 1, iqmFile); + + for (int m = 0; m < iqm.num_meshes; m++) + { + int tcounter = 0; + + for (int i = imesh[m].first_triangle; i < imesh[m].first_triangle+imesh[m].num_triangles; i++) + { + // IQM triangles are stored counter clockwise, but raylib sets opengl to clockwise drawing, so we swap them around + model.meshes[m].indices[tcounter+2] = tri[i].vertex[0] - imesh[m].first_vertex; + model.meshes[m].indices[tcounter+1] = tri[i].vertex[1] - imesh[m].first_vertex; + model.meshes[m].indices[tcounter] = tri[i].vertex[2] - imesh[m].first_vertex; + tcounter += 3; + } + } + + // Vertex arrays data processing + va = malloc(sizeof(IQMVertexArray)*iqm.num_vertexarrays); + fseek(iqmFile, iqm.ofs_vertexarrays, SEEK_SET); + fread(va, sizeof(IQMVertexArray)*iqm.num_vertexarrays, 1, iqmFile); + + for (int i = 0; i < iqm.num_vertexarrays; i++) + { + switch (va[i].type) + { + case IQM_POSITION: + { + vertex = malloc(sizeof(float)*iqm.num_vertexes*3); + fseek(iqmFile, va[i].offset, SEEK_SET); + fread(vertex, sizeof(float)*iqm.num_vertexes*3, 1, iqmFile); + + for (int m = 0; m < iqm.num_meshes; m++) + { + int vCounter = 0; + for (int i = imesh[m].first_vertex*3; i < (imesh[m].first_vertex + imesh[m].num_vertexes)*3; i++) + { + model.meshes[m].vertices[vCounter] = vertex[i]; + model.meshes[m].animVertices[vCounter] = vertex[i]; + vCounter++; + } + } + } break; + case IQM_NORMAL: + { + normal = malloc(sizeof(float)*iqm.num_vertexes*3); + fseek(iqmFile, va[i].offset, SEEK_SET); + fread(normal, sizeof(float)*iqm.num_vertexes*3, 1, iqmFile); + + for (int m = 0; m < iqm.num_meshes; m++) + { + int vCounter = 0; + for (int i = imesh[m].first_vertex*3; i < (imesh[m].first_vertex + imesh[m].num_vertexes)*3; i++) + { + model.meshes[m].normals[vCounter] = normal[i]; + model.meshes[m].animNormals[vCounter] = normal[i]; + vCounter++; + } + } + } break; + case IQM_TEXCOORD: + { + text = malloc(sizeof(float)*iqm.num_vertexes*2); + fseek(iqmFile, va[i].offset, SEEK_SET); + fread(text, sizeof(float)*iqm.num_vertexes*2, 1, iqmFile); + + for (int m = 0; m < iqm.num_meshes; m++) + { + int vCounter = 0; + for (int i = imesh[m].first_vertex*2; i < (imesh[m].first_vertex + imesh[m].num_vertexes)*2; i++) + { + model.meshes[m].texcoords[vCounter] = text[i]; + vCounter++; + } + } + } break; + case IQM_BLENDINDEXES: + { + blendi = malloc(sizeof(char)*iqm.num_vertexes*4); + fseek(iqmFile, va[i].offset, SEEK_SET); + fread(blendi, sizeof(char)*iqm.num_vertexes*4, 1, iqmFile); + + for (int m = 0; m < iqm.num_meshes; m++) + { + int boneCounter = 0; + for (int i = imesh[m].first_vertex*4; i < (imesh[m].first_vertex + imesh[m].num_vertexes)*4; i++) + { + model.meshes[m].boneIds[boneCounter] = blendi[i]; + boneCounter++; + } + } + } break; + case IQM_BLENDWEIGHTS: + { + blendw = malloc(sizeof(unsigned char)*iqm.num_vertexes*4); + fseek(iqmFile,va[i].offset,SEEK_SET); + fread(blendw,sizeof(unsigned char)*iqm.num_vertexes*4,1,iqmFile); + + for (int m = 0; m < iqm.num_meshes; m++) + { + int boneCounter = 0; + for (int i = imesh[m].first_vertex*4; i < (imesh[m].first_vertex + imesh[m].num_vertexes)*4; i++) + { + model.meshes[m].boneWeights[boneCounter] = blendw[i]/255.0f; + boneCounter++; + } + } + } break; + } + } + + // Bones (joints) data processing + ijoint = malloc(sizeof(IQMJoint)*iqm.num_joints); + fseek(iqmFile, iqm.ofs_joints, SEEK_SET); + fread(ijoint, sizeof(IQMJoint)*iqm.num_joints, 1, iqmFile); + + model.boneCount = iqm.num_joints; + model.bones = malloc(sizeof(BoneInfo)*iqm.num_joints); + model.bindPose = malloc(sizeof(Transform)*iqm.num_joints); + + for (int i = 0; i < iqm.num_joints; i++) + { + // Bones + model.bones[i].parent = ijoint[i].parent; + fseek(iqmFile, iqm.ofs_text + ijoint[i].name, SEEK_SET); + fread(model.bones[i].name,sizeof(char)*BONE_NAME_LENGTH, 1, iqmFile); + + // Bind pose (base pose) + model.bindPose[i].translation.x = ijoint[i].translate[0]; + model.bindPose[i].translation.y = ijoint[i].translate[1]; + model.bindPose[i].translation.z = ijoint[i].translate[2]; + + model.bindPose[i].rotation.x = ijoint[i].rotate[0]; + model.bindPose[i].rotation.y = ijoint[i].rotate[1]; + model.bindPose[i].rotation.z = ijoint[i].rotate[2]; + model.bindPose[i].rotation.w = ijoint[i].rotate[3]; + + model.bindPose[i].scale.x = ijoint[i].scale[0]; + model.bindPose[i].scale.y = ijoint[i].scale[1]; + model.bindPose[i].scale.z = ijoint[i].scale[2]; + } + + // Build bind pose from parent joints + for (int i = 0; i < model.boneCount; i++) + { + if (model.bones[i].parent >= 0) + { + model.bindPose[i].rotation = QuaternionMultiply(model.bindPose[model.bones[i].parent].rotation, model.bindPose[i].rotation); + model.bindPose[i].translation = Vector3RotateByQuaternion(model.bindPose[i].translation, model.bindPose[model.bones[i].parent].rotation); + model.bindPose[i].translation = Vector3Add(model.bindPose[i].translation, model.bindPose[model.bones[i].parent].translation); + model.bindPose[i].scale = Vector3MultiplyV(model.bindPose[i].scale, model.bindPose[model.bones[i].parent].scale); + } + } + + fclose(iqmFile); + free(imesh); + free(tri); + free(va); + free(vertex); + free(normal); + free(text); + free(blendi); + free(blendw); + free(ijoint); return model; } @@ -2593,23 +2899,23 @@ static Model LoadGLTF(const char *fileName) // glTF data loading cgltf_options options = {0}; - cgltf_data data; + cgltf_data *data; cgltf_result result = cgltf_parse(&options, buffer, size, &data); free(buffer); if (result == cgltf_result_success) { - printf("Type: %u\n", data.file_type); - printf("Version: %d\n", data.version); - printf("Meshes: %lu\n", data.meshes_count); + // printf("Type: %u\n", data.file_type); + // printf("Version: %d\n", data.version); + // printf("Meshes: %lu\n", data.meshes_count); // TODO: Process glTF data and map to model // NOTE: data.buffers[] should be loaded to model.meshes and data.images[] should be loaded to model.materials // Use buffers[n].uri and images[n].uri... or use cgltf_load_buffers(&options, data, fileName); - cgltf_free(&data); + cgltf_free(data); } else TraceLog(LOG_WARNING, "[%s] glTF data could not be loaded", fileName); diff --git a/src/raylib.h b/src/raylib.h index 085f36f63..9607191b8 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -307,10 +307,10 @@ typedef struct Mesh { unsigned short *indices;// Vertex indices (in case vertex data comes indexed) // Animation vertex data - float *baseVertices; // Vertex base position (required to apply bones transformations) - float *baseNormals; // Vertex base normals (required to apply bones transformations) - float *weightBias; // Vertex weight bias - int *weightId; // Vertex weight id + float *animVertices; // Animated vertex positions (after bones transformations) + float *animNormals; // Animated normals (after bones transformations) + int *boneIds; // Vertex bone ids, up to 4 bones influence by vertex (skinning) + float *boneWeights; // Vertex bone weight, up to 4 bones influence by vertex (skinning) // OpenGL identifiers unsigned int vaoId; // OpenGL Vertex Array Object id @@ -337,6 +337,19 @@ typedef struct Material { float *params; // Material generic parameters (if required) } Material; +// Transformation properties +typedef struct Transform { + Vector3 translation; // Translation + Quaternion rotation; // Rotation + Vector3 scale; // Scale +} Transform; + +// Bone information +typedef struct BoneInfo { + char name[32]; // Bone name + int parent; // Bone parent +} BoneInfo; + // Model type typedef struct Model { Matrix transform; // Local transform matrix @@ -346,10 +359,23 @@ typedef struct Model { int materialCount; // Number of materials Material *materials; // Materials array - int *meshMaterial; // Mesh material number + + // Animation data + int boneCount; // Number of bones + BoneInfo *bones; // Bones information (skeleton) + Transform *bindPose; // Bones base transformation (pose) } Model; +// Model animation +typedef struct ModelAnimation { + int boneCount; // Number of bones + BoneInfo *bones; // Bones information (skeleton) + + int frameCount; // Number of animation frames + Transform **framePoses; // Poses array by frame +} ModelAnimation; + // Ray type (useful for raycast) typedef struct Ray { Vector3 position; // Ray position (origin) @@ -1228,17 +1254,16 @@ RLAPI void DrawGizmo(Vector3 position); // Model loading/unloading functions RLAPI Model LoadModel(const char *fileName); // Load model from files (meshes and materials) RLAPI Model LoadModelFromMesh(Mesh mesh); // Load model from generated mesh +//RLAPI void LoadModelAnimations(const char fileName, ModelAnimation *anims, int *animsCount); // Load model animations from file +//RLAPI void UpdateModelAnimation(Model model, ModelAnimation anim, int frame); // Update model animation pose RLAPI void UnloadModel(Model model); // Unload model from memory (RAM and/or VRAM) -// Mesh loading/unloading functions -RLAPI Mesh LoadMesh(const char *fileName); // Load mesh from file -RLAPI void UnloadMesh(Mesh *mesh); // Unload mesh from memory (RAM and/or VRAM) -RLAPI void ExportMesh(Mesh mesh, const char *fileName); // Export mesh data to file - // Mesh manipulation functions RLAPI BoundingBox MeshBoundingBox(Mesh mesh); // Compute mesh bounding box limits RLAPI void MeshTangents(Mesh *mesh); // Compute mesh tangents RLAPI void MeshBinormals(Mesh *mesh); // Compute mesh binormals +RLAPI void UnloadMesh(Mesh *mesh); // Unload mesh from memory (RAM and/or VRAM) +RLAPI void ExportMesh(Mesh mesh, const char *fileName); // Export mesh data to file // Mesh generation functions RLAPI Mesh GenMeshPoly(int sides, float radius); // Generate polygonal mesh From 3e1e7d740fd378df03346e309ba187b6f7a20daa Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 4 Apr 2019 13:50:28 +0200 Subject: [PATCH 34/74] Review merged PR formatting Removed trail spaces --- src/shapes.c | 352 ++++++++++++++++++++++++++------------------------- 1 file changed, 182 insertions(+), 170 deletions(-) diff --git a/src/shapes.c b/src/shapes.c index 879d2a107..b7f7e3df5 100644 --- a/src/shapes.c +++ b/src/shapes.c @@ -186,31 +186,31 @@ void DrawCircle(int centerX, int centerY, float radius, Color color) void DrawCircleSector(Vector2 center, float radius, int startAngle, int endAngle, int segments, Color color) { if (radius <= 0.0f) radius = 0.1f; // Avoid div by zero - + // Function expects (endAngle > startAngle) - if (endAngle < startAngle) + if (endAngle < startAngle) { // Swap values int tmp = startAngle; startAngle = endAngle; endAngle = tmp; } - + if (segments < 4) { // Calculate how many segments we need to draw a smooth circle, taken from https://stackoverflow.com/a/2244088 #define CIRCLE_ERROR_RATE 0.5f - + // Calculate the maximum angle between segments based on the error rate. float th = acosf(2*powf(1 - CIRCLE_ERROR_RATE/radius, 2) - 1); segments = (endAngle - startAngle)*ceilf(2*PI/th)/360; - + if (segments <= 0) segments = 4; } - + float stepLength = (float)(endAngle - startAngle)/(float)segments; float angle = startAngle; - + #if defined(SUPPORT_QUADS_DRAW_MODE) if (rlCheckBufferLimit(4*segments/2)) rlglDraw(); @@ -227,16 +227,16 @@ void DrawCircleSector(Vector2 center, float radius, int startAngle, int endAngle rlTexCoord2f(recTexShapes.x/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height); rlVertex2f(center.x + sinf(DEG2RAD*angle)*radius, center.y + cosf(DEG2RAD*angle)*radius); - + rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height); rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*radius, center.y + cosf(DEG2RAD*(angle + stepLength))*radius); rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, recTexShapes.y/texShapes.height); rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength*2))*radius, center.y + cosf(DEG2RAD*(angle + stepLength*2))*radius); - + angle += (stepLength*2); } - + // NOTE: In case number of segments is odd, we add one last piece to the cake if (segments%2) { @@ -247,7 +247,7 @@ void DrawCircleSector(Vector2 center, float radius, int startAngle, int endAngle rlTexCoord2f(recTexShapes.x/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height); rlVertex2f(center.x + sinf(DEG2RAD*angle)*radius, center.y + cosf(DEG2RAD*angle)*radius); - + rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height); rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*radius, center.y + cosf(DEG2RAD*(angle + stepLength))*radius); @@ -268,7 +268,7 @@ void DrawCircleSector(Vector2 center, float radius, int startAngle, int endAngle rlVertex2f(center.x, center.y); rlVertex2f(center.x + sinf(DEG2RAD*angle)*radius, center.y + cosf(DEG2RAD*angle)*radius); rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*radius, center.y + cosf(DEG2RAD*(angle + stepLength))*radius); - + angle += stepLength; } rlEnd(); @@ -278,40 +278,40 @@ void DrawCircleSector(Vector2 center, float radius, int startAngle, int endAngle void DrawCircleSectorLines(Vector2 center, float radius, int startAngle, int endAngle, int segments, Color color) { if (radius <= 0.0f) radius = 0.1f; // Avoid div by zero issue - + // Function expects (endAngle > startAngle) - if (endAngle < startAngle) + if (endAngle < startAngle) { // Swap values int tmp = startAngle; startAngle = endAngle; endAngle = tmp; } - + if (segments < 4) { // Calculate how many segments we need to draw a smooth circle, taken from https://stackoverflow.com/a/2244088 #ifndef CIRCLE_ERROR_RATE #define CIRCLE_ERROR_RATE 0.5f #endif - + // Calculate the maximum angle between segments based on the error rate. float th = acosf(2*powf(1 - CIRCLE_ERROR_RATE/radius, 2) - 1); segments = (endAngle - startAngle)*ceilf(2*PI/th)/360; - + if (segments <= 0) segments = 4; } - + float stepLength = (float)(endAngle - startAngle)/(float)segments; float angle = startAngle; - + // Hide the cap lines when the circle is full bool showCapLines = true; int limit = 2*(segments + 2); if ((endAngle - startAngle)%360 == 0) { limit = 2*segments; showCapLines = false; } - + if (rlCheckBufferLimit(limit)) rlglDraw(); - + rlBegin(RL_LINES); if (showCapLines) { @@ -319,17 +319,17 @@ void DrawCircleSectorLines(Vector2 center, float radius, int startAngle, int end rlVertex2f(center.x, center.y); rlVertex2f(center.x + sinf(DEG2RAD*angle)*radius, center.y + cosf(DEG2RAD*angle)*radius); } - + for (int i = 0; i < segments; i++) { rlColor4ub(color.r, color.g, color.b, color.a); - + rlVertex2f(center.x + sinf(DEG2RAD*angle)*radius, center.y + cosf(DEG2RAD*angle)*radius); rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*radius, center.y + cosf(DEG2RAD*(angle + stepLength))*radius); - + angle += stepLength; } - + if (showCapLines) { rlColor4ub(color.r, color.g, color.b, color.a); @@ -385,55 +385,55 @@ void DrawCircleLines(int centerX, int centerY, float radius, Color color) void DrawRing(Vector2 center, float innerRadius, float outerRadius, int startAngle, int endAngle, int segments, Color color) { if (startAngle == endAngle) return; - + // Function expects (outerRadius > innerRadius) if (outerRadius < innerRadius) { float tmp = outerRadius; outerRadius = innerRadius; innerRadius = tmp; - + if (outerRadius <= 0.0f) outerRadius = 0.1f; } - + // Function expects (endAngle > startAngle) - if (endAngle < startAngle) + if (endAngle < startAngle) { // Swap values int tmp = startAngle; startAngle = endAngle; endAngle = tmp; } - + if (segments < 4) { // Calculate how many segments we need to draw a smooth circle, taken from https://stackoverflow.com/a/2244088 #ifndef CIRCLE_ERROR_RATE #define CIRCLE_ERROR_RATE 0.5f #endif - + // Calculate the maximum angle between segments based on the error rate. float th = acosf(2*powf(1 - CIRCLE_ERROR_RATE/outerRadius, 2) - 1); segments = (endAngle - startAngle)*ceilf(2*PI/th)/360; - + if (segments <= 0) segments = 4; } - + // Not a ring - if (innerRadius <= 0.0f) + if (innerRadius <= 0.0f) { DrawCircleSector(center, outerRadius, startAngle, endAngle, segments, color); return; } - + float stepLength = (float)(endAngle - startAngle)/(float)segments; float angle = startAngle; - + #if defined(SUPPORT_QUADS_DRAW_MODE) if (rlCheckBufferLimit(4*segments)) rlglDraw(); rlEnableTexture(GetShapesTexture().id); - + rlBegin(RL_QUADS); for (int i = 0; i < segments; i++) { @@ -444,13 +444,13 @@ void DrawRing(Vector2 center, float innerRadius, float outerRadius, int startAng rlTexCoord2f(recTexShapes.x/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height); rlVertex2f(center.x + sinf(DEG2RAD*angle)*outerRadius, center.y + cosf(DEG2RAD*angle)*outerRadius); - + rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height); rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*outerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*outerRadius); rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, recTexShapes.y/texShapes.height); rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*innerRadius); - + angle += stepLength; } rlEnd(); @@ -463,15 +463,15 @@ void DrawRing(Vector2 center, float innerRadius, float outerRadius, int startAng for (int i = 0; i < segments; i++) { rlColor4ub(color.r, color.g, color.b, color.a); - + rlVertex2f(center.x + sinf(DEG2RAD*angle)*innerRadius, center.y + cosf(DEG2RAD*angle)*innerRadius); rlVertex2f(center.x + sinf(DEG2RAD*angle)*outerRadius, center.y + cosf(DEG2RAD*angle)*outerRadius); rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*innerRadius); - + rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*innerRadius); rlVertex2f(center.x + sinf(DEG2RAD*angle)*outerRadius, center.y + cosf(DEG2RAD*angle)*outerRadius); rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*outerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*outerRadius); - + angle += stepLength; } rlEnd(); @@ -481,55 +481,55 @@ void DrawRing(Vector2 center, float innerRadius, float outerRadius, int startAng void DrawRingLines(Vector2 center, float innerRadius, float outerRadius, int startAngle, int endAngle, int segments, Color color) { if (startAngle == endAngle) return; - + // Function expects (outerRadius > innerRadius) if (outerRadius < innerRadius) { float tmp = outerRadius; outerRadius = innerRadius; innerRadius = tmp; - + if (outerRadius <= 0.0f) outerRadius = 0.1f; } - + // Function expects (endAngle > startAngle) - if (endAngle < startAngle) + if (endAngle < startAngle) { // Swap values int tmp = startAngle; startAngle = endAngle; endAngle = tmp; } - + if (segments < 4) { // Calculate how many segments we need to draw a smooth circle, taken from https://stackoverflow.com/a/2244088 #ifndef CIRCLE_ERROR_RATE #define CIRCLE_ERROR_RATE 0.5f #endif - + // Calculate the maximum angle between segments based on the error rate. float th = acosf(2*powf(1 - CIRCLE_ERROR_RATE/outerRadius, 2) - 1); segments = (endAngle - startAngle)*ceilf(2*PI/th)/360; - + if (segments <= 0) segments = 4; } - - if (innerRadius <= 0.0f) + + if (innerRadius <= 0.0f) { DrawCircleSectorLines(center, outerRadius, startAngle, endAngle, segments, color); return; } - + float stepLength = (float)(endAngle - startAngle)/(float)segments; - float angle = startAngle; - + float angle = startAngle; + bool showCapLines = true; int limit = 4*(segments + 1); if ((endAngle - startAngle)%360 == 0) { limit = 4*segments; showCapLines = false; } - + if (rlCheckBufferLimit(limit)) rlglDraw(); - + rlBegin(RL_LINES); if (showCapLines) { @@ -537,20 +537,20 @@ void DrawRingLines(Vector2 center, float innerRadius, float outerRadius, int sta rlVertex2f(center.x + sinf(DEG2RAD*angle)*outerRadius, center.y + cosf(DEG2RAD*angle)*outerRadius); rlVertex2f(center.x + sinf(DEG2RAD*angle)*innerRadius, center.y + cosf(DEG2RAD*angle)*innerRadius); } - + for (int i = 0; i < segments; i++) { rlColor4ub(color.r, color.g, color.b, color.a); - + rlVertex2f(center.x + sinf(DEG2RAD*angle)*outerRadius, center.y + cosf(DEG2RAD*angle)*outerRadius); rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*outerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*outerRadius); - + rlVertex2f(center.x + sinf(DEG2RAD*angle)*innerRadius, center.y + cosf(DEG2RAD*angle)*innerRadius); rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*innerRadius); - + angle += stepLength; } - + if (showCapLines) { rlColor4ub(color.r, color.g, color.b, color.a); @@ -698,22 +698,22 @@ void DrawRectangleLinesEx(Rectangle rec, int lineThick, Color color) DrawRectangle( (int)rec.x, (int)(rec.y + lineThick), lineThick, (int)(rec.height - lineThick*2), color); } -// Draw rectangle with rounded edges. -void DrawRoundedRect(Rectangle rec, float roundness, int segments, Color color) +// Draw rectangle with rounded edges +void DrawRectangleRounded(Rectangle rec, float roundness, int segments, Color color) { // Not a rounded rectangle - if(roundness <= 0.0f || rec.width < 1 || rec.height < 1 ) + if ((roundness <= 0.0f) || (rec.width < 1) || (rec.height < 1 )) { DrawRectangleRec(rec, color); return; } - - if(roundness >= 1.0f) roundness = 1.0f; - + + if (roundness >= 1.0f) roundness = 1.0f; + // Calculate corner radius - float radius = rec.width > rec.height ? (rec.height*roundness)/2 : (rec.width*roundness)/2; - if(radius <= 0.0f) return; - + float radius = (rec.width > rec.height)? (rec.height*roundness)/2 : (rec.width*roundness)/2; + if (radius <= 0.0f) return; + // Calculate number of segments to use for the corners if (segments < 4) { @@ -726,9 +726,9 @@ void DrawRoundedRect(Rectangle rec, float roundness, int segments, Color color) segments = ceilf(2*PI/th)/4; if (segments <= 0) segments = 4; } - + float stepLength = 90.0f/(float)segments; - + /* Quick sketch to make sense of all of this (there are 9 parts to draw, also mark the 12 points we'll use below) * Not my best attempt at ASCII art, just preted it's rounded rectangle :) * P0 P1 @@ -744,7 +744,7 @@ void DrawRoundedRect(Rectangle rec, float roundness, int segments, Color color) * \|____________________|/ * P5 P4 */ - + const Vector2 point[12] = { // coordinates of the 12 points that define the rounded rect (the idea here is to make things easier) {(float)rec.x + radius, rec.y}, {(float)(rec.x + rec.width) - radius, rec.y}, { rec.x + rec.width, (float)rec.y + radius }, // PO, P1, P2 {rec.x + rec.width, (float)(rec.y + rec.height) - radius}, {(float)(rec.x + rec.width) - radius, rec.y + rec.height}, // P3, P4 @@ -752,15 +752,16 @@ void DrawRoundedRect(Rectangle rec, float roundness, int segments, Color color) {(float)rec.x + radius, (float)rec.y + radius}, {(float)(rec.x + rec.width) - radius, (float)rec.y + radius}, // P8, P9 {(float)(rec.x + rec.width) - radius, (float)(rec.y + rec.height) - radius}, {(float)rec.x + radius, (float)(rec.y + rec.height) - radius} // P10, P11 }; + const Vector2 centers[4] = { point[8], point[9], point[10], point[11] }; - const float angles[4] = {180.0f, 90.0f, 0.0f, 270.0f }; + const float angles[4] = { 180.0f, 90.0f, 0.0f, 270.0f }; #if defined(SUPPORT_QUADS_DRAW_MODE) if (rlCheckBufferLimit(16*segments/2 + 5*4)) rlglDraw(); - + rlBegin(RL_QUADS); - // Draw all of the 4 corners: [1] Upper Left Corner, [3] Upper Right Corner, [5] Lower Right Corner, [7] Lower Left Corner - for(int k = 0; k < 4; ++k) // Hope the compiler is smart enough to unroll this loop + // Draw all of the 4 corners: [1] Upper Left Corner, [3] Upper Right Corner, [5] Lower Right Corner, [7] Lower Left Corner + for (int k = 0; k < 4; ++k) // Hope the compiler is smart enough to unroll this loop { float angle = angles[k]; const Vector2 center = centers[k]; @@ -784,49 +785,49 @@ void DrawRoundedRect(Rectangle rec, float roundness, int segments, Color color) rlVertex2f(center.x, center.y); } } - + // [2] Upper Rectangle rlColor4ub(color.r, color.g, color.b, color.a); rlVertex2f(point[0].x, point[0].y); rlVertex2f(point[8].x, point[8].y); rlVertex2f(point[9].x, point[9].y); rlVertex2f(point[1].x, point[1].y); - + // [4] Right Rectangle rlColor4ub(color.r, color.g, color.b, color.a); rlVertex2f(point[2].x, point[2].y); rlVertex2f(point[9].x, point[9].y); rlVertex2f(point[10].x, point[10].y); rlVertex2f(point[3].x, point[3].y); - + // [6] Bottom Rectangle rlColor4ub(color.r, color.g, color.b, color.a); rlVertex2f(point[11].x, point[11].y); rlVertex2f(point[5].x, point[5].y); rlVertex2f(point[4].x, point[4].y); rlVertex2f(point[10].x, point[10].y); - + // [8] Left Rectangle rlColor4ub(color.r, color.g, color.b, color.a); rlVertex2f(point[7].x, point[7].y); rlVertex2f(point[6].x, point[6].y); rlVertex2f(point[11].x, point[11].y); rlVertex2f(point[8].x, point[8].y); - + // [9] Middle Rectangle rlColor4ub(color.r, color.g, color.b, color.a); rlVertex2f(point[8].x, point[8].y); rlVertex2f(point[11].x, point[11].y); rlVertex2f(point[10].x, point[10].y); rlVertex2f(point[9].x, point[9].y); - + rlEnd(); #else if (rlCheckBufferLimit(12*segments + 5*6)) rlglDraw(); // 4 corners with 3 vertices per segment + 5 rectangles with 6 vertices each - + rlBegin(RL_TRIANGLES); - // Draw all of the 4 corners: [1] Upper Left Corner, [3] Upper Right Corner, [5] Lower Right Corner, [7] Lower Left Corner - for(int k = 0; k < 4; ++k) // Hope the compiler is smart enough to unroll this loop + // Draw all of the 4 corners: [1] Upper Left Corner, [3] Upper Right Corner, [5] Lower Right Corner, [7] Lower Left Corner + for (int k = 0; k < 4; ++k) // Hope the compiler is smart enough to unroll this loop { float angle = angles[k]; const Vector2 center = centers[k]; @@ -848,7 +849,7 @@ void DrawRoundedRect(Rectangle rec, float roundness, int segments, Color color) rlVertex2f(point[1].x, point[1].y); rlVertex2f(point[0].x, point[0].y); rlVertex2f(point[9].x, point[9].y); - + // [4] Right Rectangle rlColor4ub(color.r, color.g, color.b, color.a); rlVertex2f(point[9].x, point[9].y); @@ -857,7 +858,7 @@ void DrawRoundedRect(Rectangle rec, float roundness, int segments, Color color) rlVertex2f(point[2].x, point[2].y); rlVertex2f(point[9].x, point[9].y); rlVertex2f(point[3].x, point[3].y); - + // [6] Bottom Rectangle rlColor4ub(color.r, color.g, color.b, color.a); rlVertex2f(point[11].x, point[11].y); @@ -866,7 +867,7 @@ void DrawRoundedRect(Rectangle rec, float roundness, int segments, Color color) rlVertex2f(point[10].x, point[10].y); rlVertex2f(point[11].x, point[11].y); rlVertex2f(point[4].x, point[4].y); - + // [8] Left Rectangle rlColor4ub(color.r, color.g, color.b, color.a); rlVertex2f(point[7].x, point[7].y); @@ -875,7 +876,7 @@ void DrawRoundedRect(Rectangle rec, float roundness, int segments, Color color) rlVertex2f(point[8].x, point[8].y); rlVertex2f(point[7].x, point[7].y); rlVertex2f(point[11].x, point[11].y); - + // [9] Middle Rectangle rlColor4ub(color.r, color.g, color.b, color.a); rlVertex2f(point[8].x, point[8].y); @@ -888,23 +889,24 @@ void DrawRoundedRect(Rectangle rec, float roundness, int segments, Color color) #endif } -// Draw rounded rectangle outline -void DrawRoundedRectLines(Rectangle rec, float roundness, int segments, int lineThick, Color color) +// Draw rectangle with rounded edges outline +void DrawRectangleRoundedLines(Rectangle rec, float roundness, int segments, int lineThick, Color color) { - if(lineThick < 0) lineThick = 0; + if (lineThick < 0) lineThick = 0; + // Not a rounded rectangle - if(roundness <= 0.0f ) + if (roundness <= 0.0f) { DrawRectangleLinesEx((Rectangle){rec.x-lineThick, rec.y-lineThick, rec.width+2*lineThick, rec.height+2*lineThick}, lineThick, color); return; } - - if(roundness >= 1.0f) roundness = 1.0f; - + + if (roundness >= 1.0f) roundness = 1.0f; + // Calculate corner radius - float radius = rec.width > rec.height ? (rec.height*roundness)/2 : (rec.width*roundness)/2; - if(radius <= 0.0f) return; - + float radius = (rec.width > rec.height)? (rec.height*roundness)/2 : (rec.width*roundness)/2; + if (radius <= 0.0f) return; + // Calculate number of segments to use for the corners if (segments < 4) { @@ -917,10 +919,10 @@ void DrawRoundedRectLines(Rectangle rec, float roundness, int segments, int line segments = ceilf(2*PI/th)/2; if (segments <= 0) segments = 4; } - + float stepLength = 90.0f/(float)segments; const float outerRadius = radius + (float)lineThick, innerRadius = radius; - + /* Quick sketch to make sense of all of this (mark the 16 + 4(corner centers P16-19) points we'll use below) * Not my best attempt at ASCII art, just preted it's rounded rectangle :) * P0 P1 @@ -937,7 +939,7 @@ void DrawRoundedRectLines(Rectangle rec, float roundness, int segments, int line * ==================== * P5 P4 */ - const Vector2 point[16] = { + const Vector2 point[16] = { {(float)rec.x + innerRadius, rec.y - lineThick}, {(float)(rec.x + rec.width) - innerRadius, rec.y - lineThick}, { rec.x + rec.width + lineThick, (float)rec.y + innerRadius }, // PO, P1, P2 {rec.x + rec.width + lineThick, (float)(rec.y + rec.height) - innerRadius}, {(float)(rec.x + rec.width) - innerRadius, rec.y + rec.height + lineThick}, // P3, P4 {(float)rec.x + innerRadius, rec.y + rec.height + lineThick}, { rec.x - lineThick, (float)(rec.y + rec.height) - innerRadius}, {rec.x - lineThick, (float)rec.y + innerRadius}, // P5, P6, P7 @@ -946,86 +948,93 @@ void DrawRoundedRectLines(Rectangle rec, float roundness, int segments, int line {(float)(rec.x + rec.width) - innerRadius, rec.y + rec.height}, {(float)rec.x + innerRadius, rec.y + rec.height}, // P12, P13 { rec.x, (float)(rec.y + rec.height) - innerRadius}, {rec.x, (float)rec.y + innerRadius} // P14, P15 }; - const Vector2 centers[4] = { + + const Vector2 centers[4] = { {(float)rec.x + innerRadius, (float)rec.y + innerRadius}, {(float)(rec.x + rec.width) - innerRadius, (float)rec.y + innerRadius}, // P16, P17 - {(float)(rec.x + rec.width) - innerRadius, (float)(rec.y + rec.height) - innerRadius}, {(float)rec.x + innerRadius, (float)(rec.y + rec.height) - innerRadius} // P18, P19 + {(float)(rec.x + rec.width) - innerRadius, (float)(rec.y + rec.height) - innerRadius}, {(float)rec.x + innerRadius, (float)(rec.y + rec.height) - innerRadius} // P18, P19 }; - const float angles[4] = {180.0f, 90.0f, 0.0f, 270.0f }; - - if(lineThick > 1) + + const float angles[4] = { 180.0f, 90.0f, 0.0f, 270.0f }; + + if (lineThick > 1) { #if defined(SUPPORT_QUADS_DRAW_MODE) if (rlCheckBufferLimit(4*4*segments + 4*4)) rlglDraw(); // 4 corners with 4 vertices for each segment + 4 rectangles with 4 vertices each + rlBegin(RL_QUADS); // Draw all of the 4 corners first: Upper Left Corner, Upper Right Corner, Lower Right Corner, Lower Left Corner - for(int k = 0; k < 4; ++k) // Hope the compiler is smart enough to unroll this loop - { - float angle = angles[k]; - const Vector2 center = centers[k]; - for (int i = 0; i < segments; i++) - { - rlColor4ub(color.r, color.g, color.b, color.a); - rlVertex2f(center.x + sinf(DEG2RAD*angle)*innerRadius, center.y + cosf(DEG2RAD*angle)*innerRadius); - rlVertex2f(center.x + sinf(DEG2RAD*angle)*outerRadius, center.y + cosf(DEG2RAD*angle)*outerRadius); - rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*outerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*outerRadius); - rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*innerRadius); - - angle += stepLength; - } - } - // Upper rectangle - rlColor4ub(color.r, color.g, color.b, color.a); - rlVertex2f(point[0].x, point[0].y); - rlVertex2f(point[8].x, point[8].y); - rlVertex2f(point[9].x, point[9].y); - rlVertex2f(point[1].x, point[1].y); - - // Right rectangle - rlColor4ub(color.r, color.g, color.b, color.a); - rlVertex2f(point[2].x, point[2].y); - rlVertex2f(point[10].x, point[10].y); - rlVertex2f(point[11].x, point[11].y); - rlVertex2f(point[3].x, point[3].y); - - // Lower rectangle - rlColor4ub(color.r, color.g, color.b, color.a); - rlVertex2f(point[13].x, point[13].y); - rlVertex2f(point[5].x, point[5].y); - rlVertex2f(point[4].x, point[4].y); - rlVertex2f(point[12].x, point[12].y); - - // Left rectangle - rlColor4ub(color.r, color.g, color.b, color.a); - rlVertex2f(point[15].x, point[15].y); - rlVertex2f(point[7].x, point[7].y); - rlVertex2f(point[6].x, point[6].y); - rlVertex2f(point[14].x, point[14].y); - - rlEnd(); -#else - if (rlCheckBufferLimit(4*6*segments + 4*6)) rlglDraw(); // 4 corners with 6(2*3) vertices for each segment + 4 rectangles with 6 vertices each - rlBegin(RL_TRIANGLES); - // Draw all of the 4 corners first: Upper Left Corner, Upper Right Corner, Lower Right Corner, Lower Left Corner - for(int k = 0; k < 4; ++k) // Hope the compiler is smart enough to unroll this loop + for (int k = 0; k < 4; ++k) // Hope the compiler is smart enough to unroll this loop { float angle = angles[k]; const Vector2 center = centers[k]; for (int i = 0; i < segments; i++) { rlColor4ub(color.r, color.g, color.b, color.a); - rlVertex2f(center.x + sinf(DEG2RAD*angle)*innerRadius, center.y + cosf(DEG2RAD*angle)*innerRadius); rlVertex2f(center.x + sinf(DEG2RAD*angle)*outerRadius, center.y + cosf(DEG2RAD*angle)*outerRadius); - rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*innerRadius); - - rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*innerRadius); - rlVertex2f(center.x + sinf(DEG2RAD*angle)*outerRadius, center.y + cosf(DEG2RAD*angle)*outerRadius); rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*outerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*outerRadius); - + rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*innerRadius); + angle += stepLength; } } - + + // Upper rectangle + rlColor4ub(color.r, color.g, color.b, color.a); + rlVertex2f(point[0].x, point[0].y); + rlVertex2f(point[8].x, point[8].y); + rlVertex2f(point[9].x, point[9].y); + rlVertex2f(point[1].x, point[1].y); + + // Right rectangle + rlColor4ub(color.r, color.g, color.b, color.a); + rlVertex2f(point[2].x, point[2].y); + rlVertex2f(point[10].x, point[10].y); + rlVertex2f(point[11].x, point[11].y); + rlVertex2f(point[3].x, point[3].y); + + // Lower rectangle + rlColor4ub(color.r, color.g, color.b, color.a); + rlVertex2f(point[13].x, point[13].y); + rlVertex2f(point[5].x, point[5].y); + rlVertex2f(point[4].x, point[4].y); + rlVertex2f(point[12].x, point[12].y); + + // Left rectangle + rlColor4ub(color.r, color.g, color.b, color.a); + rlVertex2f(point[15].x, point[15].y); + rlVertex2f(point[7].x, point[7].y); + rlVertex2f(point[6].x, point[6].y); + rlVertex2f(point[14].x, point[14].y); + + rlEnd(); +#else + if (rlCheckBufferLimit(4*6*segments + 4*6)) rlglDraw(); // 4 corners with 6(2*3) vertices for each segment + 4 rectangles with 6 vertices each + + rlBegin(RL_TRIANGLES); + + // Draw all of the 4 corners first: Upper Left Corner, Upper Right Corner, Lower Right Corner, Lower Left Corner + for (int k = 0; k < 4; ++k) // Hope the compiler is smart enough to unroll this loop + { + float angle = angles[k]; + const Vector2 center = centers[k]; + + for (int i = 0; i < segments; i++) + { + rlColor4ub(color.r, color.g, color.b, color.a); + + rlVertex2f(center.x + sinf(DEG2RAD*angle)*innerRadius, center.y + cosf(DEG2RAD*angle)*innerRadius); + rlVertex2f(center.x + sinf(DEG2RAD*angle)*outerRadius, center.y + cosf(DEG2RAD*angle)*outerRadius); + rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*innerRadius); + + rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*innerRadius); + rlVertex2f(center.x + sinf(DEG2RAD*angle)*outerRadius, center.y + cosf(DEG2RAD*angle)*outerRadius); + rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*outerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*outerRadius); + + angle += stepLength; + } + } + // Upper rectangle rlColor4ub(color.r, color.g, color.b, color.a); rlVertex2f(point[0].x, point[0].y); @@ -1034,7 +1043,7 @@ void DrawRoundedRectLines(Rectangle rec, float roundness, int segments, int line rlVertex2f(point[1].x, point[1].y); rlVertex2f(point[0].x, point[0].y); rlVertex2f(point[9].x, point[9].y); - + // Right rectangle rlColor4ub(color.r, color.g, color.b, color.a); rlVertex2f(point[10].x, point[10].y); @@ -1043,7 +1052,7 @@ void DrawRoundedRectLines(Rectangle rec, float roundness, int segments, int line rlVertex2f(point[2].x, point[2].y); rlVertex2f(point[10].x, point[10].y); rlVertex2f(point[3].x, point[3].y); - + // Lower rectangle rlColor4ub(color.r, color.g, color.b, color.a); rlVertex2f(point[13].x, point[13].y); @@ -1052,7 +1061,7 @@ void DrawRoundedRectLines(Rectangle rec, float roundness, int segments, int line rlVertex2f(point[12].x, point[12].y); rlVertex2f(point[13].x, point[13].y); rlVertex2f(point[4].x, point[4].y); - + // Left rectangle rlColor4ub(color.r, color.g, color.b, color.a); rlVertex2f(point[7].x, point[7].y); @@ -1068,12 +1077,15 @@ void DrawRoundedRectLines(Rectangle rec, float roundness, int segments, int line { // Use LINES to draw the outline if (rlCheckBufferLimit(8*segments + 4*2)) rlglDraw(); // 4 corners with 2 vertices for each segment + 4 rectangles with 2 vertices each + rlBegin(RL_LINES); + // Draw all of the 4 corners first: Upper Left Corner, Upper Right Corner, Lower Right Corner, Lower Left Corner - for(int k = 0; k < 4; ++k) // Hope the compiler is smart enough to unroll this loop + for (int k = 0; k < 4; ++k) // Hope the compiler is smart enough to unroll this loop { float angle = angles[k]; const Vector2 center = centers[k]; + for (int i = 0; i < segments; i++) { rlColor4ub(color.r, color.g, color.b, color.a); @@ -1083,11 +1095,11 @@ void DrawRoundedRectLines(Rectangle rec, float roundness, int segments, int line } } // And now the remaining 4 lines - for(int i=0; i<8; i+=2) + for(int i = 0; i < 8; i += 2) { rlColor4ub(color.r, color.g, color.b, color.a); rlVertex2f(point[i].x, point[i].y); - rlVertex2f(point[i+1].x, point[i+1].y); + rlVertex2f(point[i + 1].x, point[i + 1].y); } rlEnd(); } From a103086443b3d3a3a94ec52ef19ec9be68a0069c Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 4 Apr 2019 13:50:52 +0200 Subject: [PATCH 35/74] Removed trail spaces --- src/core.c | 32 ++++++++++----------- src/models.c | 78 +++++++++++++++++++++++++------------------------- src/raudio.c | 4 +-- src/raylib.h | 14 ++++----- src/rlgl.h | 28 +++++++++--------- src/textures.c | 18 ++++++------ 6 files changed, 87 insertions(+), 87 deletions(-) diff --git a/src/core.c b/src/core.c index 7f2f7f1c3..acc872927 100644 --- a/src/core.c +++ b/src/core.c @@ -760,7 +760,7 @@ bool IsWindowResized(void) return windowResized; #else return false; -#endif +#endif } // Check if window is currently hidden @@ -1684,14 +1684,14 @@ const char *GetFileName(const char *filePath) const char *GetFileNameWithoutExt(const char *filePath) { #define MAX_FILENAMEWITHOUTEXT_LENGTH 64 - + static char fileName[MAX_FILENAMEWITHOUTEXT_LENGTH]; memset(fileName, 0, MAX_FILENAMEWITHOUTEXT_LENGTH); - + strcpy(fileName, GetFileName(filePath)); // Get filename with extension - + int len = strlen(fileName); - + for (int i = 0; (i < len) && (i < MAX_FILENAMEWITHOUTEXT_LENGTH); i++) { if (fileName[i] == '.') @@ -3154,7 +3154,7 @@ static void PollInputEvents(void) gamepadAxisCount = axisCount; } } - + windowResized = false; #if defined(SUPPORT_EVENTS_WAITING) @@ -3433,7 +3433,7 @@ static void WindowSizeCallback(GLFWwindow *window, int width, int height) currentHeight = height; // NOTE: Postprocessing texture is not scaled to new size - + windowResized = true; } @@ -4279,7 +4279,7 @@ static void *EventThread(void *arg) { // Scancode to keycode mapping for US keyboards // TODO: Proabobly replace this with a keymap from the X11 to get the correct regional map for the keyboard (Currently non US keyboards will have the wrong mapping for some keys) - static const int keymap_US[] = + static const int keymap_US[] = {0,256,49,50,51,52,53,54,55,56,57,48,45,61,259,258,81,87,69,82,84, 89,85,73,79,80,91,93,257,341,65,83,68,70,71,72,74,75,76,59,39,96, 340,92,90,88,67,86,66,78,77,44,46,47,344,332,342,32,280,290,291, @@ -4298,7 +4298,7 @@ static void *EventThread(void *arg) struct input_event event; InputEventWorker *worker = (InputEventWorker *)arg; - + int touchAction = -1; bool gestureUpdate = false; int keycode; @@ -4400,7 +4400,7 @@ static void *EventThread(void *arg) if ((event.code == BTN_TOUCH) || (event.code == BTN_LEFT)) { currentMouseStateEvdev[MOUSE_LEFT_BUTTON] = event.value; - + #if defined(SUPPORT_GESTURES_SYSTEM) if (event.value > 0) touchAction = TOUCH_DOWN; else touchAction = TOUCH_UP; @@ -4415,9 +4415,9 @@ static void *EventThread(void *arg) // Keyboard button parsing if((event.code >= 1) && (event.code <= 255)) //Keyboard keys appear for codes 1 to 255 { - keycode = keymap_US[event.code & 0xFF]; // The code we get is a scancode so we look up the apropriate keycode + keycode = keymap_US[event.code & 0xFF]; // The code we get is a scancode so we look up the apropriate keycode // Make sure we got a valid keycode - if((keycode > 0) && (keycode < sizeof(currentKeyState))) + if((keycode > 0) && (keycode < sizeof(currentKeyState))) { // Store the key information for raylib to later use currentKeyStateEvdev[keycode] = event.value; @@ -4449,22 +4449,22 @@ static void *EventThread(void *arg) gestureEvent.pointCount = 0; gestureEvent.touchAction = touchAction; - + if (touchPosition[0].x >= 0) gestureEvent.pointCount++; if (touchPosition[1].x >= 0) gestureEvent.pointCount++; if (touchPosition[2].x >= 0) gestureEvent.pointCount++; if (touchPosition[3].x >= 0) gestureEvent.pointCount++; - + gestureEvent.pointerId[0] = 0; gestureEvent.pointerId[1] = 1; gestureEvent.pointerId[2] = 2; gestureEvent.pointerId[3] = 3; - + gestureEvent.position[0] = touchPosition[0]; gestureEvent.position[1] = touchPosition[1]; gestureEvent.position[2] = touchPosition[2]; gestureEvent.position[3] = touchPosition[3]; - + ProcessGestureEvent(gestureEvent); #endif } diff --git a/src/models.c b/src/models.c index 632aca2bd..239ab5d84 100644 --- a/src/models.c +++ b/src/models.c @@ -624,19 +624,19 @@ Model LoadModel(const char *fileName) #if defined(SUPPORT_FILEFORMAT_IQM) if (IsFileExtension(fileName, ".iqm")) model = LoadIQM(fileName); #endif - + // Make sure model transform is set to identity matrix! model.transform = MatrixIdentity(); - if (model.meshCount == 0) + if (model.meshCount == 0) { TraceLog(LOG_WARNING, "[%s] No meshes can be loaded, default to cube mesh", fileName); - + model.meshCount = 1; model.meshes = (Mesh *)calloc(model.meshCount, sizeof(Mesh)); model.meshes[0] = GenMeshCube(1.0f, 1.0f, 1.0f); } - else + else { // Upload vertex data to GPU (static mesh) for (int i = 0; i < model.meshCount; i++) rlLoadMesh(&model.meshes[i], false); @@ -645,11 +645,11 @@ Model LoadModel(const char *fileName) if (model.materialCount == 0) { TraceLog(LOG_WARNING, "[%s] No materials can be loaded, default to white material", fileName); - + model.materialCount = 1; model.materials = (Material *)calloc(model.materialCount, sizeof(Material)); model.materials[0] = LoadMaterialDefault(); - + model.meshMaterial = (int *)calloc(model.meshCount, sizeof(int)); } @@ -665,15 +665,15 @@ Model LoadModelFromMesh(Mesh mesh) Model model = { 0 }; model.transform = MatrixIdentity(); - + model.meshCount = 1; model.meshes = (Mesh *)malloc(model.meshCount*sizeof(Mesh)); model.meshes[0] = mesh; - + model.materialCount = 1; model.materials = (Material *)malloc(model.materialCount*sizeof(Material)); model.materials[0] = LoadMaterialDefault(); - + model.meshMaterial = (int *)malloc(model.meshCount*sizeof(int)); model.meshMaterial[0] = 0; // First material index @@ -685,11 +685,11 @@ void UnloadModel(Model model) { for (int i = 0; i < model.meshCount; i++) UnloadMesh(&model.meshes[i]); for (int i = 0; i < model.materialCount; i++) UnloadMaterial(model.materials[i]); - + free(model.meshes); free(model.materials); free(model.meshMaterial); - + // Unload animation data free(model.bones); free(model.bindPose); @@ -1817,11 +1817,11 @@ Material LoadMaterial(const char *fileName) { tinyobj_material_t *materials; unsigned int materialCount = 0; - + int result = tinyobj_parse_mtl_file(&materials, &materialCount, fileName); - + // TODO: Process materials to return - + tinyobj_materials_free(materials, materialCount); } #else @@ -1886,7 +1886,7 @@ void DrawModelEx(Model model, Vector3 position, Vector3 rotationAxis, float rota // Combine model transformation matrix (model.transform) with matrix generated by function parameters (matTransform) model.transform = MatrixMultiply(model.transform, matTransform); - for (int i = 0; i < model.meshCount; i++) + for (int i = 0; i < model.meshCount; i++) { model.materials[model.meshMaterial[i]].maps[MAP_DIFFUSE].color = tint; rlDrawMesh(model.meshes[i], model.materials[model.meshMaterial[i]], model.transform); @@ -2246,7 +2246,7 @@ BoundingBox MeshBoundingBox(Mesh mesh) // Get min and max vertex to construct bounds (AABB) Vector3 minVertex = { 0 }; Vector3 maxVertex = { 0 }; - + printf("Mesh vertex count: %i\n", mesh.vertexCount); if (mesh.vertices != NULL) @@ -2402,20 +2402,20 @@ static Model LoadOBJ(const char *fileName) { unsigned int flags = TINYOBJ_FLAG_TRIANGULATE; int ret = tinyobj_parse_obj(&attrib, &meshes, &meshCount, &materials, &materialCount, data, dataLength, flags); - + if (ret != TINYOBJ_SUCCESS) TraceLog(LOG_WARNING, "[%s] Model data could not be loaded", fileName); else TraceLog(LOG_INFO, "[%s] Model data loaded successfully: %i meshes / %i materials", fileName, meshCount, materialCount); - + // Init model meshes array model.meshCount = meshCount; model.meshes = (Mesh *)malloc(model.meshCount*sizeof(Mesh)); - + // Init model materials array model.materialCount = materialCount; model.materials = (Material *)malloc(model.materialCount*sizeof(Material)); model.meshMaterial = (int *)calloc(model.meshCount, sizeof(int)); - - /* + + /* // Multiple meshes data reference // NOTE: They are provided as a faces offset typedef struct { @@ -2424,7 +2424,7 @@ static Model LoadOBJ(const char *fileName) unsigned int length; } tinyobj_shape_t; */ - + // Init model meshes for (int m = 0; m < 1; m++) { @@ -2435,25 +2435,25 @@ static Model LoadOBJ(const char *fileName) mesh.vertices = (float *)malloc(mesh.vertexCount*3*sizeof(float)); mesh.texcoords = (float *)malloc(mesh.vertexCount*2*sizeof(float)); mesh.normals = (float *)malloc(mesh.vertexCount*3*sizeof(float)); - + int vCount = 0; int vtCount = 0; int vnCount = 0; - + for (int f = 0; f < attrib.num_faces; f++) { // Get indices for the face tinyobj_vertex_index_t idx0 = attrib.faces[3*f + 0]; tinyobj_vertex_index_t idx1 = attrib.faces[3*f + 1]; tinyobj_vertex_index_t idx2 = attrib.faces[3*f + 2]; - + // TraceLog(LOG_DEBUG, "Face %i index: v %i/%i/%i . vt %i/%i/%i . vn %i/%i/%i\n", f, idx0.v_idx, idx1.v_idx, idx2.v_idx, idx0.vt_idx, idx1.vt_idx, idx2.vt_idx, idx0.vn_idx, idx1.vn_idx, idx2.vn_idx); - + // Fill vertices buffer (float) using vertex index of the face for (int v = 0; v < 3; v++) { mesh.vertices[vCount + v] = attrib.vertices[idx0.v_idx*3 + v]; } vCount +=3; for (int v = 0; v < 3; v++) { mesh.vertices[vCount + v] = attrib.vertices[idx1.v_idx*3 + v]; } vCount +=3; for (int v = 0; v < 3; v++) { mesh.vertices[vCount + v] = attrib.vertices[idx2.v_idx*3 + v]; } vCount +=3; - + // Fill texcoords buffer (float) using vertex index of the face // NOTE: Y-coordinate must be flipped upside-down mesh.texcoords[vtCount + 0] = attrib.texcoords[idx0.vt_idx*2 + 0]; @@ -2462,7 +2462,7 @@ static Model LoadOBJ(const char *fileName) mesh.texcoords[vtCount + 1] = 1.0f - attrib.texcoords[idx1.vt_idx*2 + 1]; vtCount += 2; mesh.texcoords[vtCount + 0] = attrib.texcoords[idx2.vt_idx*2 + 0]; mesh.texcoords[vtCount + 1] = 1.0f - attrib.texcoords[idx2.vt_idx*2 + 1]; vtCount += 2; - + // Fill normals buffer (float) using vertex index of the face for (int v = 0; v < 3; v++) { mesh.normals[vnCount + v] = attrib.normals[idx0.vn_idx*3 + v]; } vnCount +=3; for (int v = 0; v < 3; v++) { mesh.normals[vnCount + v] = attrib.normals[idx1.vn_idx*3 + v]; } vnCount +=3; @@ -2481,7 +2481,7 @@ static Model LoadOBJ(const char *fileName) // Init material to default // NOTE: Uses default shader, only MAP_DIFFUSE supported model.materials[m] = LoadMaterialDefault(); - + /* typedef struct { char *name; @@ -2508,19 +2508,19 @@ static Model LoadOBJ(const char *fileName) char *alpha_texname; // map_d } tinyobj_material_t; */ - + model.materials[m].maps[MAP_DIFFUSE].texture = LoadTexture(materials[m].diffuse_texname); //char *diffuse_texname; // map_Kd model.materials[m].maps[MAP_DIFFUSE].color = (Color){ (float)(materials[m].diffuse[0]*255.0f), (float)(materials[m].diffuse[1]*255.0f), (float)(materials[m].diffuse[2]*255.0f), 255 }; //float diffuse[3]; model.materials[m].maps[MAP_DIFFUSE].value = 0.0f; - + model.materials[m].maps[MAP_SPECULAR].texture = LoadTexture(materials[m].specular_texname); //char *specular_texname; // map_Ks model.materials[m].maps[MAP_SPECULAR].color = (Color){ (float)(materials[m].specular[0]*255.0f), (float)(materials[m].specular[1]*255.0f), (float)(materials[m].specular[2]*255.0f), 255 }; //float specular[3]; model.materials[m].maps[MAP_SPECULAR].value = 0.0f; - + model.materials[m].maps[MAP_NORMAL].texture = LoadTexture(materials[m].bump_texname); //char *bump_texname; // map_bump, bump model.materials[m].maps[MAP_NORMAL].color = WHITE; model.materials[m].maps[MAP_NORMAL].value = materials[m].shininess; - + model.materials[m].maps[MAP_EMISSION].color = (Color){ (float)(materials[m].emission[0]*255.0f), (float)(materials[m].emission[1]*255.0f), (float)(materials[m].emission[2]*255.0f), 255 }; //float emission[3]; model.materials[m].maps[MAP_HEIGHT].texture = LoadTexture(materials[m].displacement_texname); //char *displacement_texname; // disp @@ -2579,7 +2579,7 @@ static Model LoadIQM(const char *fileName) } IQMTriangle; // NOTE: Adjacency unused by default - typedef struct IQMAdjacency { + typedef struct IQMAdjacency { unsigned int triangle[3]; } IQMAdjacency; @@ -2677,7 +2677,7 @@ static Model LoadIQM(const char *fileName) model.meshCount = iqm.num_meshes; model.meshes = malloc(sizeof(Mesh)*iqm.num_meshes); - + char name[MESH_NAME_LENGTH]; for (int i = 0; i < iqm.num_meshes; i++) @@ -2685,17 +2685,17 @@ static Model LoadIQM(const char *fileName) fseek(iqmFile,iqm.ofs_text+imesh[i].name,SEEK_SET); fread(name, sizeof(char)*MESH_NAME_LENGTH, 1, iqmFile); // Mesh name not used... model.meshes[i].vertexCount = imesh[i].num_vertexes; - + model.meshes[i].vertices = malloc(sizeof(float)*imesh[i].num_vertexes*3); // Default vertex positions model.meshes[i].normals = malloc(sizeof(float)*imesh[i].num_vertexes*3); // Default vertex normals model.meshes[i].texcoords = malloc(sizeof(float)*imesh[i].num_vertexes*2); // Default vertex texcoords - + model.meshes[i].boneIds = malloc(sizeof(int)*imesh[i].num_vertexes*4); // Up-to 4 bones supported! model.meshes[i].boneWeights = malloc(sizeof(float)*imesh[i].num_vertexes*4); // Up-to 4 bones supported! - + model.meshes[i].triangleCount = imesh[i].num_triangles; model.meshes[i].indices = malloc(sizeof(unsigned short)*imesh[i].num_triangles*3); - + // Animated verted data, what we actually process for rendering // NOTE: Animated vertex should be re-uploaded to GPU (if not using GPU skinning) model.meshes[i].animVertices = malloc(sizeof(float)*imesh[i].num_vertexes*3); diff --git a/src/raudio.c b/src/raudio.c index 1c1530098..d360b2d34 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -525,7 +525,7 @@ void InitAudioDevice(void) TraceLog(LOG_INFO, "Audio channels: %d -> %d", device.playback.channels, device.playback.internalChannels); TraceLog(LOG_INFO, "Audio sample rate: %d -> %d", device.sampleRate, device.playback.internalSampleRate); TraceLog(LOG_INFO, "Audio buffer size: %d", device.playback.internalBufferSizeInFrames); - + isAudioInitialized = MA_TRUE; } @@ -587,7 +587,7 @@ AudioBuffer *CreateAudioBuffer(ma_format format, ma_uint32 channels, ma_uint32 s dspConfig.pUserData = audioBuffer; dspConfig.allowDynamicSampleRate = MA_TRUE; // <-- Required for pitch shifting. ma_result result = ma_pcm_converter_init(&dspConfig, &audioBuffer->dsp); - + if (result != MA_SUCCESS) { TraceLog(LOG_ERROR, "CreateAudioBuffer() : Failed to create data conversion pipeline"); diff --git a/src/raylib.h b/src/raylib.h index 93965038c..c365fa47f 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -339,7 +339,7 @@ typedef struct Material { // Transformation properties typedef struct Transform { - Vector3 translation; // Translation + Vector3 translation; // Translation Quaternion rotation; // Rotation Vector3 scale; // Scale } Transform; @@ -353,14 +353,14 @@ typedef struct BoneInfo { // Model type typedef struct Model { Matrix transform; // Local transform matrix - + int meshCount; // Number of meshes Mesh *meshes; // Meshes array int materialCount; // Number of materials Material *materials; // Materials array int *meshMaterial; // Mesh material number - + // Animation data int boneCount; // Number of bones BoneInfo *bones; // Bones information (skeleton) @@ -371,7 +371,7 @@ typedef struct Model { typedef struct ModelAnimation { int boneCount; // Number of bones BoneInfo *bones; // Bones information (skeleton) - + int frameCount; // Number of animation frames Transform **framePoses; // Poses array by frame } ModelAnimation; @@ -1082,8 +1082,8 @@ RLAPI void DrawRectangleGradientH(int posX, int posY, int width, int height, Col RLAPI void DrawRectangleGradientEx(Rectangle rec, Color col1, Color col2, Color col3, Color col4); // Draw a gradient-filled rectangle with custom vertex colors RLAPI void DrawRectangleLines(int posX, int posY, int width, int height, Color color); // Draw rectangle outline RLAPI void DrawRectangleLinesEx(Rectangle rec, int lineThick, Color color); // Draw rectangle outline with extended parameters -RLAPI void DrawRoundedRect(Rectangle rec, float roundness, int segments, Color color); // Draw rectangle with rounded edges -RLAPI void DrawRoundedRectLines(Rectangle rec, float roundness, int segments, int lineThick, Color color); // Draw rounded rectangle outline +RLAPI void DrawRectangleRounded(Rectangle rec, float roundness, int segments, Color color); // Draw rectangle with rounded edges +RLAPI void DrawRectangleRoundedLines(Rectangle rec, float roundness, int segments, int lineThick, Color color); // Draw rectangle with rounded edges outline RLAPI void DrawTriangle(Vector2 v1, Vector2 v2, Vector2 v3, Color color); // Draw a color-filled triangle RLAPI void DrawTriangleLines(Vector2 v1, Vector2 v2, Vector2 v3, Color color); // Draw triangle outline RLAPI void DrawPoly(Vector2 center, int sides, float radius, float rotation, Color color); // Draw a regular polygon (Vector version) @@ -1201,7 +1201,7 @@ RLAPI void DrawFPS(int posX, int posY); RLAPI void DrawText(const char *text, int posX, int posY, int fontSize, Color color); // Draw text (using default font) RLAPI void DrawTextEx(Font font, const char *text, Vector2 position, float fontSize, float spacing, Color tint); // Draw text using font and additional parameters RLAPI void DrawTextRec(Font font, const char *text, Rectangle rec, float fontSize, float spacing, bool wordWrap, Color tint); // Draw text using font inside rectangle limits -RLAPI void DrawTextRecEx(Font font, const char *text, Rectangle rec, float fontSize, float spacing, bool wordWrap, Color tint, +RLAPI void DrawTextRecEx(Font font, const char *text, Rectangle rec, float fontSize, float spacing, bool wordWrap, Color tint, int selectStart, int selectLength, Color selectText, Color selectBack); // Draw text using font inside rectangle limits with support for text selection // Text misc. functions diff --git a/src/rlgl.h b/src/rlgl.h index 8cc4c5900..3f3054629 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -1130,22 +1130,22 @@ void rlBegin(int mode) // Make sure current draws[i].vertexCount is aligned a multiple of 4, // that way, following QUADS drawing will keep aligned with index processing // It implies adding some extra alignment vertex at the end of the draw, - // those vertex are not processed but they are considered as an additional offset + // those vertex are not processed but they are considered as an additional offset // for the next set of vertex to be drawn if (draws[drawsCounter - 1].mode == RL_LINES) draws[drawsCounter - 1].vertexAlignment = ((draws[drawsCounter - 1].vertexCount < 4)? draws[drawsCounter - 1].vertexCount : draws[drawsCounter - 1].vertexCount%4); else if (draws[drawsCounter - 1].mode == RL_TRIANGLES) draws[drawsCounter - 1].vertexAlignment = ((draws[drawsCounter - 1].vertexCount < 4)? 1 : (4 - (draws[drawsCounter - 1].vertexCount%4))); - + if (rlCheckBufferLimit(draws[drawsCounter - 1].vertexAlignment)) rlglDraw(); else { vertexData[currentBuffer].vCounter += draws[drawsCounter - 1].vertexAlignment; vertexData[currentBuffer].cCounter += draws[drawsCounter - 1].vertexAlignment; vertexData[currentBuffer].tcCounter += draws[drawsCounter - 1].vertexAlignment; - + drawsCounter++; } } - + if (drawsCounter >= MAX_DRAWCALL_REGISTERED) rlglDraw(); draws[drawsCounter - 1].mode = mode; @@ -1301,22 +1301,22 @@ void rlEnableTexture(unsigned int id) // Make sure current draws[i].vertexCount is aligned a multiple of 4, // that way, following QUADS drawing will keep aligned with index processing // It implies adding some extra alignment vertex at the end of the draw, - // those vertex are not processed but they are considered as an additional offset + // those vertex are not processed but they are considered as an additional offset // for the next set of vertex to be drawn if (draws[drawsCounter - 1].mode == RL_LINES) draws[drawsCounter - 1].vertexAlignment = ((draws[drawsCounter - 1].vertexCount < 4)? draws[drawsCounter - 1].vertexCount : draws[drawsCounter - 1].vertexCount%4); else if (draws[drawsCounter - 1].mode == RL_TRIANGLES) draws[drawsCounter - 1].vertexAlignment = ((draws[drawsCounter - 1].vertexCount < 4)? 1 : (4 - (draws[drawsCounter - 1].vertexCount%4))); - + if (rlCheckBufferLimit(draws[drawsCounter - 1].vertexAlignment)) rlglDraw(); else { vertexData[currentBuffer].vCounter += draws[drawsCounter - 1].vertexAlignment; vertexData[currentBuffer].cCounter += draws[drawsCounter - 1].vertexAlignment; vertexData[currentBuffer].tcCounter += draws[drawsCounter - 1].vertexAlignment; - + drawsCounter++; } } - + if (drawsCounter >= MAX_DRAWCALL_REGISTERED) rlglDraw(); draws[drawsCounter - 1].textureId = id; @@ -2044,7 +2044,7 @@ unsigned int rlLoadTexture(void *data, int width, int height, int format, int mi unsigned int rlLoadTextureDepth(int width, int height, int bits, bool useRenderBuffer) { unsigned int id = 0; - + #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) unsigned int glInternalFormat = GL_DEPTH_COMPONENT16; @@ -2103,7 +2103,7 @@ unsigned int rlLoadTextureCubemap(void *data, int size, int format) { unsigned int cubemapId = 0; unsigned int dataSize = GetPixelDataSize(size, size, format); - + #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) glGenTextures(1, &cubemapId); glBindTexture(GL_TEXTURE_CUBE_MAP, cubemapId); @@ -2305,7 +2305,7 @@ void rlRenderTextureAttach(RenderTexture2D target, unsigned int id, int attachTy bool rlRenderTextureComplete(RenderTexture target) { bool result = false; - + #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) glBindFramebuffer(GL_FRAMEBUFFER, target.id); @@ -4196,7 +4196,7 @@ static void DrawBuffersDefault(void) glUniformMatrix4fv(currentShader.locs[LOC_MATRIX_MVP], 1, false, MatrixToFloat(matMVP)); glUniform4f(currentShader.locs[LOC_COLOR_DIFFUSE], 1.0f, 1.0f, 1.0f, 1.0f); glUniform1i(currentShader.locs[LOC_MAP_DIFFUSE], 0); // Provided value refers to the texture unit (active) - + // TODO: Support additional texture units on custom shader //if (currentShader->locs[LOC_MAP_SPECULAR] > 0) glUniform1i(currentShader.locs[LOC_MAP_SPECULAR], 1); //if (currentShader->locs[LOC_MAP_NORMAL] > 0) glUniform1i(currentShader.locs[LOC_MAP_NORMAL], 2); @@ -4231,7 +4231,7 @@ static void DrawBuffersDefault(void) for (int i = 0; i < drawsCounter; i++) { glBindTexture(GL_TEXTURE_2D, draws[i].textureId); - + // TODO: Find some way to bind additional textures --> Use global texture IDs? Register them on draw[i]? //if (currentShader->locs[LOC_MAP_SPECULAR] > 0) { glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, textureUnit1_id); } //if (currentShader->locs[LOC_MAP_SPECULAR] > 0) { glActiveTexture(GL_TEXTURE2); glBindTexture(GL_TEXTURE_2D, textureUnit2_id); } @@ -4248,7 +4248,7 @@ static void DrawBuffersDefault(void) glDrawElements(GL_TRIANGLES, draws[i].vertexCount/4*6, GL_UNSIGNED_SHORT, (GLvoid *)(sizeof(GLushort)*vertexOffset/4*6)); #endif } - + vertexOffset += (draws[i].vertexCount + draws[i].vertexAlignment); } diff --git a/src/textures.c b/src/textures.c index 38995f116..cd0bd208a 100644 --- a/src/textures.c +++ b/src/textures.c @@ -181,7 +181,7 @@ static Image LoadASTC(const char *fileName); // Load ASTC file Image LoadImage(const char *fileName) { Image image = { 0 }; - + #if defined(SUPPORT_FILEFORMAT_PNG) || \ defined(SUPPORT_FILEFORMAT_BMP) || \ defined(SUPPORT_FILEFORMAT_TGA) || \ @@ -744,7 +744,7 @@ Image GetTextureData(Texture2D texture) RLAPI Image GetScreenData(void) { Image image = { 0 }; - + image.width = GetScreenWidth(); image.height = GetScreenHeight(); image.mipmaps = 1; @@ -1411,13 +1411,13 @@ void ImageResizeNN(Image *image,int newWidth,int newHeight) void ImageResizeCanvas(Image *image, int newWidth,int newHeight, int offsetX, int offsetY, Color color) { // TODO: Review different scaling situations - + if ((newWidth != image->width) || (newHeight != image->height)) { if ((newWidth > image->width) && (newHeight > image->height)) { Image imTemp = GenImageColor(newWidth, newHeight, color); - + Rectangle srcRec = { 0.0f, 0.0f, (float)image->width, (float)image->height }; Rectangle dstRec = { (float)offsetX, (float)offsetY, (float)srcRec.width, (float)srcRec.height }; @@ -1434,23 +1434,23 @@ void ImageResizeCanvas(Image *image, int newWidth,int newHeight, int offsetX, in else // One side is bigger and the other is smaller { Image imTemp = GenImageColor(newWidth, newHeight, color); - + Rectangle srcRec = { 0.0f, 0.0f, (float)image->width, (float)image->height }; Rectangle dstRec = { (float)offsetX, (float)offsetY, (float)newWidth, (float)newHeight }; - + if (newWidth < image->width) { srcRec.x = offsetX; srcRec.width = newWidth; - + dstRec.x = 0.0f; } - + if (newHeight < image->height) { srcRec.y = offsetY; srcRec.height = newHeight; - + dstRec.y = 0.0f; } From 28b9de661d49911f7c5b7996576c7faeea0684d5 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 5 Apr 2019 13:12:54 +0200 Subject: [PATCH 36/74] Minor tweaks --- src/core.c | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/src/core.c b/src/core.c index acc872927..b0c0658d7 100644 --- a/src/core.c +++ b/src/core.c @@ -3191,7 +3191,7 @@ static void PollInputEvents(void) } else currentGamepadState[i][j] = 0; - //printf("Gamepad %d, button %d: Digital: %d, Analog: %g\n", gamepadState.index, j, gamepadState.digitalButton[j], gamepadState.analogButton[j]); + //TraceLog(LOG_DEBUG, "Gamepad %d, button %d: Digital: %d, Analog: %g", gamepadState.index, j, gamepadState.digitalButton[j], gamepadState.analogButton[j]); } // Register axis data for every connected gamepad @@ -3792,14 +3792,14 @@ static EM_BOOL EmscriptenTouchCallback(int eventType, const EmscriptenTouchEvent y = touchEvent->touches[i].canvasY; } - printf("%s, numTouches: %d %s%s%s%s\n", emscripten_event_type_to_string(eventType), event->numTouches, + TraceLog(LOG_DEBUG, "%s, numTouches: %d %s%s%s%s", emscripten_event_type_to_string(eventType), event->numTouches, event->ctrlKey? " CTRL" : "", event->shiftKey? " SHIFT" : "", event->altKey? " ALT" : "", event->metaKey? " META" : ""); for (int i = 0; i < event->numTouches; ++i) { const EmscriptenTouchPoint *t = &event->touches[i]; - printf(" %ld: screen: (%ld,%ld), client: (%ld,%ld), page: (%ld,%ld), isChanged: %d, onTarget: %d, canvas: (%ld, %ld)\n", + TraceLog(LOG_DEBUG, " %ld: screen: (%ld,%ld), client: (%ld,%ld), page: (%ld,%ld), isChanged: %d, onTarget: %d, canvas: (%ld, %ld)", t->identifier, t->screenX, t->screenY, t->clientX, t->clientY, t->pageX, t->pageY, t->isChanged, t->onTarget, t->canvasX, t->canvasY); } */ @@ -3858,12 +3858,12 @@ static EM_BOOL EmscriptenTouchCallback(int eventType, const EmscriptenTouchEvent static EM_BOOL EmscriptenGamepadCallback(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData) { /* - printf("%s: timeStamp: %g, connected: %d, index: %ld, numAxes: %d, numButtons: %d, id: \"%s\", mapping: \"%s\"\n", + TraceLog(LOG_DEBUG, "%s: timeStamp: %g, connected: %d, index: %ld, numAxes: %d, numButtons: %d, id: \"%s\", mapping: \"%s\"", eventType != 0? emscripten_event_type_to_string(eventType) : "Gamepad state", gamepadEvent->timestamp, gamepadEvent->connected, gamepadEvent->index, gamepadEvent->numAxes, gamepadEvent->numButtons, gamepadEvent->id, gamepadEvent->mapping); - for(int i = 0; i < gamepadEvent->numAxes; ++i) printf("Axis %d: %g\n", i, gamepadEvent->axis[i]); - for(int i = 0; i < gamepadEvent->numButtons; ++i) printf("Button %d: Digital: %d, Analog: %g\n", i, gamepadEvent->digitalButton[i], gamepadEvent->analogButton[i]); + for(int i = 0; i < gamepadEvent->numAxes; ++i) TraceLog(LOG_DEBUG, "Axis %d: %g", i, gamepadEvent->axis[i]); + for(int i = 0; i < gamepadEvent->numButtons; ++i) TraceLog(LOG_DEBUG, "Button %d: Digital: %d, Analog: %g", i, gamepadEvent->digitalButton[i], gamepadEvent->analogButton[i]); */ if ((gamepadEvent->connected) && (gamepadEvent->index < MAX_GAMEPADS)) gamepadReady[gamepadEvent->index] = true; @@ -3939,17 +3939,13 @@ static void ProcessKeyboard(void) for (int i = 0; i < 512; i++) currentKeyState[i] = 0; // Check keys from event input workers (This is the new keyboard reading method) - for (int i = 0; i < 512; i++)currentKeyState[i] = currentKeyStateEvdev[i]; + for (int i = 0; i < 512; i++) currentKeyState[i] = currentKeyStateEvdev[i]; // Fill all read bytes (looking for keys) for (int i = 0; i < bufferByteCount; i++) { TraceLog(LOG_DEBUG, "Bytes on keysBuffer: %i", bufferByteCount); - //printf("Key(s) bytes: "); - //for (int i = 0; i < bufferByteCount; i++) printf("0x%02x ", keysBuffer[i]); - //printf("\n"); - // NOTE: If (key == 0x1b), depending on next key, it could be a special keymap code! // Up -> 1b 5b 41 / Left -> 1b 5b 44 / Right -> 1b 5b 43 / Down -> 1b 5b 42 if (keysBuffer[i] == 0x1b) From a728376cdf8e76c33e36dc3be3b18d99d38be758 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 5 Apr 2019 13:13:10 +0200 Subject: [PATCH 37/74] Rename enum type --- src/rlgl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rlgl.h b/src/rlgl.h index 3f3054629..3d433377a 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -386,7 +386,7 @@ typedef unsigned char byte; MAP_IRRADIANCE, // NOTE: Uses GL_TEXTURE_CUBE_MAP MAP_PREFILTER, // NOTE: Uses GL_TEXTURE_CUBE_MAP MAP_BRDF - } TexmapIndex; + } MaterialMapType; #define MAP_DIFFUSE MAP_ALBEDO #define MAP_SPECULAR MAP_METALNESS From 38a13b76d13c6e4f6f6c02bfd63900de56de4a42 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 5 Apr 2019 13:13:42 +0200 Subject: [PATCH 38/74] Corrected issue with LoadMesh() --- examples/models/models_obj_loading.c | 2 +- examples/models/models_obj_viewer.c | 30 ++++++++++++++-------------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/examples/models/models_obj_loading.c b/examples/models/models_obj_loading.c index 0251fd3ee..74e7d08a0 100644 --- a/examples/models/models_obj_loading.c +++ b/examples/models/models_obj_loading.c @@ -59,7 +59,7 @@ int main() DrawGizmo(position); // Draw gizmo EndMode3D(); - + DrawText("(c) Castle 3D model by Alberto Cano", screenWidth - 200, screenHeight - 20, 10, GRAY); DrawFPS(10, 10); diff --git a/examples/models/models_obj_viewer.c b/examples/models/models_obj_viewer.c index ffa609e08..0581df343 100644 --- a/examples/models/models_obj_viewer.c +++ b/examples/models/models_obj_viewer.c @@ -28,11 +28,11 @@ int main() Model model = LoadModel("resources/models/turret.obj"); // Load default model obj Texture2D texture = LoadTexture("resources/models/turret_diffuse.png"); // Load default model texture model.materials[0].maps[MAP_DIFFUSE].texture = texture; // Bind texture to model - + Vector3 position = { 0.0, 0.0, 0.0 }; // Set model position - BoundingBox bounds = MeshBoundingBox(model.meshes[0]); // Set model bounds + BoundingBox bounds = MeshBoundingBox(model.meshes[0]); // Set model bounds bool selected = false; // Selected object flag - + SetCameraMode(camera, CAMERA_FREE); // Set a free camera mode char objFilename[64] = "turret.obj"; @@ -49,13 +49,13 @@ int main() { int count = 0; char **droppedFiles = GetDroppedFiles(&count); - + if (count == 1) { if (IsFileExtension(droppedFiles[0], ".obj")) { - UnloadMesh(&model.meshes[0]); - model.meshes[0] = LoadMesh(droppedFiles[0]); + for (int i = 0; i < model.meshCount; i++) UnloadMesh(&model.meshes[i]); + model.meshes = LoadMeshes(droppedFiles[0], &model.meshCount); bounds = MeshBoundingBox(model.meshes[0]); } else if (IsFileExtension(droppedFiles[0], ".png")) @@ -67,12 +67,12 @@ int main() strcpy(objFilename, GetFileName(droppedFiles[0])); } - + ClearDroppedFiles(); // Clear internal buffers } - + UpdateCamera(&camera); - + // Select model on mouse click if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) { @@ -93,21 +93,21 @@ int main() DrawModel(model, position, 1.0f, WHITE); // Draw 3d model with texture DrawGrid(20.0, 10.0); // Draw a grid - + if (selected) DrawBoundingBox(bounds, GREEN); - + EndMode3D(); - + DrawText("Free camera default controls:", 10, 20, 10, DARKGRAY); DrawText("- Mouse Wheel to Zoom in-out", 20, 40, 10, GRAY); DrawText("- Mouse Wheel Pressed to Pan", 20, 60, 10, GRAY); DrawText("- Alt + Mouse Wheel Pressed to Rotate", 20, 80, 10, GRAY); DrawText("- Alt + Ctrl + Mouse Wheel Pressed for Smooth Zoom", 20, 100, 10, GRAY); - + DrawText("Drag & drop .obj/.png to load mesh/texture.", 10, GetScreenHeight() - 20, 10, DARKGRAY); DrawText(FormatText("Current file: %s", objFilename), 250, GetScreenHeight() - 20, 10, GRAY); if (selected) DrawText("MODEL SELECTED", GetScreenWidth() - 110, 10, 10, GREEN); - + DrawText("(c) Turret 3D model by Alberto Cano", screenWidth - 200, screenHeight - 20, 10, GRAY); EndDrawing(); @@ -119,7 +119,7 @@ int main() UnloadModel(model); // Unload model ClearDroppedFiles(); // Clear internal buffers - + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- From 92733d6695e0cdab3b42972f2cd6ed48d98ec689 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 5 Apr 2019 13:15:56 +0200 Subject: [PATCH 39/74] BIG UPDATE: New models functions for animations! Multiple functions added and some reviewed to adapt to the new multi-mesh, multi-material and animated models. --- .../models_animation.c} | 51 +- .../resources/guy}/guy.blend | Bin .../resources/guy}/guy.iqm | Bin .../resources/guy}/guyanim.iqm | Bin .../resources/guy}/guytex.png | Bin examples/others/iqm_loader/riqm.h | 739 +----------------- src/models.c | 660 ++++++++++++---- src/raylib.h | 33 +- 8 files changed, 547 insertions(+), 936 deletions(-) rename examples/{others/iqm_loader/models_iqm_animation.c => models/models_animation.c} (61%) rename examples/{others/iqm_loader/resources => models/resources/guy}/guy.blend (100%) rename examples/{others/iqm_loader/resources => models/resources/guy}/guy.iqm (100%) rename examples/{others/iqm_loader/resources => models/resources/guy}/guyanim.iqm (100%) rename examples/{others/iqm_loader/resources => models/resources/guy}/guytex.png (100%) diff --git a/examples/others/iqm_loader/models_iqm_animation.c b/examples/models/models_animation.c similarity index 61% rename from examples/others/iqm_loader/models_iqm_animation.c rename to examples/models/models_animation.c index 18dd8577b..a75241b37 100644 --- a/examples/others/iqm_loader/models_iqm_animation.c +++ b/examples/models/models_animation.c @@ -1,19 +1,16 @@ /******************************************************************************************* * -* raylib [models] example - Load IQM 3d model with animations and play them +* raylib [models] example - Load 3d model with animations and play them * -* This example has been created using raylib 2.0 (www.raylib.com) +* This example has been created using raylib 2.5 (www.raylib.com) * raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) * -* Copyright (c) 2018 @culacant and @raysan5 +* Copyright (c) 2019 Ramon Santamaria (@raysan5) and @culacant * ********************************************************************************************/ #include "raylib.h" -#define RIQM_IMPLEMENTATION -#include "riqm.h" - int main() { // Initialization @@ -21,7 +18,7 @@ int main() int screenWidth = 800; int screenHeight = 450; - InitWindow(screenWidth, screenHeight, "raylib [models] example - iqm animation"); + InitWindow(screenWidth, screenHeight, "raylib [models] example - model animation"); // Define the camera to look into our 3d world Camera camera = { 0 }; @@ -31,26 +28,25 @@ int main() camera.fovy = 45.0f; // Camera field-of-view Y camera.type = CAMERA_PERSPECTIVE; // Camera mode type - // Load the animated model mesh and basic data - AnimatedModel model = LoadAnimatedModel("resources/guy.iqm"); - // Load model texture and set material - // NOTE: There is only 1 mesh and 1 material (both at index 0), thats what the 2 0's are - model = AnimatedModelAddTexture(model, "resources/guytex.png"); // REPLACE! - model = SetMeshMaterial(model, 0, 0); // REPLACE! + Model model = LoadModel("resources/guy/guy.iqm"); // Load the animated model mesh and basic data + Texture2D texture = LoadTexture("resources/guy/guytex.png"); // Load model texture and set material + SetMaterialTexture(&model.materials[0], MAP_DIFFUSE, texture); // Set model material map texture + + Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model position // Load animation data - Animation anim = LoadAnimationFromIQM("resources/guyanim.iqm"); - + int animsCount = 0; + ModelAnimation *anims = LoadModelAnimations("resources/guy/guyanim.iqm", &animsCount); int animFrameCounter = 0; - SetCameraMode(camera, CAMERA_FREE); // Set free camera mode + SetCameraMode(camera, CAMERA_FREE); // Set free camera mode - SetTargetFPS(60); // Set our game to run at 60 frames-per-second + SetTargetFPS(60); // 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 //---------------------------------------------------------------------------------- @@ -60,7 +56,8 @@ int main() if (IsKeyDown(KEY_SPACE)) { animFrameCounter++; - AnimateModel(model, anim, animFrameCounter); // Animate the model with animation data and frame + UpdateModelAnimation(model, anims[0], animFrameCounter); + if (animFrameCounter >= anims[0].frameCount) animFrameCounter = 0; } //---------------------------------------------------------------------------------- @@ -72,14 +69,18 @@ int main() BeginMode3D(camera); - DrawAnimatedModel(model, Vector3Zero(), 1.0f, WHITE); // Draw animated model + DrawModelEx(model, position, (Vector3){ 1.0f, 0.0f, 0.0f }, -90.0f, (Vector3){ 1.0f, 1.0f, 1.0f }, WHITE); + + for (int i = 0; i < model.boneCount; i++) + { + DrawCube(anims[0].framePoses[animFrameCounter][i].translation, 0.2f, 0.2f, 0.2f, RED); + } DrawGrid(10, 1.0f); // Draw a grid EndMode3D(); - - DrawText("PRESS SPACE to PLAY IQM MODEL ANIMATION", 10, 10, 20, MAROON); + DrawText("PRESS SPACE to PLAY MODEL ANIMATION", 10, 10, 20, MAROON); DrawText("(c) Guy IQM 3D model by @culacant", screenWidth - 200, screenHeight - 20, 10, GRAY); EndDrawing(); @@ -88,8 +89,10 @@ int main() // De-Initialization //-------------------------------------------------------------------------------------- - UnloadAnimation(anim); // Unload animation data - UnloadAnimatedModel(model); // Unload animated model + // Unload model animations data + for (int i = 0; i < animsCount; i++) UnloadModelAnimation(anims[i]); + + UnloadModel(model); // Unload model CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- diff --git a/examples/others/iqm_loader/resources/guy.blend b/examples/models/resources/guy/guy.blend similarity index 100% rename from examples/others/iqm_loader/resources/guy.blend rename to examples/models/resources/guy/guy.blend diff --git a/examples/others/iqm_loader/resources/guy.iqm b/examples/models/resources/guy/guy.iqm similarity index 100% rename from examples/others/iqm_loader/resources/guy.iqm rename to examples/models/resources/guy/guy.iqm diff --git a/examples/others/iqm_loader/resources/guyanim.iqm b/examples/models/resources/guy/guyanim.iqm similarity index 100% rename from examples/others/iqm_loader/resources/guyanim.iqm rename to examples/models/resources/guy/guyanim.iqm diff --git a/examples/others/iqm_loader/resources/guytex.png b/examples/models/resources/guy/guytex.png similarity index 100% rename from examples/others/iqm_loader/resources/guytex.png rename to examples/models/resources/guy/guytex.png diff --git a/examples/others/iqm_loader/riqm.h b/examples/others/iqm_loader/riqm.h index 694e3a958..41ef8a140 100644 --- a/examples/others/iqm_loader/riqm.h +++ b/examples/others/iqm_loader/riqm.h @@ -49,67 +49,13 @@ // Types and Structures Definition //---------------------------------------------------------------------------------- -#define JOINT_NAME_LENGTH 32 // Joint name string length -#define MESH_NAME_LENGTH 32 // Mesh name string length - -typedef struct Joint { - char name[JOINT_NAME_LENGTH]; - int parent; -} Joint; - -typedef struct Pose { - Vector3 translation; - Quaternion rotation; - Vector3 scale; -} Pose; - -typedef struct Animation { - int jointCount; // Number of joints (bones) - Joint *joints; // Joints array - // NOTE: Joints in anims do not have names - - int frameCount; // Number of animation frames - float framerate; // Frame change speed - - Pose **framepose; // Poses array by frame (and one pose by joint) -} Animation; - -// Animated Model type -typedef struct AnimatedModel { - Matrix transform; // Local transform matrix - - int meshCount; // Number of meshes - Mesh *meshes; // Meshes array - - int materialCount; // Number of materials - Material *materials; // Materials array - - int *meshMaterialId; // Mesh materials ids - - // Animation required data - int jointCount; // Number of joints (and keyposes) - Joint *joints; // Mesh joints (bones) - Pose *basepose; // Mesh base-poses by joint -} AnimatedModel; +#define BONE_NAME_LENGTH 32 // BoneInfo name string length +#define MESH_NAME_LENGTH 32 // Mesh name string length //---------------------------------------------------------------------------------- // Module Functions Declaration //---------------------------------------------------------------------------------- -// Loading/Unloading functions -RIQMDEF AnimatedModel LoadAnimatedModel(const char *filename); -RIQMDEF void UnloadAnimatedModel(AnimatedModel model); -RIQMDEF Animation LoadAnimation(const char *filename); -RIQMDEF void UnloadAnimation(Animation anim); - -RIQMDEF AnimatedModel AnimatedModelAddTexture(AnimatedModel model, const char *filename); // GENERIC! -RIQMDEF AnimatedModel SetMeshMaterial(AnimatedModel model, int meshid, int textureid); // GENERIC! - -// Usage functionality -RIQMDEF bool CheckSkeletonsMatch(AnimatedModel model, Animation anim); -RIQMDEF void AnimateModel(AnimatedModel model, Animation anim, int frame); -RIQMDEF void DrawAnimatedModel(AnimatedModel model, Vector3 position, float scale, Color tint); -RIQMDEF void DrawAnimatedModelEx(AnimatedModel model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint); #endif // RIQM_H @@ -133,90 +79,6 @@ RIQMDEF void DrawAnimatedModelEx(AnimatedModel model, Vector3 position, Vector3 //---------------------------------------------------------------------------------- // Defines and Macros //---------------------------------------------------------------------------------- -#define IQM_MAGIC "INTERQUAKEMODEL" // IQM file magic number -#define IQM_VERSION 2 // only IQM version 2 supported -#define ANIMJOINTNAME "ANIMJOINT" // default joint name (used in Animation) - -//---------------------------------------------------------------------------------- -// Types and Structures Definition -//---------------------------------------------------------------------------------- -// iqm file structs -typedef struct IQMHeader { - char magic[16]; - unsigned int version; - unsigned int filesize; - unsigned int flags; - unsigned int num_text, ofs_text; - unsigned int num_meshes, ofs_meshes; - unsigned int num_vertexarrays, num_vertexes, ofs_vertexarrays; - unsigned int num_triangles, ofs_triangles, ofs_adjacency; - unsigned int num_joints, ofs_joints; - unsigned int num_poses, ofs_poses; - unsigned int num_anims, ofs_anims; - unsigned int num_frames, num_framechannels, ofs_frames, ofs_bounds; - unsigned int num_comment, ofs_comment; - unsigned int num_extensions, ofs_extensions; -} IQMHeader; - -typedef struct IQMMesh { - unsigned int name; - unsigned int material; - unsigned int first_vertex, num_vertexes; - unsigned int first_triangle, num_triangles; -} IQMMesh; - -typedef struct IQMTriangle { - unsigned int vertex[3]; -} IQMTriangle; - -typedef struct IQMAdjacency { // adjacency unused by default - unsigned int triangle[3]; -} IQMAdjacency; - -typedef struct IQMJoint { - unsigned int name; - int parent; - float translate[3], rotate[4], scale[3]; -} IQMJoint; - -typedef struct IQMPose { - int parent; - unsigned int mask; - float channeloffset[10]; - float channelscale[10]; -} IQMPose; - -typedef struct IQMAnim { - unsigned int name; - unsigned int first_frame, num_frames; - float framerate; - unsigned int flags; -} IQMAnim; - -typedef struct IQMVertexArray { - unsigned int type; - unsigned int flags; - unsigned int format; - unsigned int size; - unsigned int offset; -} IQMVertexArray; - -typedef struct IQMBounds { // bounds unused by default - float bbmin[3], bbmax[3]; - float xyradius, radius; -} IQMBounds; - - -typedef enum { - IQM_POSITION = 0, - IQM_TEXCOORD = 1, - IQM_NORMAL = 2, - IQM_TANGENT = 3, // tangents unused by default - IQM_BLENDINDEXES = 4, - IQM_BLENDWEIGHTS = 5, - IQM_COLOR = 6, // vertex colors unused by default - IQM_CUSTOM = 0x10 // custom vertex values unused by default -} IQMVertexType; //---------------------------------------------------------------------------------- // Global Variables Definition @@ -225,609 +87,12 @@ typedef enum { //---------------------------------------------------------------------------------- // Module specific Functions Declaration //---------------------------------------------------------------------------------- -static AnimatedModel LoadIQM(const char *filename); #ifdef __cplusplus extern "C" { // Prevents name mangling of functions #endif -// Load .iqm file and initialize animated model -AnimatedModel LoadAnimatedModel(const char *filename) -{ - AnimatedModel out = LoadIQM(filename); - for (int i = 0; i < out.meshCount; i++) rlLoadMesh(&out.meshes[i], false); - out.transform = MatrixIdentity(); - out.meshMaterialId = malloc(sizeof(int)*out.meshCount); - out.materials = NULL; - out.materialCount = 0; - - for (int i = 0; i < out.meshCount; i++) out.meshMaterialId[i] = -1; - - return out; -} - -// Add a texture to an animated model -AnimatedModel AnimatedModelAddTexture(AnimatedModel model, const char *filename) -{ - Texture2D texture = LoadTexture(filename); - - model.materials = realloc(model.materials, sizeof(Material)*(model.materialCount + 1)); - model.materials[model.materialCount] = LoadMaterialDefault(); - model.materials[model.materialCount].maps[MAP_DIFFUSE].texture = texture; - model.materialCount++; - - return model; -} - -// Set the material for a meshes -AnimatedModel SetMeshMaterial(AnimatedModel model, int meshid, int textureid) -{ - if (meshid > model.meshCount) - { - TraceLog(LOG_WARNING, "MeshId greater than meshCount\n"); - return model; - } - - if (textureid > model.materialCount) - { - TraceLog(LOG_WARNING,"textureid greater than materialCount\n"); - return model; - } - - model.meshMaterialId[meshid] = textureid; - - return model; -} - -// Load animations from a .iqm file -Animation LoadAnimationFromIQM(const char *filename) -{ - Animation animation = { 0 }; - - FILE *iqmFile; - IQMHeader iqm; - - iqmFile = fopen(filename,"rb"); - - if (!iqmFile) - { - TraceLog(LOG_ERROR, "[%s] Unable to open file", filename); - return animation; - } - - // header - fread(&iqm, sizeof(IQMHeader), 1, iqmFile); - - if (strncmp(iqm.magic, IQM_MAGIC, sizeof(IQM_MAGIC))) - { - TraceLog(LOG_ERROR, "Magic Number \"%s\"does not match.", iqm.magic); - fclose(iqmFile); - return animation; - } - - if (iqm.version != IQM_VERSION) - { - TraceLog(LOG_ERROR, "IQM version %i is incorrect.", iqm.version); - fclose(iqmFile); - return animation; - } - - // header - if (iqm.num_anims > 1) TraceLog(LOG_WARNING, "More than 1 animation in file, only the first one will get loaded"); - - // joints - IQMPose *poses; - poses = malloc(sizeof(IQMPose)*iqm.num_poses); - fseek(iqmFile, iqm.ofs_poses, SEEK_SET); - fread(poses, sizeof(IQMPose)*iqm.num_poses, 1, iqmFile); - - animation.jointCount = iqm.num_poses; - animation.joints = malloc(sizeof(Joint)*iqm.num_poses); - - for (int j = 0; j < iqm.num_poses; j++) - { - strcpy(animation.joints[j].name, ANIMJOINTNAME); - animation.joints[j].parent = poses[j].parent; - } - - // animations - IQMAnim anim = {0}; - fseek(iqmFile, iqm.ofs_anims, SEEK_SET); - fread(&anim, sizeof(IQMAnim), 1, iqmFile); - - animation.frameCount = anim.num_frames; - animation.framerate = anim.framerate; - - // frameposes - unsigned short *framedata = malloc(sizeof(unsigned short)*iqm.num_frames*iqm.num_framechannels); - fseek(iqmFile, iqm.ofs_frames, SEEK_SET); - fread(framedata, sizeof(unsigned short)*iqm.num_frames*iqm.num_framechannels, 1, iqmFile); - - animation.framepose = malloc(sizeof(Pose*)*anim.num_frames); - for (int j = 0; j < anim.num_frames; j++) animation.framepose[j] = malloc(sizeof(Pose)*iqm.num_poses); - - int dcounter = anim.first_frame*iqm.num_framechannels; - - for (int frame = 0; frame < anim.num_frames; frame++) - { - for (int i = 0; i < iqm.num_poses; i++) - { - animation.framepose[frame][i].translation.x = poses[i].channeloffset[0]; - - if (poses[i].mask & 0x01) - { - animation.framepose[frame][i].translation.x += framedata[dcounter]*poses[i].channelscale[0]; - dcounter++; - } - - animation.framepose[frame][i].translation.y = poses[i].channeloffset[1]; - - if (poses[i].mask & 0x02) - { - animation.framepose[frame][i].translation.y += framedata[dcounter]*poses[i].channelscale[1]; - dcounter++; - } - - animation.framepose[frame][i].translation.z = poses[i].channeloffset[2]; - - if (poses[i].mask & 0x04) - { - animation.framepose[frame][i].translation.z += framedata[dcounter]*poses[i].channelscale[2]; - dcounter++; - } - - animation.framepose[frame][i].rotation.x = poses[i].channeloffset[3]; - - if (poses[i].mask & 0x08) - { - animation.framepose[frame][i].rotation.x += framedata[dcounter]*poses[i].channelscale[3]; - dcounter++; - } - - animation.framepose[frame][i].rotation.y = poses[i].channeloffset[4]; - - if (poses[i].mask & 0x10) - { - animation.framepose[frame][i].rotation.y += framedata[dcounter]*poses[i].channelscale[4]; - dcounter++; - } - - animation.framepose[frame][i].rotation.z = poses[i].channeloffset[5]; - - if (poses[i].mask & 0x20) - { - animation.framepose[frame][i].rotation.z += framedata[dcounter]*poses[i].channelscale[5]; - dcounter++; - } - - animation.framepose[frame][i].rotation.w = poses[i].channeloffset[6]; - - if (poses[i].mask & 0x40) - { - animation.framepose[frame][i].rotation.w += framedata[dcounter]*poses[i].channelscale[6]; - dcounter++; - } - - animation.framepose[frame][i].scale.x = poses[i].channeloffset[7]; - - if (poses[i].mask & 0x80) - { - animation.framepose[frame][i].scale.x += framedata[dcounter]*poses[i].channelscale[7]; - dcounter++; - } - - animation.framepose[frame][i].scale.y = poses[i].channeloffset[8]; - - if (poses[i].mask & 0x100) - { - animation.framepose[frame][i].scale.y += framedata[dcounter]*poses[i].channelscale[8]; - dcounter++; - } - - animation.framepose[frame][i].scale.z = poses[i].channeloffset[9]; - - if (poses[i].mask & 0x200) - { - animation.framepose[frame][i].scale.z += framedata[dcounter]*poses[i].channelscale[9]; - dcounter++; - } - - animation.framepose[frame][i].rotation = QuaternionNormalize(animation.framepose[frame][i].rotation); - } - } - - // Build frameposes - for (int frame = 0; frame < anim.num_frames; frame++) - { - for (int i = 0; i < animation.jointCount; i++) - { - if (animation.joints[i].parent >= 0) - { - animation.framepose[frame][i].rotation = QuaternionMultiply(animation.framepose[frame][animation.joints[i].parent].rotation, animation.framepose[frame][i].rotation); - animation.framepose[frame][i].translation = Vector3RotateByQuaternion(animation.framepose[frame][i].translation, animation.framepose[frame][animation.joints[i].parent].rotation); - animation.framepose[frame][i].translation = Vector3Add(animation.framepose[frame][i].translation, animation.framepose[frame][animation.joints[i].parent].translation); - animation.framepose[frame][i].scale = Vector3MultiplyV(animation.framepose[frame][i].scale, animation.framepose[frame][animation.joints[i].parent].scale); - } - } - } - - free(framedata); - free(poses); - - fclose(iqmFile); - - return animation; -} - -// Unload animated model -void UnloadAnimatedModel(AnimatedModel model) -{ - free(model.materials); - free(model.meshMaterialId); - free(model.joints); - free(model.basepose); - - for (int i = 0; i < model.meshCount; i++) rlUnloadMesh(&model.meshes[i]); - - free(model.meshes); -} - -// Unload animation -void UnloadAnimation(Animation anim) -{ - free(anim.joints); - free(anim.framepose); - - for (int i = 0; i < anim.frameCount; i++) free(anim.framepose[i]); -} - -// Check if skeletons match, only parents and jointCount are checked -bool CheckSkeletonsMatch(AnimatedModel model, Animation anim) -{ - if (model.jointCount != anim.jointCount) return 0; - - for (int i = 0; i < model.jointCount; i++) - { - if (model.joints[i].parent != anim.joints[i].parent) return 0; - } - - return 1; -} - -// Calculate the animated vertex positions and normals based on an animation at a given frame -void AnimateModel(AnimatedModel model, Animation anim, int frame) -{ - if (frame >= anim.frameCount) frame = frame%anim.frameCount; - - for (int m = 0; m < model.meshCount; m++) - { - Vector3 outv = {0}; - Vector3 outn = {0}; - - Vector3 baset = {0}; - Quaternion baser = {0}; - Vector3 bases = {0}; - - Vector3 outt = {0}; - Quaternion outr = {0}; - Vector3 outs = {0}; - - int vcounter = 0; - int wcounter = 0; - int weightId = 0; - - for (int i = 0; i < model.meshes[m].vertexCount; i++) - { - weightId = model.meshes[m].weightId[wcounter]; - baset = model.basepose[weightId].translation; - baser = model.basepose[weightId].rotation; - bases = model.basepose[weightId].scale; - outt = anim.framepose[frame][weightId].translation; - outr = anim.framepose[frame][weightId].rotation; - outs = anim.framepose[frame][weightId].scale; - - // vertices - // NOTE: We use meshes.baseVertices (default position) to calculate meshes.vertices (animated position) - outv = (Vector3){ model.meshes[m].baseVertices[vcounter], model.meshes[m].baseVertices[vcounter + 1], model.meshes[m].baseVertices[vcounter + 2] }; - outv = Vector3MultiplyV(outv, outs); - outv = Vector3Subtract(outv, baset); - outv = Vector3RotateByQuaternion(outv, QuaternionMultiply(outr, QuaternionInvert(baser))); - outv = Vector3Add(outv, outt); - model.meshes[m].vertices[vcounter] = outv.x; - model.meshes[m].vertices[vcounter + 1] = outv.y; - model.meshes[m].vertices[vcounter + 2] = outv.z; - - // normals - // NOTE: We use meshes.baseNormals (default normal) to calculate meshes.normals (animated normals) - outn = (Vector3){ model.meshes[m].baseNormals[vcounter], model.meshes[m].baseNormals[vcounter + 1], model.meshes[m].baseNormals[vcounter + 2] }; - outn = Vector3RotateByQuaternion(outn, QuaternionMultiply(outr, QuaternionInvert(baser))); - model.meshes[m].normals[vcounter] = outn.x; - model.meshes[m].normals[vcounter + 1] = outn.y; - model.meshes[m].normals[vcounter + 2] = outn.z; - vcounter += 3; - wcounter += 4; - } - } -} - -// Draw an animated model -void DrawAnimatedModel(AnimatedModel model, Vector3 position, float scale, Color tint) -{ - Vector3 vScale = { scale, scale, scale }; - Vector3 rotationAxis = { 1.0f, 0.0f,0.0f }; - - DrawAnimatedModelEx(model, position, rotationAxis, -90.0f, vScale, tint); -} - -// Draw an animated model with extended parameters -void DrawAnimatedModelEx(AnimatedModel model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint) -{ - if (model.materialCount == 0) - { - TraceLog(LOG_WARNING,"No materials set, can't draw animated meshes\n"); - return; - } - - Matrix matScale = MatrixScale(scale.x, scale.y, scale.z); - Matrix matRotation = MatrixRotate(rotationAxis, rotationAngle*DEG2RAD); - Matrix matTranslation = MatrixTranslate(position.x, position.y, position.z); - - Matrix matTransform = MatrixMultiply(MatrixMultiply(matScale, matRotation), matTranslation); - model.transform = MatrixMultiply(model.transform, matTransform); - - for (int i = 0; i < model.meshCount; i++) - { - rlUpdateMesh(model.meshes[i], 0, model.meshes[i].vertexCount); // Update vertex position - rlUpdateMesh(model.meshes[i], 2, model.meshes[i].vertexCount); // Update vertex normals - rlDrawMesh(model.meshes[i], model.materials[model.meshMaterialId[i]], model.transform); // Draw meshes - } -} - -// Load animated model meshes from IQM file -static AnimatedModel LoadIQM(const char *filename) -{ - AnimatedModel model = { 0 }; - - FILE *iqmFile; - IQMHeader iqm; - - IQMMesh *imesh; - IQMTriangle *tri; - IQMVertexArray *va; - IQMJoint *ijoint; - - float *vertex; - float *normal; - float *text; - char *blendi; - unsigned char *blendw; - - iqmFile = fopen(filename, "rb"); - - if (!iqmFile) - { - TraceLog(LOG_ERROR, "[%s] Unable to open file", filename); - return model; - } - - // header - fread(&iqm,sizeof(IQMHeader), 1, iqmFile); - - if (strncmp(iqm.magic, IQM_MAGIC, sizeof(IQM_MAGIC))) - { - TraceLog(LOG_ERROR, "Magic Number \"%s\"does not match.", iqm.magic); - fclose(iqmFile); - return model; - } - - if(iqm.version != IQM_VERSION) - { - TraceLog(LOG_ERROR, "IQM version %i is incorrect.", iqm.version); - fclose(iqmFile); - return model; - } - - // meshes - imesh = malloc(sizeof(IQMMesh)*iqm.num_meshes); - fseek(iqmFile, iqm.ofs_meshes, SEEK_SET); - fread(imesh, sizeof(IQMMesh)*iqm.num_meshes, 1, iqmFile); - - model.meshCount = iqm.num_meshes; - model.meshes = malloc(sizeof(Mesh)*iqm.num_meshes); - - char name[MESH_NAME_LENGTH]; - - for (int i = 0; i < iqm.num_meshes; i++) - { - fseek(iqmFile,iqm.ofs_text+imesh[i].name,SEEK_SET); - fread(name, sizeof(char)*MESH_NAME_LENGTH, 1, iqmFile); // Mesh name not used... - model.meshes[i].vertexCount = imesh[i].num_vertexes; - - model.meshes[i].baseVertices = malloc(sizeof(float)*imesh[i].num_vertexes*3); // Default IQM base position - model.meshes[i].baseNormals = malloc(sizeof(float)*imesh[i].num_vertexes*3); // Default IQM base normal - - model.meshes[i].texcoords = malloc(sizeof(float)*imesh[i].num_vertexes*2); - model.meshes[i].weightId = malloc(sizeof(int)*imesh[i].num_vertexes*4); - model.meshes[i].weightBias = malloc(sizeof(float)*imesh[i].num_vertexes*4); - - model.meshes[i].triangleCount = imesh[i].num_triangles; - model.meshes[i].indices = malloc(sizeof(unsigned short)*imesh[i].num_triangles*3); - - // What we actually process for rendering, should be updated transforming meshes.vertices and meshes.normals - model.meshes[i].vertices = malloc(sizeof(float)*imesh[i].num_vertexes*3); - model.meshes[i].normals = malloc(sizeof(float)*imesh[i].num_vertexes*3); - } - - // tris - tri = malloc(sizeof(IQMTriangle)*iqm.num_triangles); - fseek(iqmFile, iqm.ofs_triangles, SEEK_SET); - fread(tri, sizeof(IQMTriangle)*iqm.num_triangles, 1, iqmFile); - - for (int m = 0; m < iqm.num_meshes; m++) - { - int tcounter = 0; - - for (int i = imesh[m].first_triangle; i < imesh[m].first_triangle+imesh[m].num_triangles; i++) - { - // IQM triangles are stored counter clockwise, but raylib sets opengl to clockwise drawing, so we swap them around - model.meshes[m].indices[tcounter+2] = tri[i].vertex[0] - imesh[m].first_vertex; - model.meshes[m].indices[tcounter+1] = tri[i].vertex[1] - imesh[m].first_vertex; - model.meshes[m].indices[tcounter] = tri[i].vertex[2] - imesh[m].first_vertex; - tcounter += 3; - } - } - - // vertarrays - va = malloc(sizeof(IQMVertexArray)*iqm.num_vertexarrays); - fseek(iqmFile, iqm.ofs_vertexarrays, SEEK_SET); - fread(va, sizeof(IQMVertexArray)*iqm.num_vertexarrays, 1, iqmFile); - - for (int i = 0; i < iqm.num_vertexarrays; i++) - { - switch (va[i].type) - { - case IQM_POSITION: - { - vertex = malloc(sizeof(float)*iqm.num_vertexes*3); - fseek(iqmFile, va[i].offset, SEEK_SET); - fread(vertex, sizeof(float)*iqm.num_vertexes*3, 1, iqmFile); - - for (int m = 0; m < iqm.num_meshes; m++) - { - int vcounter = 0; - for (int i = imesh[m].first_vertex*3; i < (imesh[m].first_vertex + imesh[m].num_vertexes)*3; i++) - { - model.meshes[m].vertices[vcounter] = vertex[i]; - model.meshes[m].baseVertices[vcounter] = vertex[i]; - vcounter++; - } - } - } break; - case IQM_NORMAL: - { - normal = malloc(sizeof(float)*iqm.num_vertexes*3); - fseek(iqmFile, va[i].offset, SEEK_SET); - fread(normal, sizeof(float)*iqm.num_vertexes*3, 1, iqmFile); - - for (int m = 0; m < iqm.num_meshes; m++) - { - int vcounter = 0; - for (int i = imesh[m].first_vertex*3; i < (imesh[m].first_vertex + imesh[m].num_vertexes)*3; i++) - { - model.meshes[m].normals[vcounter] = normal[i]; - model.meshes[m].baseNormals[vcounter] = normal[i]; - vcounter++; - } - } - } break; - case IQM_TEXCOORD: - { - text = malloc(sizeof(float)*iqm.num_vertexes*2); - fseek(iqmFile, va[i].offset, SEEK_SET); - fread(text, sizeof(float)*iqm.num_vertexes*2, 1, iqmFile); - - for (int m = 0; m < iqm.num_meshes; m++) - { - int vcounter = 0; - for (int i = imesh[m].first_vertex*2; i < (imesh[m].first_vertex + imesh[m].num_vertexes)*2; i++) - { - model.meshes[m].texcoords[vcounter] = text[i]; - vcounter++; - } - } - } break; - case IQM_BLENDINDEXES: - { - blendi = malloc(sizeof(char)*iqm.num_vertexes*4); - fseek(iqmFile, va[i].offset, SEEK_SET); - fread(blendi, sizeof(char)*iqm.num_vertexes*4, 1, iqmFile); - - for (int m = 0; m < iqm.num_meshes; m++) - { - int vcounter = 0; - for (int i = imesh[m].first_vertex*4; i < (imesh[m].first_vertex + imesh[m].num_vertexes)*4; i++) - { - model.meshes[m].weightId[vcounter] = blendi[i]; - vcounter++; - } - } - } break; - case IQM_BLENDWEIGHTS: - { - blendw = malloc(sizeof(unsigned char)*iqm.num_vertexes*4); - fseek(iqmFile,va[i].offset,SEEK_SET); - fread(blendw,sizeof(unsigned char)*iqm.num_vertexes*4,1,iqmFile); - - for (int m = 0; m < iqm.num_meshes; m++) - { - int vcounter = 0; - for (int i = imesh[m].first_vertex*4; i < (imesh[m].first_vertex + imesh[m].num_vertexes)*4; i++) - { - model.meshes[m].weightBias[vcounter] = blendw[i]/255.0f; - vcounter++; - } - } - } break; - } - } - - // joints, include base poses - ijoint = malloc(sizeof(IQMJoint)*iqm.num_joints); - fseek(iqmFile, iqm.ofs_joints, SEEK_SET); - fread(ijoint, sizeof(IQMJoint)*iqm.num_joints, 1, iqmFile); - - model.jointCount = iqm.num_joints; - model.joints = malloc(sizeof(Joint)*iqm.num_joints); - model.basepose = malloc(sizeof(Pose)*iqm.num_joints); - - for (int i = 0; i < iqm.num_joints; i++) - { - // joints - model.joints[i].parent = ijoint[i].parent; - fseek(iqmFile, iqm.ofs_text + ijoint[i].name, SEEK_SET); - fread(model.joints[i].name,sizeof(char)*JOINT_NAME_LENGTH, 1, iqmFile); - - // basepose - model.basepose[i].translation.x = ijoint[i].translate[0]; - model.basepose[i].translation.y = ijoint[i].translate[1]; - model.basepose[i].translation.z = ijoint[i].translate[2]; - - model.basepose[i].rotation.x = ijoint[i].rotate[0]; - model.basepose[i].rotation.y = ijoint[i].rotate[1]; - model.basepose[i].rotation.z = ijoint[i].rotate[2]; - model.basepose[i].rotation.w = ijoint[i].rotate[3]; - - model.basepose[i].scale.x = ijoint[i].scale[0]; - model.basepose[i].scale.y = ijoint[i].scale[1]; - model.basepose[i].scale.z = ijoint[i].scale[2]; - } - - // build base pose - for (int i = 0; i < model.jointCount; i++) - { - if (model.joints[i].parent >= 0) - { - model.basepose[i].rotation = QuaternionMultiply(model.basepose[model.joints[i].parent].rotation, model.basepose[i].rotation); - model.basepose[i].translation = Vector3RotateByQuaternion(model.basepose[i].translation, model.basepose[model.joints[i].parent].rotation); - model.basepose[i].translation = Vector3Add(model.basepose[i].translation, model.basepose[model.joints[i].parent].translation); - model.basepose[i].scale = Vector3MultiplyV(model.basepose[i].scale, model.basepose[model.joints[i].parent].scale); - } - } - - fclose(iqmFile); - free(imesh); - free(tri); - free(va); - free(vertex); - free(normal); - free(text); - free(blendi); - free(blendw); - free(ijoint); - - return model; -} #endif diff --git a/src/models.c b/src/models.c index 239ab5d84..e437a0ab8 100644 --- a/src/models.c +++ b/src/models.c @@ -697,6 +697,18 @@ void UnloadModel(Model model) TraceLog(LOG_INFO, "Unloaded model data from RAM and VRAM"); } +// Load meshes from model file +Mesh *LoadMeshes(const char *fileName, int *meshCount) +{ + Mesh *meshes = NULL; + int count = 0; + + // TODO: Load meshes from file (OBJ, IQM, GLTF) + + *meshCount = count; + return meshes; +} + // Unload mesh from memory (RAM and/or VRAM) void UnloadMesh(Mesh *mesh) { @@ -759,6 +771,386 @@ void ExportMesh(Mesh mesh, const char *fileName) else TraceLog(LOG_WARNING, "Mesh could not be exported."); } +// Load materials from model file +Material *LoadMaterials(const char *fileName, int *materialCount) +{ + Material *materials = NULL; + unsigned int count = 0; + + // TODO: Support IQM and GLTF for materials parsing + +#if defined(SUPPORT_FILEFORMAT_MTL) + if (IsFileExtension(fileName, ".mtl")) + { + tinyobj_material_t *mats; + + int result = tinyobj_parse_mtl_file(&mats, &count, fileName); + + // TODO: Process materials to return + + tinyobj_materials_free(mats, count); + } +#else + TraceLog(LOG_WARNING, "[%s] Materials file not supported", fileName); +#endif + + // Set materials shader to default (DIFFUSE, SPECULAR, NORMAL) + for (int i = 0; i < count; i++) materials[i].shader = GetShaderDefault(); + + *materialCount = count; + return materials; +} + +// Load default material (Supports: DIFFUSE, SPECULAR, NORMAL maps) +Material LoadMaterialDefault(void) +{ + Material material = { 0 }; + + material.shader = GetShaderDefault(); + material.maps[MAP_DIFFUSE].texture = GetTextureDefault(); // White texture (1x1 pixel) + //material.maps[MAP_NORMAL].texture; // NOTE: By default, not set + //material.maps[MAP_SPECULAR].texture; // NOTE: By default, not set + + material.maps[MAP_DIFFUSE].color = WHITE; // Diffuse color + material.maps[MAP_SPECULAR].color = WHITE; // Specular color + + return material; +} + +// Unload material from memory +void UnloadMaterial(Material material) +{ + // Unload material shader (avoid unloading default shader, managed by raylib) + if (material.shader.id != GetShaderDefault().id) UnloadShader(material.shader); + + // Unload loaded texture maps (avoid unloading default texture, managed by raylib) + for (int i = 0; i < MAX_MATERIAL_MAPS; i++) + { + if (material.maps[i].texture.id != GetTextureDefault().id) rlDeleteTextures(material.maps[i].texture.id); + } +} + +// Set texture for a material map type (MAP_DIFFUSE, MAP_SPECULAR...) +// NOTE: Previous texture should be manually unloaded +void SetMaterialTexture(Material *material, int mapType, Texture2D texture) +{ + material->maps[mapType].texture = texture; +} + +// Set the material for a mesh +void SetModelMeshMaterial(Model *model, int meshId, int materialId) +{ + if (meshId >= model->meshCount) TraceLog(LOG_WARNING, "Mesh id greater than mesh count"); + else if (materialId >= model->materialCount) TraceLog(LOG_WARNING,"Material id greater than material count"); + else model->meshMaterial[meshId] = materialId; +} + +// Load model animations from file +ModelAnimation *LoadModelAnimations(const char *filename, int *animCount) +{ + ModelAnimation *animations = (ModelAnimation *)malloc(1*sizeof(ModelAnimation)); + int count = 1; + + #define IQM_MAGIC "INTERQUAKEMODEL" // IQM file magic number + #define IQM_VERSION 2 // only IQM version 2 supported + + typedef struct IQMHeader { + char magic[16]; + unsigned int version; + unsigned int filesize; + unsigned int flags; + unsigned int num_text, ofs_text; + unsigned int num_meshes, ofs_meshes; + unsigned int num_vertexarrays, num_vertexes, ofs_vertexarrays; + unsigned int num_triangles, ofs_triangles, ofs_adjacency; + unsigned int num_joints, ofs_joints; + unsigned int num_poses, ofs_poses; + unsigned int num_anims, ofs_anims; + unsigned int num_frames, num_framechannels, ofs_frames, ofs_bounds; + unsigned int num_comment, ofs_comment; + unsigned int num_extensions, ofs_extensions; + } IQMHeader; + + typedef struct IQMPose { + int parent; + unsigned int mask; + float channeloffset[10]; + float channelscale[10]; + } IQMPose; + + typedef struct IQMAnim { + unsigned int name; + unsigned int first_frame, num_frames; + float framerate; + unsigned int flags; + } IQMAnim; + + ModelAnimation animation = { 0 }; + + FILE *iqmFile; + IQMHeader iqm; + + iqmFile = fopen(filename,"rb"); + + if (!iqmFile) + { + TraceLog(LOG_ERROR, "[%s] Unable to open file", filename); + } + + // header + fread(&iqm, sizeof(IQMHeader), 1, iqmFile); + + if (strncmp(iqm.magic, IQM_MAGIC, sizeof(IQM_MAGIC))) + { + TraceLog(LOG_ERROR, "Magic Number \"%s\"does not match.", iqm.magic); + fclose(iqmFile); + } + + if (iqm.version != IQM_VERSION) + { + TraceLog(LOG_ERROR, "IQM version %i is incorrect.", iqm.version); + fclose(iqmFile); + } + + // header + if (iqm.num_anims > 1) TraceLog(LOG_WARNING, "More than 1 animation in file, only the first one will be loaded"); + + // bones + IQMPose *poses; + poses = malloc(sizeof(IQMPose)*iqm.num_poses); + fseek(iqmFile, iqm.ofs_poses, SEEK_SET); + fread(poses, sizeof(IQMPose)*iqm.num_poses, 1, iqmFile); + + animation.boneCount = iqm.num_poses; + animation.bones = malloc(sizeof(BoneInfo)*iqm.num_poses); + + for (int j = 0; j < iqm.num_poses; j++) + { + strcpy(animation.bones[j].name, "ANIMJOINTNAME"); + animation.bones[j].parent = poses[j].parent; + } + + // animations + IQMAnim anim = {0}; + fseek(iqmFile, iqm.ofs_anims, SEEK_SET); + fread(&anim, sizeof(IQMAnim), 1, iqmFile); + + animation.frameCount = anim.num_frames; + //animation.framerate = anim.framerate; + + // frameposes + unsigned short *framedata = malloc(sizeof(unsigned short)*iqm.num_frames*iqm.num_framechannels); + fseek(iqmFile, iqm.ofs_frames, SEEK_SET); + fread(framedata, sizeof(unsigned short)*iqm.num_frames*iqm.num_framechannels, 1, iqmFile); + + animation.framePoses = malloc(sizeof(Transform*)*anim.num_frames); + for (int j = 0; j < anim.num_frames; j++) animation.framePoses[j] = malloc(sizeof(Transform)*iqm.num_poses); + + int dcounter = anim.first_frame*iqm.num_framechannels; + + for (int frame = 0; frame < anim.num_frames; frame++) + { + for (int i = 0; i < iqm.num_poses; i++) + { + animation.framePoses[frame][i].translation.x = poses[i].channeloffset[0]; + + if (poses[i].mask & 0x01) + { + animation.framePoses[frame][i].translation.x += framedata[dcounter]*poses[i].channelscale[0]; + dcounter++; + } + + animation.framePoses[frame][i].translation.y = poses[i].channeloffset[1]; + + if (poses[i].mask & 0x02) + { + animation.framePoses[frame][i].translation.y += framedata[dcounter]*poses[i].channelscale[1]; + dcounter++; + } + + animation.framePoses[frame][i].translation.z = poses[i].channeloffset[2]; + + if (poses[i].mask & 0x04) + { + animation.framePoses[frame][i].translation.z += framedata[dcounter]*poses[i].channelscale[2]; + dcounter++; + } + + animation.framePoses[frame][i].rotation.x = poses[i].channeloffset[3]; + + if (poses[i].mask & 0x08) + { + animation.framePoses[frame][i].rotation.x += framedata[dcounter]*poses[i].channelscale[3]; + dcounter++; + } + + animation.framePoses[frame][i].rotation.y = poses[i].channeloffset[4]; + + if (poses[i].mask & 0x10) + { + animation.framePoses[frame][i].rotation.y += framedata[dcounter]*poses[i].channelscale[4]; + dcounter++; + } + + animation.framePoses[frame][i].rotation.z = poses[i].channeloffset[5]; + + if (poses[i].mask & 0x20) + { + animation.framePoses[frame][i].rotation.z += framedata[dcounter]*poses[i].channelscale[5]; + dcounter++; + } + + animation.framePoses[frame][i].rotation.w = poses[i].channeloffset[6]; + + if (poses[i].mask & 0x40) + { + animation.framePoses[frame][i].rotation.w += framedata[dcounter]*poses[i].channelscale[6]; + dcounter++; + } + + animation.framePoses[frame][i].scale.x = poses[i].channeloffset[7]; + + if (poses[i].mask & 0x80) + { + animation.framePoses[frame][i].scale.x += framedata[dcounter]*poses[i].channelscale[7]; + dcounter++; + } + + animation.framePoses[frame][i].scale.y = poses[i].channeloffset[8]; + + if (poses[i].mask & 0x100) + { + animation.framePoses[frame][i].scale.y += framedata[dcounter]*poses[i].channelscale[8]; + dcounter++; + } + + animation.framePoses[frame][i].scale.z = poses[i].channeloffset[9]; + + if (poses[i].mask & 0x200) + { + animation.framePoses[frame][i].scale.z += framedata[dcounter]*poses[i].channelscale[9]; + dcounter++; + } + + animation.framePoses[frame][i].rotation = QuaternionNormalize(animation.framePoses[frame][i].rotation); + } + } + + // Build frameposes + for (int frame = 0; frame < anim.num_frames; frame++) + { + for (int i = 0; i < animation.boneCount; i++) + { + if (animation.bones[i].parent >= 0) + { + animation.framePoses[frame][i].rotation = QuaternionMultiply(animation.framePoses[frame][animation.bones[i].parent].rotation, animation.framePoses[frame][i].rotation); + animation.framePoses[frame][i].translation = Vector3RotateByQuaternion(animation.framePoses[frame][i].translation, animation.framePoses[frame][animation.bones[i].parent].rotation); + animation.framePoses[frame][i].translation = Vector3Add(animation.framePoses[frame][i].translation, animation.framePoses[frame][animation.bones[i].parent].translation); + animation.framePoses[frame][i].scale = Vector3MultiplyV(animation.framePoses[frame][i].scale, animation.framePoses[frame][animation.bones[i].parent].scale); + } + } + } + + free(framedata); + free(poses); + + fclose(iqmFile); + + animations[0] = animation; + + *animCount = count; + return animations; +} + +// Update model animated vertex data (positions and normals) for a given frame +// NOTE: Updated data is uploaded to GPU +void UpdateModelAnimation(Model model, ModelAnimation anim, int frame) +{ + if (frame >= anim.frameCount) frame = frame%anim.frameCount; + + for (int m = 0; m < model.meshCount; m++) + { + Vector3 animVertex = { 0 }; + Vector3 animNormal = { 0 }; + + Vector3 inTranslation = { 0 }; + Quaternion inRotation = { 0 }; + Vector3 inScale = { 0 }; + + Vector3 outTranslation = { 0 }; + Quaternion outRotation = { 0 }; + Vector3 outScale = { 0 }; + + int vCounter = 0; + int boneCounter = 0; + int boneId = 0; + + for (int i = 0; i < model.meshes[m].vertexCount; i++) + { + boneId = model.meshes[m].boneIds[boneCounter]; + inTranslation = model.bindPose[boneId].translation; + inRotation = model.bindPose[boneId].rotation; + inScale = model.bindPose[boneId].scale; + outTranslation = anim.framePoses[frame][boneId].translation; + outRotation = anim.framePoses[frame][boneId].rotation; + outScale = anim.framePoses[frame][boneId].scale; + + // Vertices processing + // NOTE: We use meshes.vertices (default vertex position) to calculate meshes.animVertices (animated vertex position) + animVertex = (Vector3){ model.meshes[m].vertices[vCounter], model.meshes[m].vertices[vCounter + 1], model.meshes[m].vertices[vCounter + 2] }; + animVertex = Vector3MultiplyV(animVertex, outScale); + animVertex = Vector3Subtract(animVertex, inTranslation); + animVertex = Vector3RotateByQuaternion(animVertex, QuaternionMultiply(outRotation, QuaternionInvert(inRotation))); + animVertex = Vector3Add(animVertex, outTranslation); + model.meshes[m].animVertices[vCounter] = animVertex.x; + model.meshes[m].animVertices[vCounter + 1] = animVertex.y; + model.meshes[m].animVertices[vCounter + 2] = animVertex.z; + + // Normals processing + // NOTE: We use meshes.baseNormals (default normal) to calculate meshes.normals (animated normals) + animNormal = (Vector3){ model.meshes[m].normals[vCounter], model.meshes[m].normals[vCounter + 1], model.meshes[m].normals[vCounter + 2] }; + animNormal = Vector3RotateByQuaternion(animNormal, QuaternionMultiply(outRotation, QuaternionInvert(inRotation))); + model.meshes[m].animNormals[vCounter] = animNormal.x; + model.meshes[m].animNormals[vCounter + 1] = animNormal.y; + model.meshes[m].animNormals[vCounter + 2] = animNormal.z; + vCounter += 3; + + boneCounter += 4; + } + + // Upload new vertex data to GPU for model drawing + rlUpdateBuffer(model.meshes[m].vboId[0], model.meshes[m].animVertices, model.meshes[m].vertexCount*3*sizeof(float)); // Update vertex position + rlUpdateBuffer(model.meshes[m].vboId[2], model.meshes[m].animVertices, model.meshes[m].vertexCount*3*sizeof(float)); // Update vertex normals + } +} + +// Unload animation data +void UnloadModelAnimation(ModelAnimation anim) +{ + for (int i = 0; i < anim.frameCount; i++) free(anim.framePoses[i]); + + free(anim.bones); + free(anim.framePoses); +} + +// Check model animation skeleton match +// NOTE: Only number of bones and parent connections are checked +bool IsModelAnimationValid(Model model, ModelAnimation anim) +{ + int result = true; + + if (model.boneCount != anim.boneCount) result = false; + else + { + for (int i = 0; i < model.boneCount; i++) + { + if (model.bones[i].parent != anim.bones[i].parent) { result = false; break; } + } + } + + return result; +} + #if defined(SUPPORT_MESH_GENERATION) // Generate polygonal mesh Mesh GenMeshPoly(int sides, float radius) @@ -1807,59 +2199,124 @@ Mesh GenMeshCubicmap(Image cubicmap, Vector3 cubeSize) } #endif // SUPPORT_MESH_GENERATION -// Load material data (from file) -Material LoadMaterial(const char *fileName) +// Compute mesh bounding box limits +// NOTE: minVertex and maxVertex should be transformed by model transform matrix +BoundingBox MeshBoundingBox(Mesh mesh) { - Material material = { 0 }; + // Get min and max vertex to construct bounds (AABB) + Vector3 minVertex = { 0 }; + Vector3 maxVertex = { 0 }; -#if defined(SUPPORT_FILEFORMAT_MTL) - if (IsFileExtension(fileName, ".mtl")) + if (mesh.vertices != NULL) { - tinyobj_material_t *materials; - unsigned int materialCount = 0; + minVertex = (Vector3){ mesh.vertices[0], mesh.vertices[1], mesh.vertices[2] }; + maxVertex = (Vector3){ mesh.vertices[0], mesh.vertices[1], mesh.vertices[2] }; - int result = tinyobj_parse_mtl_file(&materials, &materialCount, fileName); - - // TODO: Process materials to return - - tinyobj_materials_free(materials, materialCount); + for (int i = 1; i < mesh.vertexCount; i++) + { + minVertex = Vector3Min(minVertex, (Vector3){ mesh.vertices[i*3], mesh.vertices[i*3 + 1], mesh.vertices[i*3 + 2] }); + maxVertex = Vector3Max(maxVertex, (Vector3){ mesh.vertices[i*3], mesh.vertices[i*3 + 1], mesh.vertices[i*3 + 2] }); + } } -#else - TraceLog(LOG_WARNING, "[%s] Material fileformat not supported, it can't be loaded", fileName); -#endif - // Our material uses the default shader (DIFFUSE, SPECULAR, NORMAL) - material.shader = GetShaderDefault(); + // Create the bounding box + BoundingBox box = { 0 }; + box.min = minVertex; + box.max = maxVertex; - return material; + return box; } -// Load default material (Supports: DIFFUSE, SPECULAR, NORMAL maps) -Material LoadMaterialDefault(void) +// Compute mesh tangents +// NOTE: To calculate mesh tangents and binormals we need mesh vertex positions and texture coordinates +// Implementation base don: https://answers.unity.com/questions/7789/calculating-tangents-vector4.html +void MeshTangents(Mesh *mesh) { - Material material = { 0 }; + if (mesh->tangents == NULL) mesh->tangents = (float *)malloc(mesh->vertexCount*4*sizeof(float)); + else TraceLog(LOG_WARNING, "Mesh tangents already exist"); - material.shader = GetShaderDefault(); - material.maps[MAP_DIFFUSE].texture = GetTextureDefault(); // White texture (1x1 pixel) - //material.maps[MAP_NORMAL].texture; // NOTE: By default, not set - //material.maps[MAP_SPECULAR].texture; // NOTE: By default, not set + Vector3 *tan1 = (Vector3 *)malloc(mesh->vertexCount*sizeof(Vector3)); + Vector3 *tan2 = (Vector3 *)malloc(mesh->vertexCount*sizeof(Vector3)); - material.maps[MAP_DIFFUSE].color = WHITE; // Diffuse color - material.maps[MAP_SPECULAR].color = WHITE; // Specular color - - return material; -} - -// Unload material from memory -void UnloadMaterial(Material material) -{ - // Unload material shader (avoid unloading default shader, managed by raylib) - if (material.shader.id != GetShaderDefault().id) UnloadShader(material.shader); - - // Unload loaded texture maps (avoid unloading default texture, managed by raylib) - for (int i = 0; i < MAX_MATERIAL_MAPS; i++) + for (int i = 0; i < mesh->vertexCount; i += 3) { - if (material.maps[i].texture.id != GetTextureDefault().id) rlDeleteTextures(material.maps[i].texture.id); + // Get triangle vertices + Vector3 v1 = { mesh->vertices[(i + 0)*3 + 0], mesh->vertices[(i + 0)*3 + 1], mesh->vertices[(i + 0)*3 + 2] }; + Vector3 v2 = { mesh->vertices[(i + 1)*3 + 0], mesh->vertices[(i + 1)*3 + 1], mesh->vertices[(i + 1)*3 + 2] }; + Vector3 v3 = { mesh->vertices[(i + 2)*3 + 0], mesh->vertices[(i + 2)*3 + 1], mesh->vertices[(i + 2)*3 + 2] }; + + // Get triangle texcoords + Vector2 uv1 = { mesh->texcoords[(i + 0)*2 + 0], mesh->texcoords[(i + 0)*2 + 1] }; + Vector2 uv2 = { mesh->texcoords[(i + 1)*2 + 0], mesh->texcoords[(i + 1)*2 + 1] }; + Vector2 uv3 = { mesh->texcoords[(i + 2)*2 + 0], mesh->texcoords[(i + 2)*2 + 1] }; + + float x1 = v2.x - v1.x; + float y1 = v2.y - v1.y; + float z1 = v2.z - v1.z; + float x2 = v3.x - v1.x; + float y2 = v3.y - v1.y; + float z2 = v3.z - v1.z; + + float s1 = uv2.x - uv1.x; + float t1 = uv2.y - uv1.y; + float s2 = uv3.x - uv1.x; + float t2 = uv3.y - uv1.y; + + float div = s1*t2 - s2*t1; + float r = (div == 0.0f)? 0.0f : 1.0f/div; + + Vector3 sdir = { (t2*x1 - t1*x2)*r, (t2*y1 - t1*y2)*r, (t2*z1 - t1*z2)*r }; + Vector3 tdir = { (s1*x2 - s2*x1)*r, (s1*y2 - s2*y1)*r, (s1*z2 - s2*z1)*r }; + + tan1[i + 0] = sdir; + tan1[i + 1] = sdir; + tan1[i + 2] = sdir; + + tan2[i + 0] = tdir; + tan2[i + 1] = tdir; + tan2[i + 2] = tdir; + } + + // Compute tangents considering normals + for (int i = 0; i < mesh->vertexCount; ++i) + { + Vector3 normal = { mesh->normals[i*3 + 0], mesh->normals[i*3 + 1], mesh->normals[i*3 + 2] }; + Vector3 tangent = tan1[i]; + + // TODO: Review, not sure if tangent computation is right, just used reference proposed maths... + #if defined(COMPUTE_TANGENTS_METHOD_01) + Vector3 tmp = Vector3Subtract(tangent, Vector3Multiply(normal, Vector3DotProduct(normal, tangent))); + tmp = Vector3Normalize(tmp); + mesh->tangents[i*4 + 0] = tmp.x; + mesh->tangents[i*4 + 1] = tmp.y; + mesh->tangents[i*4 + 2] = tmp.z; + mesh->tangents[i*4 + 3] = 1.0f; + #else + Vector3OrthoNormalize(&normal, &tangent); + mesh->tangents[i*4 + 0] = tangent.x; + mesh->tangents[i*4 + 1] = tangent.y; + mesh->tangents[i*4 + 2] = tangent.z; + mesh->tangents[i*4 + 3] = (Vector3DotProduct(Vector3CrossProduct(normal, tangent), tan2[i]) < 0.0f)? -1.0f : 1.0f; + #endif + } + + free(tan1); + free(tan2); + + TraceLog(LOG_INFO, "Tangents computed for mesh"); +} + +// Compute mesh binormals (aka bitangent) +void MeshBinormals(Mesh *mesh) +{ + for (int i = 0; i < mesh->vertexCount; i++) + { + Vector3 normal = { mesh->normals[i*3 + 0], mesh->normals[i*3 + 1], mesh->normals[i*3 + 2] }; + Vector3 tangent = { mesh->tangents[i*4 + 0], mesh->tangents[i*4 + 1], mesh->tangents[i*4 + 2] }; + float tangentW = mesh->tangents[i*4 + 3]; + + // TODO: Register computed binormal in mesh->binormal? + // Vector3 binormal = Vector3Multiply(Vector3CrossProduct(normal, tangent), tangentW); } } @@ -2239,129 +2696,6 @@ RayHitInfo GetCollisionRayGround(Ray ray, float groundHeight) return result; } -// Compute mesh bounding box limits -// NOTE: minVertex and maxVertex should be transformed by model transform matrix -BoundingBox MeshBoundingBox(Mesh mesh) -{ - // Get min and max vertex to construct bounds (AABB) - Vector3 minVertex = { 0 }; - Vector3 maxVertex = { 0 }; - - printf("Mesh vertex count: %i\n", mesh.vertexCount); - - if (mesh.vertices != NULL) - { - minVertex = (Vector3){ mesh.vertices[0], mesh.vertices[1], mesh.vertices[2] }; - maxVertex = (Vector3){ mesh.vertices[0], mesh.vertices[1], mesh.vertices[2] }; - - for (int i = 1; i < mesh.vertexCount; i++) - { - minVertex = Vector3Min(minVertex, (Vector3){ mesh.vertices[i*3], mesh.vertices[i*3 + 1], mesh.vertices[i*3 + 2] }); - maxVertex = Vector3Max(maxVertex, (Vector3){ mesh.vertices[i*3], mesh.vertices[i*3 + 1], mesh.vertices[i*3 + 2] }); - } - } - - // Create the bounding box - BoundingBox box = { 0 }; - box.min = minVertex; - box.max = maxVertex; - - return box; -} - -// Compute mesh tangents -// NOTE: To calculate mesh tangents and binormals we need mesh vertex positions and texture coordinates -// Implementation base don: https://answers.unity.com/questions/7789/calculating-tangents-vector4.html -void MeshTangents(Mesh *mesh) -{ - if (mesh->tangents == NULL) mesh->tangents = (float *)malloc(mesh->vertexCount*4*sizeof(float)); - else TraceLog(LOG_WARNING, "Mesh tangents already exist"); - - Vector3 *tan1 = (Vector3 *)malloc(mesh->vertexCount*sizeof(Vector3)); - Vector3 *tan2 = (Vector3 *)malloc(mesh->vertexCount*sizeof(Vector3)); - - for (int i = 0; i < mesh->vertexCount; i += 3) - { - // Get triangle vertices - Vector3 v1 = { mesh->vertices[(i + 0)*3 + 0], mesh->vertices[(i + 0)*3 + 1], mesh->vertices[(i + 0)*3 + 2] }; - Vector3 v2 = { mesh->vertices[(i + 1)*3 + 0], mesh->vertices[(i + 1)*3 + 1], mesh->vertices[(i + 1)*3 + 2] }; - Vector3 v3 = { mesh->vertices[(i + 2)*3 + 0], mesh->vertices[(i + 2)*3 + 1], mesh->vertices[(i + 2)*3 + 2] }; - - // Get triangle texcoords - Vector2 uv1 = { mesh->texcoords[(i + 0)*2 + 0], mesh->texcoords[(i + 0)*2 + 1] }; - Vector2 uv2 = { mesh->texcoords[(i + 1)*2 + 0], mesh->texcoords[(i + 1)*2 + 1] }; - Vector2 uv3 = { mesh->texcoords[(i + 2)*2 + 0], mesh->texcoords[(i + 2)*2 + 1] }; - - float x1 = v2.x - v1.x; - float y1 = v2.y - v1.y; - float z1 = v2.z - v1.z; - float x2 = v3.x - v1.x; - float y2 = v3.y - v1.y; - float z2 = v3.z - v1.z; - - float s1 = uv2.x - uv1.x; - float t1 = uv2.y - uv1.y; - float s2 = uv3.x - uv1.x; - float t2 = uv3.y - uv1.y; - - float div = s1*t2 - s2*t1; - float r = (div == 0.0f)? 0.0f : 1.0f/div; - - Vector3 sdir = { (t2*x1 - t1*x2)*r, (t2*y1 - t1*y2)*r, (t2*z1 - t1*z2)*r }; - Vector3 tdir = { (s1*x2 - s2*x1)*r, (s1*y2 - s2*y1)*r, (s1*z2 - s2*z1)*r }; - - tan1[i + 0] = sdir; - tan1[i + 1] = sdir; - tan1[i + 2] = sdir; - - tan2[i + 0] = tdir; - tan2[i + 1] = tdir; - tan2[i + 2] = tdir; - } - - // Compute tangents considering normals - for (int i = 0; i < mesh->vertexCount; ++i) - { - Vector3 normal = { mesh->normals[i*3 + 0], mesh->normals[i*3 + 1], mesh->normals[i*3 + 2] }; - Vector3 tangent = tan1[i]; - - // TODO: Review, not sure if tangent computation is right, just used reference proposed maths... - #if defined(COMPUTE_TANGENTS_METHOD_01) - Vector3 tmp = Vector3Subtract(tangent, Vector3Multiply(normal, Vector3DotProduct(normal, tangent))); - tmp = Vector3Normalize(tmp); - mesh->tangents[i*4 + 0] = tmp.x; - mesh->tangents[i*4 + 1] = tmp.y; - mesh->tangents[i*4 + 2] = tmp.z; - mesh->tangents[i*4 + 3] = 1.0f; - #else - Vector3OrthoNormalize(&normal, &tangent); - mesh->tangents[i*4 + 0] = tangent.x; - mesh->tangents[i*4 + 1] = tangent.y; - mesh->tangents[i*4 + 2] = tangent.z; - mesh->tangents[i*4 + 3] = (Vector3DotProduct(Vector3CrossProduct(normal, tangent), tan2[i]) < 0.0f)? -1.0f : 1.0f; - #endif - } - - free(tan1); - free(tan2); - - TraceLog(LOG_INFO, "Tangents computed for mesh"); -} - -// Compute mesh binormals (aka bitangent) -void MeshBinormals(Mesh *mesh) -{ - for (int i = 0; i < mesh->vertexCount; i++) - { - Vector3 normal = { mesh->normals[i*3 + 0], mesh->normals[i*3 + 1], mesh->normals[i*3 + 2] }; - Vector3 tangent = { mesh->tangents[i*4 + 0], mesh->tangents[i*4 + 1], mesh->tangents[i*4 + 2] }; - float tangentW = mesh->tangents[i*4 + 3]; - - // TODO: Register computed binormal in mesh->binormal? - // Vector3 binormal = Vector3Multiply(Vector3CrossProduct(normal, tangent), tangentW); - } -} - //---------------------------------------------------------------------------------- // Module specific Functions Definition //---------------------------------------------------------------------------------- diff --git a/src/raylib.h b/src/raylib.h index c365fa47f..55943bafe 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -752,7 +752,7 @@ typedef enum { MAP_IRRADIANCE, // NOTE: Uses GL_TEXTURE_CUBE_MAP MAP_PREFILTER, // NOTE: Uses GL_TEXTURE_CUBE_MAP MAP_BRDF -} TexmapIndex; +} MaterialMapType; #define MAP_DIFFUSE MAP_ALBEDO #define MAP_SPECULAR MAP_METALNESS @@ -1256,16 +1256,25 @@ RLAPI void DrawGizmo(Vector3 position); // Model loading/unloading functions RLAPI Model LoadModel(const char *fileName); // Load model from files (meshes and materials) RLAPI Model LoadModelFromMesh(Mesh mesh); // Load model from generated mesh -//RLAPI void LoadModelAnimations(const char fileName, ModelAnimation *anims, int *animsCount); // Load model animations from file -//RLAPI void UpdateModelAnimation(Model model, ModelAnimation anim, int frame); // Update model animation pose RLAPI void UnloadModel(Model model); // Unload model from memory (RAM and/or VRAM) -// Mesh manipulation functions -RLAPI BoundingBox MeshBoundingBox(Mesh mesh); // Compute mesh bounding box limits -RLAPI void MeshTangents(Mesh *mesh); // Compute mesh tangents -RLAPI void MeshBinormals(Mesh *mesh); // Compute mesh binormals -RLAPI void UnloadMesh(Mesh *mesh); // Unload mesh from memory (RAM and/or VRAM) +// Mesh loading/unloading functions +RLAPI Mesh *LoadMeshes(const char *fileName, int *meshCount); // Load meshes from model file RLAPI void ExportMesh(Mesh mesh, const char *fileName); // Export mesh data to file +RLAPI void UnloadMesh(Mesh *mesh); // Unload mesh from memory (RAM and/or VRAM) + +// Material loading/unloading functions +RLAPI Material *LoadMaterials(const char *fileName, int *materialCount); // Load materials from model file +RLAPI Material LoadMaterialDefault(void); // Load default material (Supports: DIFFUSE, SPECULAR, NORMAL maps) +RLAPI void UnloadMaterial(Material material); // Unload material from GPU memory (VRAM) +RLAPI void SetMaterialTexture(Material *material, int mapType, Texture2D texture); // Set texture for a material map type (MAP_DIFFUSE, MAP_SPECULAR...) +RLAPI void SetModelMeshMaterial(Model *model, int meshId, int materialId); // Set material for a mesh + +// Model animations loading/unloading functions +RLAPI ModelAnimation *LoadModelAnimations(const char *fileName, int *animsCount); // Load model animations from file +RLAPI void UpdateModelAnimation(Model model, ModelAnimation anim, int frame); // Update model animation pose +RLAPI void UnloadModelAnimation(ModelAnimation anim); // Unload animation data +RLAPI bool IsModelAnimationValid(Model model, ModelAnimation anim); // Check model animation skeleton match // Mesh generation functions RLAPI Mesh GenMeshPoly(int sides, float radius); // Generate polygonal mesh @@ -1279,10 +1288,10 @@ RLAPI Mesh GenMeshKnot(float radius, float size, int radSeg, int sides); RLAPI Mesh GenMeshHeightmap(Image heightmap, Vector3 size); // Generate heightmap mesh from image data RLAPI Mesh GenMeshCubicmap(Image cubicmap, Vector3 cubeSize); // Generate cubes-based map mesh from image data -// Material loading/unloading functions -RLAPI Material LoadMaterial(const char *fileName); // Load material from file -RLAPI Material LoadMaterialDefault(void); // Load default material (Supports: DIFFUSE, SPECULAR, NORMAL maps) -RLAPI void UnloadMaterial(Material material); // Unload material from GPU memory (VRAM) +// Mesh manipulation functions +RLAPI BoundingBox MeshBoundingBox(Mesh mesh); // Compute mesh bounding box limits +RLAPI void MeshTangents(Mesh *mesh); // Compute mesh tangents +RLAPI void MeshBinormals(Mesh *mesh); // Compute mesh binormals // Model drawing functions RLAPI void DrawModel(Model model, Vector3 position, float scale, Color tint); // Draw a model (with texture if set) From 0f9fe34c3a9228eaa3ffdb3be3f52ba586d1ddfa Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 5 Apr 2019 13:44:04 +0200 Subject: [PATCH 40/74] Start setting things up for raylib 2.5 --- CHANGELOG | 6 ++++++ HISTORY.md | 5 +++++ ROADMAP.md | 4 ++-- src/config.h | 2 +- src/raylib.rc | 10 +++++----- 5 files changed, 19 insertions(+), 8 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 332533369..8546e5b0d 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -3,6 +3,12 @@ changelog Current Release: raylib 2.0.0 (July 2018) +----------------------------------------------- +Release: raylib 2.5 (May 2019) +----------------------------------------------- + +TODO. + ----------------------------------------------- Release: raylib 2.0 (July 2018) ----------------------------------------------- diff --git a/HISTORY.md b/HISTORY.md index f9f33ef31..bbd16c439 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -225,3 +225,8 @@ Refactored all raylib configuration #defines into a **centralized `config.h` hea A part of that, lots of new features, like a brand **new font rendering and packaging system** for TTF fonts with **SDF support** (thanks to the amazing STB headers), new functions for **CPU image data manipulation**, new orthographic 3d camera mode, a complete review of `raymath.h` single-file header-only library for better consistency and performance, new examples and way, [way more](https://github.com/raysan5/raylib/blob/master/CHANGELOG). Probably by now, **raylib 2.0 is the simplest and easiest-to-use library to learn (and understand) videogames programming**... but, undoubtly its development has exceeded any initial objective; raylib has become a simple and easy-to-use trully multiplatform portable standalone media library with thousands of possibilities... and that's just the beginning! + +notes on raylib 2.5 +------------------- + +SOON. \ No newline at end of file diff --git a/ROADMAP.md b/ROADMAP.md index f81514499..ae4706076 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -7,10 +7,10 @@ Here it is a wish-list with features and ideas to improve the library. Note that [raylib source code](https://github.com/raysan5/raylib/tree/master/src) has some *TODO* marks around code with pending things to review and improve. Check [GitHub Issues](https://github.com/raysan5/raylib/issues) for further details! **raylib 2.x** - - [ ] raynet: Network module + - [ ] rnet: Network module - [ ] Support Vulkan API (GRAPHICS_API_VULKAN) - [ ] Basic CPU/GPU stats sytem (memory, draws, time...) - - [ ] Support Animated models + - [x] Support Animated models (2.5) **raylib 1.8** - [x] Improved Materials system with PBR support diff --git a/src/config.h b/src/config.h index 4174ed717..561bcfd32 100644 --- a/src/config.h +++ b/src/config.h @@ -25,7 +25,7 @@ * **********************************************************************************************/ -#define RAYLIB_VERSION "2.4-dev" +#define RAYLIB_VERSION "2.5-dev" // Edit to control what features Makefile'd raylib is compiled with #if defined(RAYLIB_CMAKE) diff --git a/src/raylib.rc b/src/raylib.rc index 2aaa5e26e..c2fdfa461 100644 --- a/src/raylib.rc +++ b/src/raylib.rc @@ -1,8 +1,8 @@ GLFW_ICON ICON "raylib.ico" 1 VERSIONINFO -FILEVERSION 2,0,0,0 -PRODUCTVERSION 2,0,0,0 +FILEVERSION 2,5,0,0 +PRODUCTVERSION 2,5,0,0 BEGIN BLOCK "StringFileInfo" BEGIN @@ -11,12 +11,12 @@ BEGIN BEGIN //VALUE "CompanyName", "raylib technologies" VALUE "FileDescription", "Created using raylib (www.raylib.com)" - VALUE "FileVersion", "2.0.0" + VALUE "FileVersion", "2.5.0" VALUE "InternalName", "raylib app" - VALUE "LegalCopyright", "(c) 2018 Ramon Santamaria - @raysan5" + VALUE "LegalCopyright", "(c) 2019 Ramon Santamaria (@raysan5)" //VALUE "OriginalFilename", "raylib_app.exe" VALUE "ProductName", "raylib game" - VALUE "ProductVersion", "2.0.0" + VALUE "ProductVersion", "2.5.0" END END BLOCK "VarFileInfo" From c600dd07668f301fc1a986326d807f341e6ecb78 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 5 Apr 2019 16:43:09 +0200 Subject: [PATCH 41/74] Review PBR shaders Issue was related to vertex tangent attibutes not uploaded to GPU, a quick solution was implemented for new vertex attributes loading for already existing meshes... I don't like it specially but it will work for now. --- examples/models/models_material_pbr.c | 8 +++++++ examples/models/resources/shaders/brdf.fs | 4 ++-- .../models/resources/shaders/irradiance.fs | 4 ++-- .../models/resources/shaders/prefilter.fs | 4 ++-- src/models.c | 3 +++ src/rlgl.h | 24 +++++++++++++++++++ 6 files changed, 41 insertions(+), 6 deletions(-) diff --git a/examples/models/models_material_pbr.c b/examples/models/models_material_pbr.c index adb4762b5..d393a20dc 100644 --- a/examples/models/models_material_pbr.c +++ b/examples/models/models_material_pbr.c @@ -38,7 +38,11 @@ int main() // Load model and PBR material Model model = LoadModel("resources/pbr/trooper.obj"); + + // Mesh tangents are generated... and uploaded to GPU + // NOTE: New VBO for tangents is generated at default location and also binded to mesh VAO MeshTangents(&model.meshes[0]); + model.materials[0] = LoadMaterialPBR((Color){ 255, 255, 255, 255 }, 1.0f, 1.0f); // Define lights attributes @@ -143,9 +147,13 @@ static Material LoadMaterialPBR(Color albedo, float metalness, float roughness) #define PATH_BRDF_FS "resources/shaders/brdf.fs" // Path to bidirectional reflectance distribution function fragment shader Shader shdrCubemap = LoadShader(PATH_CUBEMAP_VS, PATH_CUBEMAP_FS); + printf("Loaded shader: cubemap\n"); Shader shdrIrradiance = LoadShader(PATH_SKYBOX_VS, PATH_IRRADIANCE_FS); + printf("Loaded shader: irradiance\n"); Shader shdrPrefilter = LoadShader(PATH_SKYBOX_VS, PATH_PREFILTER_FS); + printf("Loaded shader: prefilter\n"); Shader shdrBRDF = LoadShader(PATH_BRDF_VS, PATH_BRDF_FS); + printf("Loaded shader: brdf\n"); // Setup required shader locations SetShaderValue(shdrCubemap, GetShaderLocation(shdrCubemap, "equirectangularMap"), (int[1]){ 0 }, UNIFORM_INT); diff --git a/examples/models/resources/shaders/brdf.fs b/examples/models/resources/shaders/brdf.fs index 3e8777d2b..d04bc6618 100644 --- a/examples/models/resources/shaders/brdf.fs +++ b/examples/models/resources/shaders/brdf.fs @@ -10,13 +10,13 @@ #version 330 -#define MAX_SAMPLES 1024u // Input vertex attributes (from vertex shader) in vec2 fragTexCoord; // Constant values const float PI = 3.14159265359; +const uint MAX_SAMPLES = 1024u; // Output fragment color out vec4 finalColor; @@ -93,7 +93,7 @@ vec2 IntegrateBRDF(float NdotV, float roughness) vec3 V = vec3(sqrt(1.0 - NdotV*NdotV), 0.0, NdotV); vec3 N = vec3(0.0, 0.0, 1.0); - for (int i = 0; i < MAX_SAMPLES; i++) + for (uint i = 0u; i < MAX_SAMPLES; i++) { // Generate a sample vector that's biased towards the preferred alignment direction (importance sampling) diff --git a/examples/models/resources/shaders/irradiance.fs b/examples/models/resources/shaders/irradiance.fs index 87113673f..b42d2143e 100644 --- a/examples/models/resources/shaders/irradiance.fs +++ b/examples/models/resources/shaders/irradiance.fs @@ -9,7 +9,7 @@ #version 330 // Input vertex attributes (from vertex shader) -in vec3 fragPos; +in vec3 fragPosition; // Input uniform values uniform samplerCube environmentMap; @@ -23,7 +23,7 @@ out vec4 finalColor; void main() { // The sample direction equals the hemisphere's orientation - vec3 normal = normalize(fragPos); + vec3 normal = normalize(fragPosition); vec3 irradiance = vec3(0.0); diff --git a/examples/models/resources/shaders/prefilter.fs b/examples/models/resources/shaders/prefilter.fs index f5cf64be2..9439810d7 100644 --- a/examples/models/resources/shaders/prefilter.fs +++ b/examples/models/resources/shaders/prefilter.fs @@ -11,7 +11,7 @@ #define CUBEMAP_RESOLUTION 1024.0 // Input vertex attributes (from vertex shader) -in vec3 fragPos; +in vec3 fragPosition; // Input uniform values uniform samplerCube environmentMap; @@ -79,7 +79,7 @@ vec3 ImportanceSampleGGX(vec2 Xi, vec3 N, float roughness) void main() { // Make the simplyfying assumption that V equals R equals the normal - vec3 N = normalize(fragPos); + vec3 N = normalize(fragPosition); vec3 R = N; vec3 V = R; diff --git a/src/models.c b/src/models.c index e437a0ab8..7a9acdf76 100644 --- a/src/models.c +++ b/src/models.c @@ -2302,6 +2302,9 @@ void MeshTangents(Mesh *mesh) free(tan1); free(tan2); + + // Load a new tangent attributes buffer + mesh->vboId[LOC_VERTEX_TANGENT] = rlLoadAttribBuffer(mesh->vaoId, LOC_VERTEX_TANGENT, mesh->tangents, mesh->vertexCount*4*sizeof(float), false); TraceLog(LOG_INFO, "Tangents computed for mesh"); } diff --git a/src/rlgl.h b/src/rlgl.h index 3d433377a..bf50ad0d3 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -456,6 +456,7 @@ void rlDeleteBuffers(unsigned int id); // Unload vertex data (V void rlClearColor(byte r, byte g, byte b, byte a); // Clear color buffer with color void rlClearScreenBuffers(void); // Clear used screen buffers (color and depth) void rlUpdateBuffer(int bufferId, void *data, int dataSize); // Update GPU buffer with new data +unsigned int rlLoadAttribBuffer(unsigned int vaoId, int shaderLoc, void *buffer, int size, bool dynamic); // Load a new attributes buffer //------------------------------------------------------------------------------------ // Functions Declaration - rlgl functionality @@ -2532,6 +2533,29 @@ void rlLoadMesh(Mesh *mesh, bool dynamic) #endif } +// Load a new attributes buffer +unsigned int rlLoadAttribBuffer(unsigned int vaoId, int shaderLoc, void *buffer, int size, bool dynamic) +{ + unsigned int id = 0; + +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + int drawHint = GL_STATIC_DRAW; + if (dynamic) drawHint = GL_DYNAMIC_DRAW; + + if (vaoSupported) glBindVertexArray(vaoId); + + glGenBuffers(1, &id); + glBindBuffer(GL_ARRAY_BUFFER, id); + glBufferData(GL_ARRAY_BUFFER, size, buffer, drawHint); + glVertexAttribPointer(shaderLoc, 2, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(shaderLoc); + + if (vaoSupported) glBindVertexArray(0); +#endif + + return id; +} + // Update vertex data on GPU (upload new data to one buffer) void rlUpdateMesh(Mesh mesh, int buffer, int numVertex) { From 9282b8ba83909b353fd34a9f584597338c1928bd Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 5 Apr 2019 17:08:46 +0200 Subject: [PATCH 42/74] ADDED: SetShaderValueTexture() Some tweaks --- examples/models/models_material_pbr.c | 7 ++++++- src/raylib.h | 3 ++- src/rlgl.h | 16 ++++++++++++++-- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/examples/models/models_material_pbr.c b/examples/models/models_material_pbr.c index d393a20dc..c1d43a24d 100644 --- a/examples/models/models_material_pbr.c +++ b/examples/models/models_material_pbr.c @@ -34,7 +34,12 @@ int main() InitWindow(screenWidth, screenHeight, "raylib [models] example - pbr material"); // Define the camera to look into our 3d world - Camera camera = {{ 4.0f, 4.0f, 4.0f }, { 0.0f, 0.5f, 0.0f }, { 0.0f, 1.0f, 0.0f }, 45.0f, 0 }; + Camera camera = { 0 }; + camera.position = (Vector3){ 4.0f, 4.0f, 4.0f }; // Camera position + camera.target = (Vector3){ 0.0f, 0.5f, 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.type = CAMERA_PERSPECTIVE; // Camera mode type // Load model and PBR material Model model = LoadModel("resources/pbr/trooper.obj"); diff --git a/src/raylib.h b/src/raylib.h index 55943bafe..dc864b8d4 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1331,7 +1331,8 @@ RLAPI Texture2D GetTextureDefault(void); // Get RLAPI int GetShaderLocation(Shader shader, const char *uniformName); // Get shader uniform location RLAPI void SetShaderValue(Shader shader, int uniformLoc, const void *value, int uniformType); // Set shader uniform value RLAPI void SetShaderValueV(Shader shader, int uniformLoc, const void *value, int uniformType, int count); // Set shader uniform value vector -RLAPI void SetShaderValueMatrix(Shader shader, int uniformLoc, Matrix mat); // Set shader uniform value (matrix 4x4) +RLAPI void SetShaderValueMatrix(Shader shader, int uniformLoc, Matrix mat); // Set shader uniform value (matrix 4x4) +RLAPI void SetShaderValueTexture(Shader shader, int uniformLoc, Texture2D texture); // Set shader uniform value for texture RLAPI void SetMatrixProjection(Matrix proj); // Set a custom projection matrix (replaces internal projection matrix) RLAPI void SetMatrixModelview(Matrix view); // Set a custom modelview matrix (replaces internal modelview matrix) RLAPI Matrix GetMatrixModelview(); // Get internal modelview matrix diff --git a/src/rlgl.h b/src/rlgl.h index bf50ad0d3..81f39d68f 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -207,8 +207,8 @@ typedef unsigned char byte; // Animation vertex data float *animVertices; // Animated vertex positions (after bones transformations) float *animNormals; // Animated normals (after bones transformations) - float *weightBias; // Vertex weight bias - int *weightId; // Vertex weight id + int *boneIds; // Vertex bone ids, up to 4 bones influence by vertex (skinning) + float *boneWeights; // Vertex bone weight, up to 4 bones influence by vertex (skinning) // OpenGL identifiers unsigned int vaoId; // OpenGL Vertex Array Object id @@ -3169,6 +3169,18 @@ void SetShaderValueMatrix(Shader shader, int uniformLoc, Matrix mat) #endif } +// Set shader uniform value for texture +void SetShaderValueTexture(Shader shader, int uniformLoc, Texture2D texture) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + glUseProgram(shader.id); + + glUniform1i(uniformLoc, texture.id); + + //glUseProgram(0); +#endif +} + // Set a custom projection matrix (replaces internal projection matrix) void SetMatrixProjection(Matrix proj) { From c23ceec338ac46c28d0b1fe2ab286775efc7f041 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 5 Apr 2019 17:29:30 +0200 Subject: [PATCH 43/74] Added missing include -_- --- examples/models/models_material_pbr.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/models/models_material_pbr.c b/examples/models/models_material_pbr.c index c1d43a24d..5c308cfcb 100644 --- a/examples/models/models_material_pbr.c +++ b/examples/models/models_material_pbr.c @@ -12,6 +12,8 @@ #include "raylib.h" #include "raymath.h" +#include + #define RLIGHTS_IMPLEMENTATION #include "rlights.h" From f21761fbbb02f0b58b5b54342f0c3ad3abc0003e Mon Sep 17 00:00:00 2001 From: ChillerDragon Date: Sun, 7 Apr 2019 17:49:12 +0200 Subject: [PATCH 44/74] Happy new year 2019 --- examples/Makefile | 2 +- examples/audio/audio_raw_stream.c | 2 +- examples/models/models_obj_viewer.c | 2 +- examples/others/rlgl_standalone.c | 2 +- games/Makefile | 2 +- games/cat_vs_roomba/Makefile | 2 +- games/cat_vs_roomba/roomba.c | 2 +- games/cat_vs_roomba/screens/screen_ending.c | 2 +- games/cat_vs_roomba/screens/screen_gameplay.c | 2 +- games/cat_vs_roomba/screens/screen_logo.c | 2 +- games/cat_vs_roomba/screens/screen_title.c | 2 +- games/cat_vs_roomba/screens/screens.h | 2 +- games/drturtle/Makefile | 2 +- games/just_do/Makefile | 2 +- games/koala_seasons/Makefile | 2 +- games/light_my_ritual/Makefile | 2 +- games/skully_escape/Makefile | 2 +- games/transmission/Makefile | 2 +- games/transmission/screens/screen_ending.c | 2 +- games/transmission/screens/screen_gameplay.c | 2 +- games/transmission/screens/screen_logo.c | 2 +- games/transmission/screens/screen_mission.c | 2 +- games/transmission/screens/screen_title.c | 2 +- games/transmission/screens/screens.h | 2 +- games/transmission/transmission.c | 2 +- games/wave_collector/Makefile | 2 +- projects/VSCode/Makefile | 2 +- src/Makefile | 2 +- src/core.c | 2 +- src/gestures.h | 2 +- src/models.c | 2 +- src/raylib.h | 2 +- src/rglfw.c | 2 +- src/rlgl.h | 2 +- src/shapes.c | 2 +- src/text.c | 2 +- src/textures.c | 2 +- src/utils.c | 2 +- src/utils.h | 2 +- templates/advance_game/Makefile | 2 +- templates/advance_game/advance_game.c | 2 +- templates/advance_game/screens/screen_ending.c | 2 +- templates/advance_game/screens/screen_gameplay.c | 2 +- templates/advance_game/screens/screen_logo.c | 2 +- templates/advance_game/screens/screen_options.c | 2 +- templates/advance_game/screens/screen_title.c | 2 +- templates/advance_game/screens/screens.h | 2 +- templates/simple_game/Makefile | 2 +- templates/simple_game/simple_game.c | 2 +- templates/standard_game/Makefile | 2 +- templates/standard_game/screens/screen_ending.c | 2 +- templates/standard_game/screens/screen_gameplay.c | 2 +- templates/standard_game/screens/screen_logo.c | 2 +- templates/standard_game/screens/screen_options.c | 2 +- templates/standard_game/screens/screen_title.c | 2 +- templates/standard_game/screens/screens.h | 2 +- templates/standard_game/standard_game.c | 2 +- 57 files changed, 57 insertions(+), 57 deletions(-) diff --git a/examples/Makefile b/examples/Makefile index e14762b31..b35e39f94 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -2,7 +2,7 @@ # # raylib makefile for Desktop platforms, Raspberry Pi, Android and HTML5 # -# Copyright (c) 2013-2018 Ramon Santamaria (@raysan5) +# Copyright (c) 2013-2019 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. diff --git a/examples/audio/audio_raw_stream.c b/examples/audio/audio_raw_stream.c index 7eee46f65..d7fa5d790 100644 --- a/examples/audio/audio_raw_stream.c +++ b/examples/audio/audio_raw_stream.c @@ -7,7 +7,7 @@ * This example has been created using raylib 1.6 (www.raylib.com) * raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) * -* Copyright (c) 2015-2018 Ramon Santamaria (@raysan5) and James Hofmann (@triplefox) +* Copyright (c) 2015-2019 Ramon Santamaria (@raysan5) and James Hofmann (@triplefox) * ********************************************************************************************/ diff --git a/examples/models/models_obj_viewer.c b/examples/models/models_obj_viewer.c index 0581df343..7d3874416 100644 --- a/examples/models/models_obj_viewer.c +++ b/examples/models/models_obj_viewer.c @@ -5,7 +5,7 @@ * This example has been created using raylib 2.0 (www.raylib.com) * raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/others/rlgl_standalone.c b/examples/others/rlgl_standalone.c index 4b262bbd2..42aec2e2f 100644 --- a/examples/others/rlgl_standalone.c +++ b/examples/others/rlgl_standalone.c @@ -24,7 +24,7 @@ * This example is licensed under an unmodified zlib/libpng license, which is an OSI-certified, * BSD-like license that allows static linking with closed source software: * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 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. diff --git a/games/Makefile b/games/Makefile index 5d60f15d7..44e053c6b 100644 --- a/games/Makefile +++ b/games/Makefile @@ -2,7 +2,7 @@ # # raylib makefile for Desktop platforms, Raspberry Pi, Android and HTML5 # -# Copyright (c) 2013-2018 Ramon Santamaria (@raysan5) +# Copyright (c) 2013-2019 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. diff --git a/games/cat_vs_roomba/Makefile b/games/cat_vs_roomba/Makefile index 6662a6be4..b13046569 100644 --- a/games/cat_vs_roomba/Makefile +++ b/games/cat_vs_roomba/Makefile @@ -2,7 +2,7 @@ # # raylib makefile for Desktop platforms, Raspberry Pi, Android and HTML5 # -# Copyright (c) 2013-2018 Ramon Santamaria (@raysan5) +# Copyright (c) 2013-2019 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. diff --git a/games/cat_vs_roomba/roomba.c b/games/cat_vs_roomba/roomba.c index 0d2367758..eeee7a719 100644 --- a/games/cat_vs_roomba/roomba.c +++ b/games/cat_vs_roomba/roomba.c @@ -8,7 +8,7 @@ * This game has been created using raylib (www.raylib.com) * raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/games/cat_vs_roomba/screens/screen_ending.c b/games/cat_vs_roomba/screens/screen_ending.c index ef2a5f749..466d9b916 100644 --- a/games/cat_vs_roomba/screens/screen_ending.c +++ b/games/cat_vs_roomba/screens/screen_ending.c @@ -4,7 +4,7 @@ * * Ending Screen Functions Definitions (Init, Update, Draw, Unload) * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 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. diff --git a/games/cat_vs_roomba/screens/screen_gameplay.c b/games/cat_vs_roomba/screens/screen_gameplay.c index 49a0bb6bf..4dd13856f 100644 --- a/games/cat_vs_roomba/screens/screen_gameplay.c +++ b/games/cat_vs_roomba/screens/screen_gameplay.c @@ -4,7 +4,7 @@ * * Gameplay Screen Functions Definitions (Init, Update, Draw, Unload) * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 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. diff --git a/games/cat_vs_roomba/screens/screen_logo.c b/games/cat_vs_roomba/screens/screen_logo.c index 9fc704c76..a697013ef 100644 --- a/games/cat_vs_roomba/screens/screen_logo.c +++ b/games/cat_vs_roomba/screens/screen_logo.c @@ -4,7 +4,7 @@ * * Logo Screen Functions Definitions (Init, Update, Draw, Unload) * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 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. diff --git a/games/cat_vs_roomba/screens/screen_title.c b/games/cat_vs_roomba/screens/screen_title.c index 009fbd0a0..6acadce5a 100644 --- a/games/cat_vs_roomba/screens/screen_title.c +++ b/games/cat_vs_roomba/screens/screen_title.c @@ -4,7 +4,7 @@ * * Title Screen Functions Definitions (Init, Update, Draw, Unload) * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 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. diff --git a/games/cat_vs_roomba/screens/screens.h b/games/cat_vs_roomba/screens/screens.h index 9cc07eab6..0ad4f9af8 100644 --- a/games/cat_vs_roomba/screens/screens.h +++ b/games/cat_vs_roomba/screens/screens.h @@ -4,7 +4,7 @@ * * Screens Functions Declarations (Init, Update, Draw, Unload) * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 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. diff --git a/games/drturtle/Makefile b/games/drturtle/Makefile index 8f1934b5d..4cd5033ea 100644 --- a/games/drturtle/Makefile +++ b/games/drturtle/Makefile @@ -2,7 +2,7 @@ # # raylib makefile for Desktop platforms, Raspberry Pi, Android and HTML5 # -# Copyright (c) 2013-2018 Ramon Santamaria (@raysan5) +# Copyright (c) 2013-2019 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. diff --git a/games/just_do/Makefile b/games/just_do/Makefile index b6c935f66..af9b31c12 100644 --- a/games/just_do/Makefile +++ b/games/just_do/Makefile @@ -2,7 +2,7 @@ # # raylib makefile for Desktop platforms, Raspberry Pi, Android and HTML5 # -# Copyright (c) 2013-2018 Ramon Santamaria (@raysan5) +# Copyright (c) 2013-2019 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. diff --git a/games/koala_seasons/Makefile b/games/koala_seasons/Makefile index 8482bf2d3..25dbe6964 100644 --- a/games/koala_seasons/Makefile +++ b/games/koala_seasons/Makefile @@ -2,7 +2,7 @@ # # raylib makefile for Desktop platforms, Raspberry Pi, Android and HTML5 # -# Copyright (c) 2013-2018 Ramon Santamaria (@raysan5) +# Copyright (c) 2013-2019 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. diff --git a/games/light_my_ritual/Makefile b/games/light_my_ritual/Makefile index fbc34aac3..3291bb3e7 100644 --- a/games/light_my_ritual/Makefile +++ b/games/light_my_ritual/Makefile @@ -2,7 +2,7 @@ # # raylib makefile for Desktop platforms, Raspberry Pi, Android and HTML5 # -# Copyright (c) 2013-2018 Ramon Santamaria (@raysan5) +# Copyright (c) 2013-2019 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. diff --git a/games/skully_escape/Makefile b/games/skully_escape/Makefile index 8875ad527..4673a549e 100644 --- a/games/skully_escape/Makefile +++ b/games/skully_escape/Makefile @@ -2,7 +2,7 @@ # # raylib makefile for Desktop platforms, Raspberry Pi, Android and HTML5 # -# Copyright (c) 2013-2018 Ramon Santamaria (@raysan5) +# Copyright (c) 2013-2019 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. diff --git a/games/transmission/Makefile b/games/transmission/Makefile index a1b7e764e..f62fe0804 100644 --- a/games/transmission/Makefile +++ b/games/transmission/Makefile @@ -2,7 +2,7 @@ # # raylib makefile for Desktop platforms, Raspberry Pi, Android and HTML5 # -# Copyright (c) 2013-2018 Ramon Santamaria (@raysan5) +# Copyright (c) 2013-2019 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. diff --git a/games/transmission/screens/screen_ending.c b/games/transmission/screens/screen_ending.c index bb355b8b0..0492a0ccc 100644 --- a/games/transmission/screens/screen_ending.c +++ b/games/transmission/screens/screen_ending.c @@ -4,7 +4,7 @@ * * Ending Screen Functions Definitions (Init, Update, Draw, Unload) * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 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. diff --git a/games/transmission/screens/screen_gameplay.c b/games/transmission/screens/screen_gameplay.c index ee70632a5..3dfce7145 100644 --- a/games/transmission/screens/screen_gameplay.c +++ b/games/transmission/screens/screen_gameplay.c @@ -4,7 +4,7 @@ * * Gameplay Screen Functions Definitions (Init, Update, Draw, Unload) * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 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. diff --git a/games/transmission/screens/screen_logo.c b/games/transmission/screens/screen_logo.c index 375433028..dc016423e 100644 --- a/games/transmission/screens/screen_logo.c +++ b/games/transmission/screens/screen_logo.c @@ -4,7 +4,7 @@ * * Logo Screen Functions Definitions (Init, Update, Draw, Unload) * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 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. diff --git a/games/transmission/screens/screen_mission.c b/games/transmission/screens/screen_mission.c index 1cd2563b8..77777c73e 100644 --- a/games/transmission/screens/screen_mission.c +++ b/games/transmission/screens/screen_mission.c @@ -3,7 +3,7 @@ * raylib - transmission mission * * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 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. diff --git a/games/transmission/screens/screen_title.c b/games/transmission/screens/screen_title.c index 2a30a6bac..e9023b089 100644 --- a/games/transmission/screens/screen_title.c +++ b/games/transmission/screens/screen_title.c @@ -3,7 +3,7 @@ * raylib - transmission mission * * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 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. diff --git a/games/transmission/screens/screens.h b/games/transmission/screens/screens.h index be5e31d92..49698f0d9 100644 --- a/games/transmission/screens/screens.h +++ b/games/transmission/screens/screens.h @@ -4,7 +4,7 @@ * * Screens Functions Declarations (Init, Update, Draw, Unload) * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 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. diff --git a/games/transmission/transmission.c b/games/transmission/transmission.c index 91ca28c1d..9fc3d802b 100644 --- a/games/transmission/transmission.c +++ b/games/transmission/transmission.c @@ -7,7 +7,7 @@ * This game has been created using raylib (www.raylib.com) * raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/games/wave_collector/Makefile b/games/wave_collector/Makefile index 06e74799f..703383b89 100644 --- a/games/wave_collector/Makefile +++ b/games/wave_collector/Makefile @@ -2,7 +2,7 @@ # # raylib makefile for Desktop platforms, Raspberry Pi, Android and HTML5 # -# Copyright (c) 2013-2018 Ramon Santamaria (@raysan5) +# Copyright (c) 2013-2019 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. diff --git a/projects/VSCode/Makefile b/projects/VSCode/Makefile index 747718fc4..3cffaaba1 100644 --- a/projects/VSCode/Makefile +++ b/projects/VSCode/Makefile @@ -2,7 +2,7 @@ # # raylib makefile for Desktop platforms, Raspberry Pi, Android and HTML5 # -# Copyright (c) 2013-2018 Ramon Santamaria (@raysan5) +# Copyright (c) 2013-2019 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. diff --git a/src/Makefile b/src/Makefile index e2fe6c5d5..0217c198c 100644 --- a/src/Makefile +++ b/src/Makefile @@ -14,7 +14,7 @@ # Many thanks to Milan Nikolic (@gen2brain) for implementing Android platform pipeline. # Many thanks to Emanuele Petriglia for his contribution on GNU/Linux pipeline. # -# Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +# Copyright (c) 2014-2019 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 diff --git a/src/core.c b/src/core.c index b0c0658d7..42db214fc 100644 --- a/src/core.c +++ b/src/core.c @@ -68,7 +68,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 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. diff --git a/src/gestures.h b/src/gestures.h index a4546eb1b..367753330 100644 --- a/src/gestures.h +++ b/src/gestures.h @@ -24,7 +24,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 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. diff --git a/src/models.c b/src/models.c index 7a9acdf76..07f8203bb 100644 --- a/src/models.c +++ b/src/models.c @@ -17,7 +17,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 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. diff --git a/src/raylib.h b/src/raylib.h index dc864b8d4..dade7aba8 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -49,7 +49,7 @@ * raylib is licensed under an unmodified zlib/libpng license, which is an OSI-certified, * BSD-like license that allows static linking with closed source software: * -* Copyright (c) 2013-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2013-2019 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. diff --git a/src/rglfw.c b/src/rglfw.c index 853c52009..3463bb96d 100644 --- a/src/rglfw.c +++ b/src/rglfw.c @@ -7,7 +7,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2017-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2017-2019 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. diff --git a/src/rlgl.h b/src/rlgl.h index 81f39d68f..3e4282414 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -42,7 +42,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 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. diff --git a/src/shapes.c b/src/shapes.c index b7f7e3df5..fbc70fc4e 100644 --- a/src/shapes.c +++ b/src/shapes.c @@ -14,7 +14,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 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. diff --git a/src/text.c b/src/text.c index d44cdd11a..cd7a68026 100644 --- a/src/text.c +++ b/src/text.c @@ -17,7 +17,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 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. diff --git a/src/textures.c b/src/textures.c index cd0bd208a..5189b6357 100644 --- a/src/textures.c +++ b/src/textures.c @@ -38,7 +38,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 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. diff --git a/src/utils.c b/src/utils.c index 6b174354d..10cce9b92 100644 --- a/src/utils.c +++ b/src/utils.c @@ -11,7 +11,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 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. diff --git a/src/utils.h b/src/utils.h index 08b33962f..d7ab88294 100644 --- a/src/utils.h +++ b/src/utils.h @@ -5,7 +5,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 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. diff --git a/templates/advance_game/Makefile b/templates/advance_game/Makefile index 2e5d6e263..e475f72c5 100644 --- a/templates/advance_game/Makefile +++ b/templates/advance_game/Makefile @@ -2,7 +2,7 @@ # # raylib makefile for Desktop platforms, Raspberry Pi, Android and HTML5 # -# Copyright (c) 2013-2018 Ramon Santamaria (@raysan5) +# Copyright (c) 2013-2019 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. diff --git a/templates/advance_game/advance_game.c b/templates/advance_game/advance_game.c index 48a34f6d7..3fb5d6571 100644 --- a/templates/advance_game/advance_game.c +++ b/templates/advance_game/advance_game.c @@ -8,7 +8,7 @@ * This game has been created using raylib (www.raylib.com) * raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/templates/advance_game/screens/screen_ending.c b/templates/advance_game/screens/screen_ending.c index 66b5ddf9f..62d1267a3 100644 --- a/templates/advance_game/screens/screen_ending.c +++ b/templates/advance_game/screens/screen_ending.c @@ -4,7 +4,7 @@ * * Ending Screen Functions Definitions (Init, Update, Draw, Unload) * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 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. diff --git a/templates/advance_game/screens/screen_gameplay.c b/templates/advance_game/screens/screen_gameplay.c index 8943adb5a..8ea614911 100644 --- a/templates/advance_game/screens/screen_gameplay.c +++ b/templates/advance_game/screens/screen_gameplay.c @@ -4,7 +4,7 @@ * * Gameplay Screen Functions Definitions (Init, Update, Draw, Unload) * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 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. diff --git a/templates/advance_game/screens/screen_logo.c b/templates/advance_game/screens/screen_logo.c index 6282e83ed..8badbf52b 100644 --- a/templates/advance_game/screens/screen_logo.c +++ b/templates/advance_game/screens/screen_logo.c @@ -4,7 +4,7 @@ * * Logo Screen Functions Definitions (Init, Update, Draw, Unload) * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 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. diff --git a/templates/advance_game/screens/screen_options.c b/templates/advance_game/screens/screen_options.c index dc8d74fa2..4b58da133 100644 --- a/templates/advance_game/screens/screen_options.c +++ b/templates/advance_game/screens/screen_options.c @@ -4,7 +4,7 @@ * * Options Screen Functions Definitions (Init, Update, Draw, Unload) * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 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. diff --git a/templates/advance_game/screens/screen_title.c b/templates/advance_game/screens/screen_title.c index 5727546a3..8e74a0270 100644 --- a/templates/advance_game/screens/screen_title.c +++ b/templates/advance_game/screens/screen_title.c @@ -4,7 +4,7 @@ * * Title Screen Functions Definitions (Init, Update, Draw, Unload) * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 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. diff --git a/templates/advance_game/screens/screens.h b/templates/advance_game/screens/screens.h index 4d7f9b538..7c2dfb48a 100644 --- a/templates/advance_game/screens/screens.h +++ b/templates/advance_game/screens/screens.h @@ -4,7 +4,7 @@ * * Screens Functions Declarations (Init, Update, Draw, Unload) * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 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. diff --git a/templates/simple_game/Makefile b/templates/simple_game/Makefile index 86e0dbde7..9f27a429a 100644 --- a/templates/simple_game/Makefile +++ b/templates/simple_game/Makefile @@ -2,7 +2,7 @@ # # raylib makefile for Desktop platforms, Raspberry Pi, Android and HTML5 # -# Copyright (c) 2013-2018 Ramon Santamaria (@raysan5) +# Copyright (c) 2013-2019 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. diff --git a/templates/simple_game/simple_game.c b/templates/simple_game/simple_game.c index 028b1da89..8f2dc36a0 100644 --- a/templates/simple_game/simple_game.c +++ b/templates/simple_game/simple_game.c @@ -8,7 +8,7 @@ * This game has been created using raylib (www.raylib.com) * raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/templates/standard_game/Makefile b/templates/standard_game/Makefile index b9c6c188e..4333c8b84 100644 --- a/templates/standard_game/Makefile +++ b/templates/standard_game/Makefile @@ -2,7 +2,7 @@ # # raylib makefile for Desktop platforms, Raspberry Pi, Android and HTML5 # -# Copyright (c) 2013-2018 Ramon Santamaria (@raysan5) +# Copyright (c) 2013-2019 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. diff --git a/templates/standard_game/screens/screen_ending.c b/templates/standard_game/screens/screen_ending.c index 871969779..1bd5ce988 100644 --- a/templates/standard_game/screens/screen_ending.c +++ b/templates/standard_game/screens/screen_ending.c @@ -4,7 +4,7 @@ * * Ending Screen Functions Definitions (Init, Update, Draw, Unload) * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 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. diff --git a/templates/standard_game/screens/screen_gameplay.c b/templates/standard_game/screens/screen_gameplay.c index 7f108265a..427e5ba7d 100644 --- a/templates/standard_game/screens/screen_gameplay.c +++ b/templates/standard_game/screens/screen_gameplay.c @@ -4,7 +4,7 @@ * * Gameplay Screen Functions Definitions (Init, Update, Draw, Unload) * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 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. diff --git a/templates/standard_game/screens/screen_logo.c b/templates/standard_game/screens/screen_logo.c index c0b60571a..435ea2418 100644 --- a/templates/standard_game/screens/screen_logo.c +++ b/templates/standard_game/screens/screen_logo.c @@ -4,7 +4,7 @@ * * Logo Screen Functions Definitions (Init, Update, Draw, Unload) * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 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. diff --git a/templates/standard_game/screens/screen_options.c b/templates/standard_game/screens/screen_options.c index 9f6690d1a..df68dd266 100644 --- a/templates/standard_game/screens/screen_options.c +++ b/templates/standard_game/screens/screen_options.c @@ -4,7 +4,7 @@ * * Options Screen Functions Definitions (Init, Update, Draw, Unload) * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 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. diff --git a/templates/standard_game/screens/screen_title.c b/templates/standard_game/screens/screen_title.c index 328448bac..5bd338258 100644 --- a/templates/standard_game/screens/screen_title.c +++ b/templates/standard_game/screens/screen_title.c @@ -4,7 +4,7 @@ * * Title Screen Functions Definitions (Init, Update, Draw, Unload) * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 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. diff --git a/templates/standard_game/screens/screens.h b/templates/standard_game/screens/screens.h index e961b5338..9450c29f1 100644 --- a/templates/standard_game/screens/screens.h +++ b/templates/standard_game/screens/screens.h @@ -4,7 +4,7 @@ * * Screens Functions Declarations (Init, Update, Draw, Unload) * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 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. diff --git a/templates/standard_game/standard_game.c b/templates/standard_game/standard_game.c index 8871484e2..7918562c1 100644 --- a/templates/standard_game/standard_game.c +++ b/templates/standard_game/standard_game.c @@ -8,7 +8,7 @@ * This game has been created using raylib (www.raylib.com) * raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) * ********************************************************************************************/ From b8ada4b877497cf31aad0efdec41c032fc686552 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 8 Apr 2019 12:25:13 +0200 Subject: [PATCH 45/74] Review creation years --- src/config.h | 2 +- src/core.c | 2 +- src/models.c | 2 +- src/raudio.c | 2 +- src/shapes.c | 2 +- src/text.c | 2 +- src/textures.c | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/config.h b/src/config.h index 561bcfd32..dc30eeb25 100644 --- a/src/config.h +++ b/src/config.h @@ -6,7 +6,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2018 Ahmad Fatoum & Ramon Santamaria (@raysan5) +* Copyright (c) 2018-2019 Ahmad Fatoum & 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. diff --git a/src/core.c b/src/core.c index 42db214fc..32773a0d8 100644 --- a/src/core.c +++ b/src/core.c @@ -68,7 +68,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) +* Copyright (c) 2013-2019 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. diff --git a/src/models.c b/src/models.c index 07f8203bb..d5e74e330 100644 --- a/src/models.c +++ b/src/models.c @@ -17,7 +17,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) +* Copyright (c) 2013-2019 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. diff --git a/src/raudio.c b/src/raudio.c index d360b2d34..636d15b85 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -46,7 +46,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) +* Copyright (c) 2013-2019 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. diff --git a/src/shapes.c b/src/shapes.c index fbc70fc4e..f407f0d2c 100644 --- a/src/shapes.c +++ b/src/shapes.c @@ -14,7 +14,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) +* Copyright (c) 2013-2019 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. diff --git a/src/text.c b/src/text.c index cd7a68026..c07f807ae 100644 --- a/src/text.c +++ b/src/text.c @@ -17,7 +17,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) +* Copyright (c) 2013-2019 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. diff --git a/src/textures.c b/src/textures.c index 5189b6357..169f8f86a 100644 --- a/src/textures.c +++ b/src/textures.c @@ -38,7 +38,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) +* Copyright (c) 2013-2019 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. From 94335e30df6c66337eaad937048191c1735e6223 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 8 Apr 2019 12:50:09 +0200 Subject: [PATCH 46/74] Avoid example on Android --- examples/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 4b04d45a9..ab5ff5489 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -47,6 +47,7 @@ if(${PLATFORM} MATCHES "Android") list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/models/models_billboard.c) list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/models/models_rlgl_solar_system.c) list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/models/models_obj_viewer.c) + list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/models/models_animation.c) list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/shaders/shaders_custom_uniform.c) list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/shaders/shaders_model_shader.c) From 32ccecb8ca1f141e6b8e20007e795c58108cb899 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 9 Apr 2019 13:23:51 +0200 Subject: [PATCH 47/74] Start working on glTF loading... --- src/models.c | 60 +++++++++++++++++++++++++++++++++++----------------- 1 file changed, 41 insertions(+), 19 deletions(-) diff --git a/src/models.c b/src/models.c index d5e74e330..73f8b9406 100644 --- a/src/models.c +++ b/src/models.c @@ -3013,30 +3013,30 @@ static Model LoadIQM(const char *fileName) fread(imesh, sizeof(IQMMesh)*iqm.num_meshes, 1, iqmFile); model.meshCount = iqm.num_meshes; - model.meshes = malloc(sizeof(Mesh)*iqm.num_meshes); + model.meshes = malloc(model.meshCount*sizeof(Mesh)); char name[MESH_NAME_LENGTH]; - for (int i = 0; i < iqm.num_meshes; i++) + for (int i = 0; i < model.meshCount; i++) { fseek(iqmFile,iqm.ofs_text+imesh[i].name,SEEK_SET); fread(name, sizeof(char)*MESH_NAME_LENGTH, 1, iqmFile); // Mesh name not used... model.meshes[i].vertexCount = imesh[i].num_vertexes; - model.meshes[i].vertices = malloc(sizeof(float)*imesh[i].num_vertexes*3); // Default vertex positions - model.meshes[i].normals = malloc(sizeof(float)*imesh[i].num_vertexes*3); // Default vertex normals - model.meshes[i].texcoords = malloc(sizeof(float)*imesh[i].num_vertexes*2); // Default vertex texcoords + model.meshes[i].vertices = malloc(sizeof(float)*model.meshes[i].vertexCount*3); // Default vertex positions + model.meshes[i].normals = malloc(sizeof(float)*model.meshes[i].vertexCount*3); // Default vertex normals + model.meshes[i].texcoords = malloc(sizeof(float)*model.meshes[i].vertexCount*2); // Default vertex texcoords - model.meshes[i].boneIds = malloc(sizeof(int)*imesh[i].num_vertexes*4); // Up-to 4 bones supported! - model.meshes[i].boneWeights = malloc(sizeof(float)*imesh[i].num_vertexes*4); // Up-to 4 bones supported! + model.meshes[i].boneIds = malloc(sizeof(int)*model.meshes[i].vertexCount*4); // Up-to 4 bones supported! + model.meshes[i].boneWeights = malloc(sizeof(float)*model.meshes[i].vertexCount*4); // Up-to 4 bones supported! model.meshes[i].triangleCount = imesh[i].num_triangles; - model.meshes[i].indices = malloc(sizeof(unsigned short)*imesh[i].num_triangles*3); + model.meshes[i].indices = malloc(sizeof(unsigned short)*model.meshes[i].triangleCount*3); // Animated verted data, what we actually process for rendering // NOTE: Animated vertex should be re-uploaded to GPU (if not using GPU skinning) - model.meshes[i].animVertices = malloc(sizeof(float)*imesh[i].num_vertexes*3); - model.meshes[i].animNormals = malloc(sizeof(float)*imesh[i].num_vertexes*3); + model.meshes[i].animVertices = malloc(sizeof(float)*model.meshes[i].vertexCount*3); + model.meshes[i].animNormals = malloc(sizeof(float)*model.meshes[i].vertexCount*3); } // Triangles data processing @@ -3044,15 +3044,15 @@ static Model LoadIQM(const char *fileName) fseek(iqmFile, iqm.ofs_triangles, SEEK_SET); fread(tri, sizeof(IQMTriangle)*iqm.num_triangles, 1, iqmFile); - for (int m = 0; m < iqm.num_meshes; m++) + for (int m = 0; m < model.meshCount; m++) { int tcounter = 0; - for (int i = imesh[m].first_triangle; i < imesh[m].first_triangle+imesh[m].num_triangles; i++) + for (int i = imesh[m].first_triangle; i < (imesh[m].first_triangle + imesh[m].num_triangles); i++) { // IQM triangles are stored counter clockwise, but raylib sets opengl to clockwise drawing, so we swap them around - model.meshes[m].indices[tcounter+2] = tri[i].vertex[0] - imesh[m].first_vertex; - model.meshes[m].indices[tcounter+1] = tri[i].vertex[1] - imesh[m].first_vertex; + model.meshes[m].indices[tcounter + 2] = tri[i].vertex[0] - imesh[m].first_vertex; + model.meshes[m].indices[tcounter + 1] = tri[i].vertex[1] - imesh[m].first_vertex; model.meshes[m].indices[tcounter] = tri[i].vertex[2] - imesh[m].first_vertex; tcounter += 3; } @@ -3235,7 +3235,7 @@ static Model LoadGLTF(const char *fileName) fclose(gltfFile); // glTF data loading - cgltf_options options = {0}; + cgltf_options options = { 0 }; cgltf_data *data; cgltf_result result = cgltf_parse(&options, buffer, size, &data); @@ -3243,11 +3243,33 @@ static Model LoadGLTF(const char *fileName) if (result == cgltf_result_success) { - // printf("Type: %u\n", data.file_type); - // printf("Version: %d\n", data.version); - // printf("Meshes: %lu\n", data.meshes_count); + TraceLog(LOG_INFO, "[%s][%s] Model meshes/materials: %i/%i", (data->file_type == 2)? "glb" : "gltf", data->meshes_count, data->materials_count); - // TODO: Process glTF data and map to model + // Read data buffers + result = cgltf_load_buffers(&options, data, fileName); + + // Process glTF data and map to model + model.meshCount = data->meshes_count; + model.meshes = malloc(model.meshCount*sizeof(Mesh)); + + for (int i = 0; i < model.meshCount; i++) + { + // NOTE: Only support meshes defined by triangle primitives + //if (data->meshes[i].primitives[n].type == cgltf_primitive_type_triangles) + { + // data.meshes[i].name not used + model.meshes[i].vertexCount = data->meshes[i].primitives_count*3; + model.meshes[i].triangleCount = data->meshes[i].primitives_count; + // data.meshes[i].weights not used (array of weights to be applied to the Morph Targets) + + model.meshes[i].vertices = malloc(sizeof(float)*model.meshes[i].vertexCount*3); // Default vertex positions + model.meshes[i].normals = malloc(sizeof(float)*model.meshes[i].vertexCount*3); // Default vertex normals + model.meshes[i].texcoords = malloc(sizeof(float)*model.meshes[i].vertexCount*2); // Default vertex texcoords + + model.meshes[i].indices = malloc(sizeof(unsigned short)*model.meshes[i].triangleCount*3); + + } + } // NOTE: data.buffers[] should be loaded to model.meshes and data.images[] should be loaded to model.materials // Use buffers[n].uri and images[n].uri... or use cgltf_load_buffers(&options, data, fileName); From f939f6abc2796fa019134e9d6c80cf7d0252eb76 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 9 Apr 2019 13:23:56 +0200 Subject: [PATCH 48/74] Update Makefile --- src/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Makefile b/src/Makefile index 0217c198c..997f041c9 100644 --- a/src/Makefile +++ b/src/Makefile @@ -155,7 +155,7 @@ endif ifeq ($(PLATFORM),PLATFORM_WEB) # Emscripten required variables EMSDK_PATH = C:/emsdk - EMSCRIPTEN_VERSION ?= 1.38.21 + EMSCRIPTEN_VERSION ?= 1.38.30 CLANG_VERSION = e$(EMSCRIPTEN_VERSION)_64bit PYTHON_VERSION = 2.7.13.1_64bit\python-2.7.13.amd64 NODE_VERSION = 8.9.1_64bit From 802afe8fe533bc66a6ebd2810090b08d1f635991 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 10 Apr 2019 00:34:46 +0200 Subject: [PATCH 49/74] Replaced size_t by unsigned int --- src/external/tinyobj_loader_c.h | 174 ++++++++++++++++---------------- 1 file changed, 87 insertions(+), 87 deletions(-) diff --git a/src/external/tinyobj_loader_c.h b/src/external/tinyobj_loader_c.h index ae975829c..846784fa3 100644 --- a/src/external/tinyobj_loader_c.h +++ b/src/external/tinyobj_loader_c.h @@ -24,8 +24,8 @@ #ifndef TINOBJ_LOADER_C_H_ #define TINOBJ_LOADER_C_H_ -/* @todo { Remove stddef dependency. size_t? } */ -#include +/* @todo { Remove stddef dependency. unsigned int? } ---> RAY: DONE. */ +//#include typedef struct { char *name; @@ -93,18 +93,18 @@ typedef struct { * Returns TINYOBJ_ERR_*** when there is an error. */ extern int tinyobj_parse_obj(tinyobj_attrib_t *attrib, tinyobj_shape_t **shapes, - size_t *num_shapes, tinyobj_material_t **materials, - size_t *num_materials, const char *buf, size_t len, + unsigned int *num_shapes, tinyobj_material_t **materials, + unsigned int *num_materials, const char *buf, unsigned int len, unsigned int flags); extern int tinyobj_parse_mtl_file(tinyobj_material_t **materials_out, - size_t *num_materials_out, + unsigned int *num_materials_out, const char *filename); extern void tinyobj_attrib_init(tinyobj_attrib_t *attrib); extern void tinyobj_attrib_free(tinyobj_attrib_t *attrib); -extern void tinyobj_shapes_free(tinyobj_shape_t *shapes, size_t num_shapes); +extern void tinyobj_shapes_free(tinyobj_shape_t *shapes, unsigned int num_shapes); extern void tinyobj_materials_free(tinyobj_material_t *materials, - size_t num_materials); + unsigned int num_materials); #ifdef TINYOBJ_LOADER_C_IMPLEMENTATION #include @@ -155,8 +155,8 @@ static int until_space(const char *token) { return (int)(p - token); } -static size_t length_until_newline(const char *token, size_t n) { - size_t len = 0; +static unsigned int length_until_newline(const char *token, unsigned int n) { + unsigned int len = 0; /* Assume token[n-1] = '\0' */ for (len = 0; len < n - 1; len++) { @@ -171,8 +171,8 @@ static size_t length_until_newline(const char *token, size_t n) { return len; } -static size_t length_until_line_feed(const char *token, size_t n) { - size_t len = 0; +static unsigned int length_until_line_feed(const char *token, unsigned int n) { + unsigned int len = 0; /* Assume token[n-1] = '\0' */ for (len = 0; len < n; len++) { @@ -202,7 +202,7 @@ static int my_atoi(const char *c) { } /* Make index zero-base, and also support relative index. */ -static int fixIndex(int idx, size_t n) { +static int fixIndex(int idx, unsigned int n) { if (idx > 0) return idx - 1; if (idx == 0) return 0; return (int)n + idx; /* negative value = relative */ @@ -453,9 +453,9 @@ static void parseFloat3(float *x, float *y, float *z, const char **token) { (*z) = parseFloat(token); } -static char *my_strdup(const char *s, size_t max_length) { +static char *my_strdup(const char *s, unsigned int max_length) { char *d; - size_t len; + unsigned int len; if (s == NULL) return NULL; @@ -465,15 +465,15 @@ static char *my_strdup(const char *s, size_t max_length) { /* trim line ending and append '\0' */ d = (char *)TINYOBJ_MALLOC(len + 1); /* + '\0' */ - memcpy(d, s, (size_t)(len)); + memcpy(d, s, (unsigned int)(len)); d[len] = '\0'; return d; } -static char *my_strndup(const char *s, size_t len) { +static char *my_strndup(const char *s, unsigned int len) { char *d; - size_t slen; + unsigned int slen; if (s == NULL) return NULL; if (len == 0) return NULL; @@ -491,10 +491,10 @@ static char *my_strndup(const char *s, size_t len) { return d; } -char *dynamic_fgets(char **buf, size_t *size, FILE *file) { +char *dynamic_fgets(char **buf, unsigned int *size, FILE *file) { char *offset; char *ret; - size_t old_size; + unsigned int old_size; if (!(ret = fgets(*buf, (int)*size, file))) { return ret; @@ -560,8 +560,8 @@ typedef struct { unsigned long* hashes; hash_table_entry_t* entries; - size_t capacity; - size_t n; + unsigned int capacity; + unsigned int n; } hash_table_t; static unsigned long hash_djb2(const unsigned char* str) @@ -576,7 +576,7 @@ static unsigned long hash_djb2(const unsigned char* str) return hash; } -static void create_hash_table(size_t start_capacity, hash_table_t* hash_table) +static void create_hash_table(unsigned int start_capacity, hash_table_t* hash_table) { if (start_capacity < 1) start_capacity = HASH_TABLE_DEFAULT_SIZE; @@ -596,10 +596,10 @@ static void destroy_hash_table(hash_table_t* hash_table) static int hash_table_insert_value(unsigned long hash, long value, hash_table_t* hash_table) { /* Insert value */ - size_t start_index = hash % hash_table->capacity; - size_t index = start_index; + unsigned int start_index = hash % hash_table->capacity; + unsigned int index = start_index; hash_table_entry_t* start_entry = hash_table->entries + start_index; - size_t i; + unsigned int i; hash_table_entry_t* entry; for (i = 1; hash_table->entries[index].filled; i++) @@ -648,11 +648,11 @@ static hash_table_entry_t* hash_table_find(unsigned long hash, hash_table_t* has return NULL; } -static void hash_table_maybe_grow(size_t new_n, hash_table_t* hash_table) +static void hash_table_maybe_grow(unsigned int new_n, hash_table_t* hash_table) { - size_t new_capacity; + unsigned int new_capacity; hash_table_t new_hash_table; - size_t i; + unsigned int i; if (new_n <= hash_table->capacity) { return; @@ -680,7 +680,7 @@ static int hash_table_exists(const char* name, hash_table_t* hash_table) return hash_table_find(hash_djb2((const unsigned char*)name), hash_table) != NULL; } -static void hash_table_set(const char* name, size_t val, hash_table_t* hash_table) +static void hash_table_set(const char* name, unsigned int val, hash_table_t* hash_table) { /* Hash name */ unsigned long hash = hash_djb2((const unsigned char *)name); @@ -709,7 +709,7 @@ static long hash_table_get(const char* name, hash_table_t* hash_table) } static tinyobj_material_t *tinyobj_material_add(tinyobj_material_t *prev, - size_t num_materials, + unsigned int num_materials, tinyobj_material_t *new_mat) { tinyobj_material_t *dst; dst = (tinyobj_material_t *)TINYOBJ_REALLOC( @@ -720,14 +720,14 @@ static tinyobj_material_t *tinyobj_material_add(tinyobj_material_t *prev, } static int tinyobj_parse_and_index_mtl_file(tinyobj_material_t **materials_out, - size_t *num_materials_out, + unsigned int *num_materials_out, const char *filename, hash_table_t* material_table) { tinyobj_material_t material; - size_t buffer_size = 128; + unsigned int buffer_size = 128; char *linebuf; FILE *fp; - size_t num_materials = 0; + unsigned int num_materials = 0; tinyobj_material_t *materials = NULL; int has_previous_material = 0; const char *line_end = NULL; @@ -788,7 +788,7 @@ static int tinyobj_parse_and_index_mtl_file(tinyobj_material_t **materials_out, #else sscanf(token, "%s", namebuf); #endif - material.name = my_strdup(namebuf, (size_t) (line_end - token)); + material.name = my_strdup(namebuf, (unsigned int) (line_end - token)); /* Add material to material table */ if (material_table) @@ -889,56 +889,56 @@ static int tinyobj_parse_and_index_mtl_file(tinyobj_material_t **materials_out, /* ambient texture */ if ((0 == strncmp(token, "map_Ka", 6)) && IS_SPACE(token[6])) { token += 7; - material.ambient_texname = my_strdup(token, (size_t) (line_end - token)); + material.ambient_texname = my_strdup(token, (unsigned int) (line_end - token)); continue; } /* diffuse texture */ if ((0 == strncmp(token, "map_Kd", 6)) && IS_SPACE(token[6])) { token += 7; - material.diffuse_texname = my_strdup(token, (size_t) (line_end - token)); + material.diffuse_texname = my_strdup(token, (unsigned int) (line_end - token)); continue; } /* specular texture */ if ((0 == strncmp(token, "map_Ks", 6)) && IS_SPACE(token[6])) { token += 7; - material.specular_texname = my_strdup(token, (size_t) (line_end - token)); + material.specular_texname = my_strdup(token, (unsigned int) (line_end - token)); continue; } /* specular highlight texture */ if ((0 == strncmp(token, "map_Ns", 6)) && IS_SPACE(token[6])) { token += 7; - material.specular_highlight_texname = my_strdup(token, (size_t) (line_end - token)); + material.specular_highlight_texname = my_strdup(token, (unsigned int) (line_end - token)); continue; } /* bump texture */ if ((0 == strncmp(token, "map_bump", 8)) && IS_SPACE(token[8])) { token += 9; - material.bump_texname = my_strdup(token, (size_t) (line_end - token)); + material.bump_texname = my_strdup(token, (unsigned int) (line_end - token)); continue; } /* alpha texture */ if ((0 == strncmp(token, "map_d", 5)) && IS_SPACE(token[5])) { token += 6; - material.alpha_texname = my_strdup(token, (size_t) (line_end - token)); + material.alpha_texname = my_strdup(token, (unsigned int) (line_end - token)); continue; } /* bump texture */ if ((0 == strncmp(token, "bump", 4)) && IS_SPACE(token[4])) { token += 5; - material.bump_texname = my_strdup(token, (size_t) (line_end - token)); + material.bump_texname = my_strdup(token, (unsigned int) (line_end - token)); continue; } /* displacement texture */ if ((0 == strncmp(token, "disp", 4)) && IS_SPACE(token[4])) { token += 5; - material.displacement_texname = my_strdup(token, (size_t) (line_end - token)); + material.displacement_texname = my_strdup(token, (unsigned int) (line_end - token)); continue; } @@ -962,7 +962,7 @@ static int tinyobj_parse_and_index_mtl_file(tinyobj_material_t **materials_out, } int tinyobj_parse_mtl_file(tinyobj_material_t **materials_out, - size_t *num_materials_out, + unsigned int *num_materials_out, const char *filename) { return tinyobj_parse_and_index_mtl_file(materials_out, num_materials_out, filename, NULL); } @@ -988,10 +988,10 @@ typedef struct { /* @todo { Use dynamic array } */ tinyobj_vertex_index_t f[TINYOBJ_MAX_FACES_PER_F_LINE]; - size_t num_f; + unsigned int num_f; int f_num_verts[TINYOBJ_MAX_FACES_PER_F_LINE]; - size_t num_f_num_verts; + unsigned int num_f_num_verts; const char *group_name; unsigned int group_name_len; @@ -1011,7 +1011,7 @@ typedef struct { CommandType type; } Command; -static int parseLine(Command *command, const char *p, size_t p_len, +static int parseLine(Command *command, const char *p, unsigned int p_len, int triangulate) { char linebuf[4096]; const char *token; @@ -1073,7 +1073,7 @@ static int parseLine(Command *command, const char *p, size_t p_len, /* face */ if (token[0] == 'f' && IS_SPACE((token[1]))) { - size_t num_f = 0; + unsigned int num_f = 0; tinyobj_vertex_index_t f[TINYOBJ_MAX_FACES_PER_F_LINE]; token += 2; @@ -1090,8 +1090,8 @@ static int parseLine(Command *command, const char *p, size_t p_len, command->type = COMMAND_F; if (triangulate) { - size_t k; - size_t n = 0; + unsigned int k; + unsigned int n = 0; tinyobj_vertex_index_t i0 = f[0]; tinyobj_vertex_index_t i1; @@ -1113,7 +1113,7 @@ static int parseLine(Command *command, const char *p, size_t p_len, command->num_f_num_verts = n; } else { - size_t k = 0; + unsigned int k = 0; assert(num_f < TINYOBJ_MAX_FACES_PER_F_LINE); for (k = 0; k < num_f; k++) { command->f[k] = f[k]; @@ -1134,7 +1134,7 @@ static int parseLine(Command *command, const char *p, size_t p_len, skip_space(&token); command->material_name = p + (token - linebuf); command->material_name_len = (unsigned int)length_until_newline( - token, (p_len - (size_t)(token - linebuf)) + 1); + token, (p_len - (unsigned int)(token - linebuf)) + 1); command->type = COMMAND_USEMTL; return 1; @@ -1148,7 +1148,7 @@ static int parseLine(Command *command, const char *p, size_t p_len, skip_space(&token); command->mtllib_name = p + (token - linebuf); command->mtllib_name_len = (unsigned int)length_until_newline( - token, p_len - (size_t)(token - linebuf)) + + token, p_len - (unsigned int)(token - linebuf)) + 1; command->type = COMMAND_MTLLIB; @@ -1162,7 +1162,7 @@ static int parseLine(Command *command, const char *p, size_t p_len, command->group_name = p + (token - linebuf); command->group_name_len = (unsigned int)length_until_newline( - token, p_len - (size_t)(token - linebuf)) + + token, p_len - (unsigned int)(token - linebuf)) + 1; command->type = COMMAND_G; @@ -1176,7 +1176,7 @@ static int parseLine(Command *command, const char *p, size_t p_len, command->object_name = p + (token - linebuf); command->object_name_len = (unsigned int)length_until_newline( - token, p_len - (size_t)(token - linebuf)) + + token, p_len - (unsigned int)(token - linebuf)) + 1; command->type = COMMAND_O; @@ -1187,11 +1187,11 @@ static int parseLine(Command *command, const char *p, size_t p_len, } typedef struct { - size_t pos; - size_t len; + unsigned int pos; + unsigned int len; } LineInfo; -static int is_line_ending(const char *p, size_t i, size_t end_i) { +static int is_line_ending(const char *p, unsigned int i, unsigned int end_i) { if (p[i] == '\0') return 1; if (p[i] == '\n') return 1; /* this includes \r\n */ if (p[i] == '\r') { @@ -1203,23 +1203,23 @@ static int is_line_ending(const char *p, size_t i, size_t end_i) { } int tinyobj_parse_obj(tinyobj_attrib_t *attrib, tinyobj_shape_t **shapes, - size_t *num_shapes, tinyobj_material_t **materials_out, - size_t *num_materials_out, const char *buf, size_t len, + unsigned int *num_shapes, tinyobj_material_t **materials_out, + unsigned int *num_materials_out, const char *buf, unsigned int len, unsigned int flags) { LineInfo *line_infos = NULL; Command *commands = NULL; - size_t num_lines = 0; + unsigned int num_lines = 0; - size_t num_v = 0; - size_t num_vn = 0; - size_t num_vt = 0; - size_t num_f = 0; - size_t num_faces = 0; + unsigned int num_v = 0; + unsigned int num_vn = 0; + unsigned int num_vt = 0; + unsigned int num_f = 0; + unsigned int num_faces = 0; int mtllib_line_index = -1; tinyobj_material_t *materials = NULL; - size_t num_materials = 0; + unsigned int num_materials = 0; hash_table_t material_table; @@ -1234,11 +1234,11 @@ int tinyobj_parse_obj(tinyobj_attrib_t *attrib, tinyobj_shape_t **shapes, tinyobj_attrib_init(attrib); /* 1. Find '\n' and create line data. */ { - size_t i; - size_t end_idx = len; - size_t prev_pos = 0; - size_t line_no = 0; - size_t last_line_ending = 0; + unsigned int i; + unsigned int end_idx = len; + unsigned int prev_pos = 0; + unsigned int line_no = 0; + unsigned int last_line_ending = 0; /* Count # of lines. */ for (i = 0; i < end_idx; i++) { @@ -1280,7 +1280,7 @@ int tinyobj_parse_obj(tinyobj_attrib_t *attrib, tinyobj_shape_t **shapes, /* 2. parse each line */ { - size_t i = 0; + unsigned int i = 0; for (i = 0; i < num_lines; i++) { int ret = parseLine(&commands[i], &buf[line_infos[i].pos], line_infos[i].len, flags & TINYOBJ_FLAG_TRIANGULATE); @@ -1328,13 +1328,13 @@ int tinyobj_parse_obj(tinyobj_attrib_t *attrib, tinyobj_shape_t **shapes, /* Construct attributes */ { - size_t v_count = 0; - size_t n_count = 0; - size_t t_count = 0; - size_t f_count = 0; - size_t face_count = 0; + unsigned int v_count = 0; + unsigned int n_count = 0; + unsigned int t_count = 0; + unsigned int f_count = 0; + unsigned int face_count = 0; int material_id = -1; /* -1 = default unknown material. */ - size_t i = 0; + unsigned int i = 0; attrib->vertices = (float *)TINYOBJ_MALLOC(sizeof(float) * num_v * 3); attrib->num_vertices = (unsigned int)num_v; @@ -1398,7 +1398,7 @@ int tinyobj_parse_obj(tinyobj_attrib_t *attrib, tinyobj_shape_t **shapes, attrib->texcoords[2 * t_count + 1] = commands[i].ty; t_count++; } else if (commands[i].type == COMMAND_F) { - size_t k = 0; + unsigned int k = 0; for (k = 0; k < commands[i].num_f; k++) { tinyobj_vertex_index_t vi = commands[i].f[k]; int v_idx = fixIndex(vi.v_idx, v_count); @@ -1423,9 +1423,9 @@ int tinyobj_parse_obj(tinyobj_attrib_t *attrib, tinyobj_shape_t **shapes, /* 5. Construct shape information. */ { unsigned int face_count = 0; - size_t i = 0; - size_t n = 0; - size_t shape_idx = 0; + unsigned int i = 0; + unsigned int n = 0; + unsigned int shape_idx = 0; const char *shape_name = NULL; unsigned int shape_name_len = 0; @@ -1497,7 +1497,7 @@ int tinyobj_parse_obj(tinyobj_attrib_t *attrib, tinyobj_shape_t **shapes, } if ((face_count - prev_face_offset) > 0) { - size_t length = face_count - prev_shape_face_offset; + unsigned int length = face_count - prev_shape_face_offset; if (length > 0) { (*shapes)[shape_idx].name = my_strndup(prev_shape_name, prev_shape_name_len); @@ -1548,8 +1548,8 @@ void tinyobj_attrib_free(tinyobj_attrib_t *attrib) { if (attrib->material_ids) TINYOBJ_FREE(attrib->material_ids); } -void tinyobj_shapes_free(tinyobj_shape_t *shapes, size_t num_shapes) { - size_t i; +void tinyobj_shapes_free(tinyobj_shape_t *shapes, unsigned int num_shapes) { + unsigned int i; if (shapes == NULL) return; for (i = 0; i < num_shapes; i++) { @@ -1560,8 +1560,8 @@ void tinyobj_shapes_free(tinyobj_shape_t *shapes, size_t num_shapes) { } void tinyobj_materials_free(tinyobj_material_t *materials, - size_t num_materials) { - size_t i; + unsigned int num_materials) { + unsigned int i; if (materials == NULL) return; for (i = 0; i < num_materials; i++) { From 21092266b57f9f444c51905fceb4eda1dd7f83c3 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 10 Apr 2019 00:44:24 +0200 Subject: [PATCH 50/74] Check textures available before loading --- src/models.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/models.c b/src/models.c index 73f8b9406..f10df262b 100644 --- a/src/models.c +++ b/src/models.c @@ -2846,21 +2846,21 @@ static Model LoadOBJ(const char *fileName) } tinyobj_material_t; */ - model.materials[m].maps[MAP_DIFFUSE].texture = LoadTexture(materials[m].diffuse_texname); //char *diffuse_texname; // map_Kd + if (materials[m].diffuse_texname != NULL) model.materials[m].maps[MAP_DIFFUSE].texture = LoadTexture(materials[m].diffuse_texname); //char *diffuse_texname; // map_Kd model.materials[m].maps[MAP_DIFFUSE].color = (Color){ (float)(materials[m].diffuse[0]*255.0f), (float)(materials[m].diffuse[1]*255.0f), (float)(materials[m].diffuse[2]*255.0f), 255 }; //float diffuse[3]; model.materials[m].maps[MAP_DIFFUSE].value = 0.0f; - model.materials[m].maps[MAP_SPECULAR].texture = LoadTexture(materials[m].specular_texname); //char *specular_texname; // map_Ks + if (materials[m].specular_texname != NULL) model.materials[m].maps[MAP_SPECULAR].texture = LoadTexture(materials[m].specular_texname); //char *specular_texname; // map_Ks model.materials[m].maps[MAP_SPECULAR].color = (Color){ (float)(materials[m].specular[0]*255.0f), (float)(materials[m].specular[1]*255.0f), (float)(materials[m].specular[2]*255.0f), 255 }; //float specular[3]; model.materials[m].maps[MAP_SPECULAR].value = 0.0f; - model.materials[m].maps[MAP_NORMAL].texture = LoadTexture(materials[m].bump_texname); //char *bump_texname; // map_bump, bump + if (materials[m].bump_texname != NULL) model.materials[m].maps[MAP_NORMAL].texture = LoadTexture(materials[m].bump_texname); //char *bump_texname; // map_bump, bump model.materials[m].maps[MAP_NORMAL].color = WHITE; model.materials[m].maps[MAP_NORMAL].value = materials[m].shininess; model.materials[m].maps[MAP_EMISSION].color = (Color){ (float)(materials[m].emission[0]*255.0f), (float)(materials[m].emission[1]*255.0f), (float)(materials[m].emission[2]*255.0f), 255 }; //float emission[3]; - model.materials[m].maps[MAP_HEIGHT].texture = LoadTexture(materials[m].displacement_texname); //char *displacement_texname; // disp + if (materials[m].displacement_texname != NULL) model.materials[m].maps[MAP_HEIGHT].texture = LoadTexture(materials[m].displacement_texname); //char *displacement_texname; // disp } tinyobj_attrib_free(&attrib); From 45c820eeb4e4f559b09c382df5a7808fb1c95115 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 10 Apr 2019 22:39:42 +0200 Subject: [PATCH 51/74] Set default white texture for diffuse mat --- src/models.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/models.c b/src/models.c index f10df262b..d8508492f 100644 --- a/src/models.c +++ b/src/models.c @@ -2846,6 +2846,8 @@ static Model LoadOBJ(const char *fileName) } tinyobj_material_t; */ + model.materials[m].maps[MAP_DIFFUSE].texture = GetTextureDefault(); // Get default texture, in case no texture is defined + if (materials[m].diffuse_texname != NULL) model.materials[m].maps[MAP_DIFFUSE].texture = LoadTexture(materials[m].diffuse_texname); //char *diffuse_texname; // map_Kd model.materials[m].maps[MAP_DIFFUSE].color = (Color){ (float)(materials[m].diffuse[0]*255.0f), (float)(materials[m].diffuse[1]*255.0f), (float)(materials[m].diffuse[2]*255.0f), 255 }; //float diffuse[3]; model.materials[m].maps[MAP_DIFFUSE].value = 0.0f; From 6168a4ca376d26dcac8a19967987280ed399f337 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 10 Apr 2019 23:50:48 +0200 Subject: [PATCH 52/74] Comments review --- src/raylib.h | 54 +++++++++++++++++++++++++++------------------------- 1 file changed, 28 insertions(+), 26 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index dade7aba8..e4e41a152 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -4,19 +4,20 @@ * * FEATURES: * - NO external dependencies, all required libraries included with raylib -* - Multiple platforms support: Windows, Linux, FreeBSD, OpenBSD, NetBSD, DragonFly, MacOS, UWP, Android, Raspberry Pi, HTML5. +* - Multiplatform: Windows, Linux, FreeBSD, OpenBSD, NetBSD, DragonFly, MacOS, UWP, Android, Raspberry Pi, HTML5. * - Written in plain C code (C99) in PascalCase/camelCase notation * - Hardware accelerated with OpenGL (1.1, 2.1, 3.3 or ES2 - choose at compile) * - Unique OpenGL abstraction layer (usable as standalone module): [rlgl] -* - Powerful fonts module with Fonts support (XNA fonts, AngelCode fonts, TTF) +* - Powerful fonts module (XNA SpriteFonts, BMFonts, TTF) * - Outstanding texture formats support, including compressed formats (DXT, ETC, ASTC) * - Full 3d support for 3d Shapes, Models, Billboards, Heightmaps and more! * - Flexible Materials system, supporting classic maps and PBR maps +* - Skeletal Animation support (CPU bones-based animation) * - Shaders support, including Model shaders and Postprocessing shaders * - Powerful math module for Vector, Matrix and Quaternion operations: [raymath] * - Audio loading and playing with streaming support (WAV, OGG, MP3, FLAC, XM, MOD) * - VR stereo rendering with configurable HMD device parameters -* - Complete bindings to LUA (raylib-lua) and Go (raylib-go) +* - Bindings to multiple programming languages available! * * NOTES: * One custom font is loaded by default when InitWindow() [core] @@ -24,24 +25,26 @@ * If using OpenGL 3.3 or ES2, several vertex buffers (VAO/VBO) are created to manage lines-triangles-quads * * DEPENDENCIES (included): -* rglfw (github.com/glfw/glfw) for window/context management and input (only PLATFORM_DESKTOP) [core] -* glad (github.com/Dav1dde/glad) for OpenGL extensions loading (3.3 Core profile, only PLATFORM_DESKTOP) [rlgl] -* mini_al (github.com/dr-soft/mini_al) for audio device/context management [audio] +* [core] rglfw (github.com/glfw/glfw) for window/context management and input (only PLATFORM_DESKTOP) +* [rlgl] glad (github.com/Dav1dde/glad) for OpenGL extensions loading (3.3 Core profile, only PLATFORM_DESKTOP) +* [raudio] miniaudio (github.com/dr-soft/miniaudio) for audio device/context management * * OPTIONAL DEPENDENCIES (included): -* stb_image (Sean Barret) for images loading (BMP, TGA, PNG, JPEG, HDR...) [textures] -* stb_image_resize (Sean Barret) for image resizing algorythms [textures] -* stb_image_write (Sean Barret) for image writting (PNG) [utils] -* stb_truetype (Sean Barret) for ttf fonts loading [text] -* stb_rect_pack (Sean Barret) for rectangles packing [text] -* stb_vorbis (Sean Barret) for OGG audio loading [audio] -* stb_perlin (Sean Barret) for Perlin noise image generation [textures] -* par_shapes (Philip Rideout) for parametric 3d shapes generation [models] -* jar_xm (Joshua Reisenauer) for XM audio module loading [audio] -* jar_mod (Joshua Reisenauer) for MOD audio module loading [audio] -* dr_flac (David Reid) for FLAC audio file loading [audio] -* dr_mp3 (David Reid) for MP3 audio file loading [audio] -* rgif (Charlie Tangora, Ramon Santamaria) for GIF recording [core] +* [core] rgif (Charlie Tangora, Ramon Santamaria) for GIF recording +* [textures] stb_image (Sean Barret) for images loading (BMP, TGA, PNG, JPEG, HDR...) +* [textures] stb_image_write (Sean Barret) for image writting (BMP, TGA, PNG, JPG) +* [textures] stb_image_resize (Sean Barret) for image resizing algorythms +* [textures] stb_perlin (Sean Barret) for Perlin noise image generation +* [text] stb_truetype (Sean Barret) for ttf fonts loading +* [text] stb_rect_pack (Sean Barret) for rectangles packing +* [models] par_shapes (Philip Rideout) for parametric 3d shapes generation +* [models] tinyobj_loader_c (Syoyo Fujita) for models loading (OBJ, MTL) +* [models] cgltf (Johannes Kuhlmann) for models loading (glTF) +* [raudio] stb_vorbis (Sean Barret) for OGG audio loading +* [raudio] dr_flac (David Reid) for FLAC audio file loading +* [raudio] dr_mp3 (David Reid) for MP3 audio file loading +* [raudio] jar_xm (Joshua Reisenauer) for XM audio module loading +* [raudio] jar_mod (Joshua Reisenauer) for MOD audio module loading * * * LICENSE: zlib/libpng @@ -144,13 +147,6 @@ //---------------------------------------------------------------------------------- // Structures Definition //---------------------------------------------------------------------------------- -// Boolean type -#if defined(__STDC__) && __STDC_VERSION__ >= 199901L - #include -#elif !defined(__cplusplus) && !defined(bool) - typedef enum { false, true } bool; -#endif - // Vector2 type typedef struct Vector2 { float x; @@ -453,6 +449,12 @@ typedef struct VrStereoConfig { //---------------------------------------------------------------------------------- // Enumerators Definition //---------------------------------------------------------------------------------- +// Boolean type +#if defined(__STDC__) && __STDC_VERSION__ >= 199901L + #include +#elif !defined(__cplusplus) && !defined(bool) + typedef enum { false, true } bool; +#endif // System config flags // NOTE: Used for bit masks From 1934f2a2f404b08a9dd5541a5407262b6fc4bb6a Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 11 Apr 2019 00:11:11 +0200 Subject: [PATCH 53/74] Some tweaks --- src/raylib.h | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index e4e41a152..e8d927cb2 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -26,7 +26,7 @@ * * DEPENDENCIES (included): * [core] rglfw (github.com/glfw/glfw) for window/context management and input (only PLATFORM_DESKTOP) -* [rlgl] glad (github.com/Dav1dde/glad) for OpenGL extensions loading (3.3 Core profile, only PLATFORM_DESKTOP) +* [rlgl] glad (github.com/Dav1dde/glad) for OpenGL 3.3 extensions loading (only PLATFORM_DESKTOP) * [raudio] miniaudio (github.com/dr-soft/miniaudio) for audio device/context management * * OPTIONAL DEPENDENCIES (included): @@ -147,6 +147,13 @@ //---------------------------------------------------------------------------------- // Structures Definition //---------------------------------------------------------------------------------- +// Boolean type +#if defined(__STDC__) && __STDC_VERSION__ >= 199901L + #include +#elif !defined(__cplusplus) && !defined(bool) + typedef enum { false, true } bool; +#endif + // Vector2 type typedef struct Vector2 { float x; @@ -449,13 +456,6 @@ typedef struct VrStereoConfig { //---------------------------------------------------------------------------------- // Enumerators Definition //---------------------------------------------------------------------------------- -// Boolean type -#if defined(__STDC__) && __STDC_VERSION__ >= 199901L - #include -#elif !defined(__cplusplus) && !defined(bool) - typedef enum { false, true } bool; -#endif - // System config flags // NOTE: Used for bit masks typedef enum { @@ -471,14 +471,14 @@ typedef enum { // Trace log type typedef enum { - LOG_ALL, // Display all logs + LOG_ALL = 0, // Display all logs LOG_TRACE, LOG_DEBUG, LOG_INFO, LOG_WARNING, LOG_ERROR, LOG_FATAL, - LOG_NONE // Disable logging + LOG_NONE // Disable logging } TraceLogType; // Keyboard keys From c7e5585ab9eef695b14fd22ae1b3d897e1b8dcd4 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 11 Apr 2019 11:29:35 +0200 Subject: [PATCH 54/74] Added binding to Factor --- BINDINGS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/BINDINGS.md b/BINDINGS.md index ac8276b9f..c9ce392d1 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -20,6 +20,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to - [raylib-chaiscript](https://github.com/RobLoach/raylib-chaiscript) : raylib **ChaiScript** binding - [node-raylib](https://github.com/RobLoach/node-raylib) : **Node.js** raylib binding - [raylib-odin](https://github.com/kevinw/raylib-odin): **Odin** raylib binding + - [raylib-factor](https://github.com/Silverbeard00/raylib-factor): **Factor** raylib binding - *[raylib flat-assembler Usage example]()* - *[raylib COBOL Usage example](https://github.com/Martinfx/Cobol/tree/master/OpenCobol/Games/raylib)* From aa8d10c48aed2bd41118156aead6eda26456b34f Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 11 Apr 2019 11:57:50 +0200 Subject: [PATCH 55/74] Update BINDINGS.md --- BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index c9ce392d1..38238f9ac 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -21,8 +21,8 @@ Some people ported raylib to other languages in form of bindings or wrappers to - [node-raylib](https://github.com/RobLoach/node-raylib) : **Node.js** raylib binding - [raylib-odin](https://github.com/kevinw/raylib-odin): **Odin** raylib binding - [raylib-factor](https://github.com/Silverbeard00/raylib-factor): **Factor** raylib binding - - *[raylib flat-assembler Usage example]()* - *[raylib COBOL Usage example](https://github.com/Martinfx/Cobol/tree/master/OpenCobol/Games/raylib)* + - *raylib flat-assembler Usage example*: not uploaded yet... Missing some language? Check the [bindings not yet in this list](https://gist.github.com/raysan5/5764cc5b885183f523fce47f098f3d9b#bindings-not-yet-in-the-official-list) or create a new binding! :) From 5bfa67535040ed1cb717908d9f259db0c302d9e6 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 11 Apr 2019 13:53:01 +0200 Subject: [PATCH 56/74] Review VR simulator mechanism - No default VR device parameteres inside raylib - VR device parameter should be provided by user - VR distortion shader should be provided by user --- examples/core/core_vr_simulator.c | 38 ++- examples/core/resources/distortion.fs | 43 +++ src/config.h | 2 - src/raylib.h | 30 +- src/rlgl.h | 416 ++++++++------------------ 5 files changed, 202 insertions(+), 327 deletions(-) create mode 100644 examples/core/resources/distortion.fs diff --git a/examples/core/core_vr_simulator.c b/examples/core/core_vr_simulator.c index 3f59e8391..65054b732 100644 --- a/examples/core/core_vr_simulator.c +++ b/examples/core/core_vr_simulator.c @@ -23,7 +23,35 @@ int main() InitWindow(screenWidth, screenHeight, "raylib [core] example - vr simulator"); // Init VR simulator (Oculus Rift CV1 parameters) - InitVrSimulator(GetVrDeviceInfo(HMD_OCULUS_RIFT_CV1)); + InitVrSimulator(); + + VrDeviceInfo hmd = { 0 }; // VR device parameters (head-mounted-device) + + // Oculus Rift CV1 parameters for simulator + hmd.hResolution = 2160; // HMD horizontal resolution in pixels + hmd.vResolution = 1200; // HMD vertical resolution in pixels + hmd.hScreenSize = 0.133793f; // HMD horizontal size in meters + hmd.vScreenSize = 0.0669f; // HMD vertical size in meters + hmd.vScreenCenter = 0.04678f; // HMD screen center in meters + hmd.eyeToScreenDistance = 0.041f; // HMD distance between eye and display in meters + hmd.lensSeparationDistance = 0.07f; // HMD lens separation distance in meters + hmd.interpupillaryDistance = 0.07f; // HMD IPD (distance between pupils) in meters + + // NOTE: CV1 uses a Fresnel-hybrid-asymmetric lenses with specific distortion compute shaders. + // Following parameters are an approximation to distortion stereo rendering but results differ from actual device. + hmd.lensDistortionValues[0] = 1.0f; // HMD lens distortion constant parameter 0 + hmd.lensDistortionValues[1] = 0.22f; // HMD lens distortion constant parameter 1 + hmd.lensDistortionValues[2] = 0.24f; // HMD lens distortion constant parameter 2 + hmd.lensDistortionValues[3] = 0.0f; // HMD lens distortion constant parameter 3 + hmd.chromaAbCorrection[0] = 0.996f; // HMD chromatic aberration correction parameter 0 + hmd.chromaAbCorrection[1] = -0.004f; // HMD chromatic aberration correction parameter 1 + hmd.chromaAbCorrection[2] = 1.014f; // HMD chromatic aberration correction parameter 2 + hmd.chromaAbCorrection[3] = 0.0f; // HMD chromatic aberration correction parameter 3 + + // Distortion shader (uses device lens distortion and chroma) + Shader distortion = LoadShader(0, "resources/distortion.fs"); + + SetVrConfiguration(hmd, distortion); // Set Vr device parameters for stereo rendering // Define the camera to look into our 3d world Camera camera; @@ -77,9 +105,11 @@ int main() // De-Initialization //-------------------------------------------------------------------------------------- - CloseVrSimulator(); // Close VR simulator - - CloseWindow(); // Close window and OpenGL context + UnloadShader(distortion); // Unload distortion shader + + CloseVrSimulator(); // Close VR simulator + + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; diff --git a/examples/core/resources/distortion.fs b/examples/core/resources/distortion.fs new file mode 100644 index 000000000..207a0affb --- /dev/null +++ b/examples/core/resources/distortion.fs @@ -0,0 +1,43 @@ +#version 330 + +in vec2 fragTexCoord; +in vec4 fragColor; +out vec4 finalColor; + +uniform sampler2D texture0; +uniform vec2 leftLensCenter = vec2(0.288, 0.5); +uniform vec2 rightLensCenter = vec2(0.712, 0.5); +uniform vec2 leftScreenCenter = vec2(0.25, 0.5); +uniform vec2 rightScreenCenter = vec2(0.75, 0.5); +uniform vec2 scale = vec2(0.25, 0.45); +uniform vec2 scaleIn = vec2(4, 2.2222); +uniform vec4 hmdWarpParam = vec4(1, 0.22, 0.24, 0); +uniform vec4 chromaAbParam = vec4(0.996, -0.004, 1.014, 0.0); + +void main() +{ + vec2 lensCenter = fragTexCoord.x < 0.5? leftLensCenter : rightLensCenter; + vec2 screenCenter = fragTexCoord.x < 0.5? leftScreenCenter : rightScreenCenter; + vec2 theta = (fragTexCoord - lensCenter)*scaleIn; + float rSq = theta.x*theta.x + theta.y*theta.y; + vec2 theta1 = theta*(hmdWarpParam.x + hmdWarpParam.y*rSq + hmdWarpParam.z*rSq*rSq + hmdWarpParam.w*rSq*rSq*rSq); + vec2 thetaBlue = theta1*(chromaAbParam.z + chromaAbParam.w*rSq); + vec2 tcBlue = lensCenter + scale*thetaBlue; + + if (any(bvec2(clamp(tcBlue, screenCenter - vec2(0.25, 0.5), screenCenter + vec2(0.25, 0.5)) - tcBlue))) + { + finalColor = vec4(0.0, 0.0, 0.0, 1.0); + } + else + { + float blue = texture(texture0, tcBlue).b; + vec2 tcGreen = lensCenter + scale*theta1; + float green = texture(texture0, tcGreen).g; + + vec2 thetaRed = theta1*(chromaAbParam.x + chromaAbParam.y*rSq); + vec2 tcRed = lensCenter + scale*thetaRed; + + float red = texture(texture0, tcRed).r; + finalColor = vec4(red, green, blue, 1.0); + } +}; diff --git a/src/config.h b/src/config.h index dc30eeb25..2ea4b438f 100644 --- a/src/config.h +++ b/src/config.h @@ -57,8 +57,6 @@ //------------------------------------------------------------------------------------ // Support VR simulation functionality (stereo rendering) #define SUPPORT_VR_SIMULATOR 1 -// Include stereo rendering distortion shader (shader_distortion.h) -#define SUPPORT_DISTORTION_SHADER 1 //------------------------------------------------------------------------------------ diff --git a/src/raylib.h b/src/raylib.h index e8d927cb2..73661f58d 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -443,16 +443,6 @@ typedef struct VrDeviceInfo { float chromaAbCorrection[4]; // HMD chromatic aberration correction parameters } VrDeviceInfo; -// VR Stereo rendering configuration for simulator -typedef struct VrStereoConfig { - RenderTexture2D stereoFbo; // VR stereo rendering framebuffer - Shader distortionShader; // VR stereo rendering distortion shader - Matrix eyesProjection[2]; // VR stereo rendering eyes projection matrices - Matrix eyesViewOffset[2]; // VR stereo rendering eyes view offset matrices - int eyeViewportRight[4]; // VR stereo rendering right eye viewport [x, y, w, h] - int eyeViewportLeft[4]; // VR stereo rendering left eye viewport [x, y, w, h] -} VrStereoConfig; - //---------------------------------------------------------------------------------- // Enumerators Definition //---------------------------------------------------------------------------------- @@ -860,16 +850,6 @@ typedef enum { CAMERA_ORTHOGRAPHIC } CameraType; -// Head Mounted Display devices -typedef enum { - HMD_DEFAULT_DEVICE = 0, - HMD_OCULUS_RIFT_DK2, - HMD_OCULUS_RIFT_CV1, - HMD_OCULUS_GO, - HMD_VALVE_HTC_VIVE, - HMD_SONY_PSVR -} VrDeviceType; - // Type of n-patch typedef enum { NPT_9PATCH = 0, // Npatch defined by 3x3 tiles @@ -1330,7 +1310,7 @@ RLAPI Shader GetShaderDefault(void); // Get RLAPI Texture2D GetTextureDefault(void); // Get default texture // Shader configuration functions -RLAPI int GetShaderLocation(Shader shader, const char *uniformName); // Get shader uniform location +RLAPI int GetShaderLocation(Shader shader, const char *uniformName); // Get shader uniform location RLAPI void SetShaderValue(Shader shader, int uniformLoc, const void *value, int uniformType); // Set shader uniform value RLAPI void SetShaderValueV(Shader shader, int uniformLoc, const void *value, int uniformType, int count); // Set shader uniform value vector RLAPI void SetShaderValueMatrix(Shader shader, int uniformLoc, Matrix mat); // Set shader uniform value (matrix 4x4) @@ -1344,7 +1324,7 @@ RLAPI Matrix GetMatrixModelview(); // Get RLAPI Texture2D GenTextureCubemap(Shader shader, Texture2D skyHDR, int size); // Generate cubemap texture from HDR texture RLAPI Texture2D GenTextureIrradiance(Shader shader, Texture2D cubemap, int size); // Generate irradiance texture using cubemap data RLAPI Texture2D GenTexturePrefilter(Shader shader, Texture2D cubemap, int size); // Generate prefilter texture using cubemap data -RLAPI Texture2D GenTextureBRDF(Shader shader, int size); // Generate BRDF texture using cubemap data +RLAPI Texture2D GenTextureBRDF(Shader shader, int size); // Generate BRDF texture // Shading begin/end functions RLAPI void BeginShaderMode(Shader shader); // Begin custom shader drawing @@ -1355,10 +1335,10 @@ RLAPI void BeginScissorMode(int x, int y, int width, int height); // Beg RLAPI void EndScissorMode(void); // End scissor mode // VR control functions -RLAPI VrDeviceInfo GetVrDeviceInfo(int vrDeviceType); // Get VR device information for some standard devices -RLAPI void InitVrSimulator(VrDeviceInfo info); // Init VR simulator for selected device parameters -RLAPI void UpdateVrTracking(Camera *camera); // Update VR tracking (position and orientation) and camera +RLAPI void InitVrSimulator(void); // Init VR simulator for selected device parameters RLAPI void CloseVrSimulator(void); // Close VR simulator for current device +RLAPI void UpdateVrTracking(Camera *camera); // Update VR tracking (position and orientation) and camera +RLAPI void SetVrConfiguration(VrDeviceInfo info, Shader distortion); // Set stereo rendering configuration parameters RLAPI bool IsVrSimulatorReady(void); // Detect if VR simulator is ready RLAPI void ToggleVrMode(void); // Enable/Disable VR experience RLAPI void BeginVrDrawing(void); // Begin VR simulator stereo rendering diff --git a/src/rlgl.h b/src/rlgl.h index 3e4282414..5c0d28230 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -32,9 +32,6 @@ * #define SUPPORT_VR_SIMULATOR * Support VR simulation functionality (stereo rendering) * -* #define SUPPORT_DISTORTION_SHADER -* Include stereo rendering distortion shader (embedded) -* * DEPENDENCIES: * raymath - 3D math functionality (Vector3, Matrix, Quaternion) * GLAD - OpenGL extensions loading (OpenGL 3.3 Core only) @@ -263,7 +260,6 @@ typedef unsigned char byte; // VR Stereo rendering configuration for simulator typedef struct VrStereoConfig { - RenderTexture2D stereoFbo; // VR stereo rendering framebuffer Shader distortionShader; // VR stereo rendering distortion shader Matrix eyesProjection[2]; // VR stereo rendering eyes projection matrices Matrix eyesViewOffset[2]; // VR stereo rendering eyes view offset matrices @@ -390,16 +386,6 @@ typedef unsigned char byte; #define MAP_DIFFUSE MAP_ALBEDO #define MAP_SPECULAR MAP_METALNESS - - // VR Head Mounted Display devices - typedef enum { - HMD_DEFAULT_DEVICE = 0, - HMD_OCULUS_RIFT_DK2, - HMD_OCULUS_RIFT_CV1, - HMD_OCULUS_GO, - HMD_VALVE_HTC_VIVE, - HMD_SONY_PSVR - } VrDevice; #endif #if defined(__cplusplus) @@ -534,14 +520,14 @@ void BeginBlendMode(int mode); // Begin blending mode (alpha, void EndBlendMode(void); // End blending mode (reset to default: alpha blending) // VR control functions -VrDeviceInfo GetVrDeviceInfo(int vrDeviceType); // Get VR device information for some standard devices -void InitVrSimulator(VrDeviceInfo info); // Init VR simulator for selected device parameters -void UpdateVrTracking(Camera *camera); // Update VR tracking (position and orientation) and camera -void CloseVrSimulator(void); // Close VR simulator for current device -bool IsVrSimulatorReady(void); // Detect if VR simulator is ready -void ToggleVrMode(void); // Enable/Disable VR experience -void BeginVrDrawing(void); // Begin VR simulator stereo rendering -void EndVrDrawing(void); // End VR simulator stereo rendering +RLAPI void InitVrSimulator(void); // Init VR simulator for selected device parameters +RLAPI void CloseVrSimulator(void); // Close VR simulator for current device +RLAPI void UpdateVrTracking(Camera *camera); // Update VR tracking (position and orientation) and camera +RLAPI void SetVrConfiguration(VrDeviceInfo info, Shader distortion); // Set stereo rendering configuration parameters +RLAPI bool IsVrSimulatorReady(void); // Detect if VR simulator is ready +RLAPI void ToggleVrMode(void); // Enable/Disable VR experience +RLAPI void BeginVrDrawing(void); // Begin VR simulator stereo rendering +RLAPI void EndVrDrawing(void); // End VR simulator stereo rendering void TraceLog(int msgType, const char *text, ...); // Show trace log messages (LOG_INFO, LOG_WARNING, LOG_ERROR, LOG_DEBUG) int GetPixelDataSize(int width, int height, int format);// Get pixel data size in bytes (image or texture) @@ -561,10 +547,7 @@ int GetPixelDataSize(int width, int height, int format);// Get pixel data size i #if defined(RLGL_IMPLEMENTATION) -#if defined(RLGL_STANDALONE) - #define SUPPORT_VR_SIMULATOR - #define SUPPORT_DISTORTION_SHADER -#else +#if !defined(RLGL_STANDALONE) // Check if config flags have been externally provided on compilation line #if !defined(EXTERNAL_CONFIG_FLAGS) #include "config.h" // Defines module configuration flags @@ -740,88 +723,20 @@ typedef struct DrawCall { //Matrix modelview; // Modelview matrix for this draw } DrawCall; +#if defined(SUPPORT_VR_SIMULATOR) +// VR Stereo rendering configuration for simulator +typedef struct VrStereoConfig { + Shader distortionShader; // VR stereo rendering distortion shader + Matrix eyesProjection[2]; // VR stereo rendering eyes projection matrices + Matrix eyesViewOffset[2]; // VR stereo rendering eyes view offset matrices + int eyeViewportRight[4]; // VR stereo rendering right eye viewport [x, y, w, h] + int eyeViewportLeft[4]; // VR stereo rendering left eye viewport [x, y, w, h] +} VrStereoConfig; +#endif + //---------------------------------------------------------------------------------- // Global Variables Definition //---------------------------------------------------------------------------------- -#if !defined(GRAPHICS_API_OPENGL_11) && defined(SUPPORT_DISTORTION_SHADER) - // Distortion shader embedded - static char distortionFShaderStr[] = - #if defined(GRAPHICS_API_OPENGL_21) - "#version 120 \n" - #elif defined(GRAPHICS_API_OPENGL_ES2) - "#version 100 \n" - "precision mediump float; \n" // precision required for OpenGL ES2 (WebGL) - #endif - #if defined(GRAPHICS_API_OPENGL_ES2) || defined(GRAPHICS_API_OPENGL_21) - "varying vec2 fragTexCoord; \n" - "varying vec4 fragColor; \n" - #elif defined(GRAPHICS_API_OPENGL_33) - "#version 330 \n" - "in vec2 fragTexCoord; \n" - "in vec4 fragColor; \n" - "out vec4 finalColor; \n" - #endif - "uniform sampler2D texture0; \n" - #if defined(GRAPHICS_API_OPENGL_ES2) || defined(GRAPHICS_API_OPENGL_21) - "uniform vec2 leftLensCenter; \n" - "uniform vec2 rightLensCenter; \n" - "uniform vec2 leftScreenCenter; \n" - "uniform vec2 rightScreenCenter; \n" - "uniform vec2 scale; \n" - "uniform vec2 scaleIn; \n" - "uniform vec4 hmdWarpParam; \n" - "uniform vec4 chromaAbParam; \n" - #elif defined(GRAPHICS_API_OPENGL_33) - "uniform vec2 leftLensCenter = vec2(0.288, 0.5); \n" - "uniform vec2 rightLensCenter = vec2(0.712, 0.5); \n" - "uniform vec2 leftScreenCenter = vec2(0.25, 0.5); \n" - "uniform vec2 rightScreenCenter = vec2(0.75, 0.5); \n" - "uniform vec2 scale = vec2(0.25, 0.45); \n" - "uniform vec2 scaleIn = vec2(4, 2.2222); \n" - "uniform vec4 hmdWarpParam = vec4(1, 0.22, 0.24, 0); \n" - "uniform vec4 chromaAbParam = vec4(0.996, -0.004, 1.014, 0.0); \n" - #endif - "void main() \n" - "{ \n" - " vec2 lensCenter = fragTexCoord.x < 0.5? leftLensCenter : rightLensCenter; \n" - " vec2 screenCenter = fragTexCoord.x < 0.5? leftScreenCenter : rightScreenCenter; \n" - " vec2 theta = (fragTexCoord - lensCenter)*scaleIn; \n" - " float rSq = theta.x*theta.x + theta.y*theta.y; \n" - " vec2 theta1 = theta*(hmdWarpParam.x + hmdWarpParam.y*rSq + hmdWarpParam.z*rSq*rSq + hmdWarpParam.w*rSq*rSq*rSq); \n" - " vec2 thetaBlue = theta1*(chromaAbParam.z + chromaAbParam.w*rSq); \n" - " vec2 tcBlue = lensCenter + scale*thetaBlue; \n" - " if (any(bvec2(clamp(tcBlue, screenCenter - vec2(0.25, 0.5), screenCenter + vec2(0.25, 0.5)) - tcBlue))) \n" - " { \n" - #if defined(GRAPHICS_API_OPENGL_ES2) || defined(GRAPHICS_API_OPENGL_21) - " gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0); \n" - #elif defined(GRAPHICS_API_OPENGL_33) - " finalColor = vec4(0.0, 0.0, 0.0, 1.0); \n" - #endif - " } \n" - " else \n" - " { \n" - #if defined(GRAPHICS_API_OPENGL_ES2) || defined(GRAPHICS_API_OPENGL_21) - " float blue = texture2D(texture0, tcBlue).b; \n" - " vec2 tcGreen = lensCenter + scale*theta1; \n" - " float green = texture2D(texture0, tcGreen).g; \n" - #elif defined(GRAPHICS_API_OPENGL_33) - " float blue = texture(texture0, tcBlue).b; \n" - " vec2 tcGreen = lensCenter + scale*theta1; \n" - " float green = texture(texture0, tcGreen).g; \n" - #endif - " vec2 thetaRed = theta1*(chromaAbParam.x + chromaAbParam.y*rSq); \n" - " vec2 tcRed = lensCenter + scale*thetaRed; \n" - #if defined(GRAPHICS_API_OPENGL_ES2) || defined(GRAPHICS_API_OPENGL_21) - " float red = texture2D(texture0, tcRed).r; \n" - " gl_FragColor = vec4(red, green, blue, 1.0); \n" - #elif defined(GRAPHICS_API_OPENGL_33) - " float red = texture(texture0, tcRed).r; \n" - " finalColor = vec4(red, green, blue, 1.0); \n" - #endif - " } \n" - "} \n"; -#endif - #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) static Matrix stack[MAX_MATRIX_STACK_SIZE] = { 0 }; static int stackCounter = 0; @@ -891,6 +806,7 @@ static PFNGLDELETEVERTEXARRAYSOESPROC glDeleteVertexArrays; #if defined(SUPPORT_VR_SIMULATOR) // VR global variables static VrStereoConfig vrConfig = { 0 }; // VR stereo configuration for simulator +static RenderTexture2D stereoFbo; // VR stereo rendering framebuffer static bool vrSimulatorReady = false; // VR simulator ready flag static bool vrStereoRender = false; // VR stereo rendering enabled/disabled flag // NOTE: This flag is useful to render data over stereo image (i.e. FPS) @@ -924,7 +840,6 @@ static void GenDrawCube(void); // Generate and draw cube static void GenDrawQuad(void); // Generate and draw quad #if defined(SUPPORT_VR_SIMULATOR) -static VrStereoConfig SetStereoConfig(VrDeviceInfo info, Shader distortion); // Configure stereo rendering (including distortion shader) with HMD device parameters static void SetStereoView(int eye, Matrix matProjection, Matrix matModelView); // Set internal projection and modelview matrix depending on eye #endif @@ -3577,101 +3492,17 @@ void EndScissorMode(void) } #if defined(SUPPORT_VR_SIMULATOR) -// Get VR device information for some standard devices -VrDeviceInfo GetVrDeviceInfo(int vrDeviceType) -{ - VrDeviceInfo hmd = { 0 }; // Current VR device info - - switch (vrDeviceType) - { - case HMD_DEFAULT_DEVICE: - case HMD_OCULUS_RIFT_CV1: - { - // Oculus Rift CV1 parameters - // NOTE: CV1 represents a complete HMD redesign compared to previous versions, - // new Fresnel-hybrid-asymmetric lenses have been added and, consequently, - // previous parameters (DK2) and distortion shader (DK2) doesn't work any more. - // I just defined a set of parameters for simulator that approximate to CV1 stereo rendering - // but result is not the same obtained with Oculus PC SDK. - hmd.hResolution = 2160; // HMD horizontal resolution in pixels - hmd.vResolution = 1200; // HMD vertical resolution in pixels - hmd.hScreenSize = 0.133793f; // HMD horizontal size in meters - hmd.vScreenSize = 0.0669f; // HMD vertical size in meters - hmd.vScreenCenter = 0.04678f; // HMD screen center in meters - hmd.eyeToScreenDistance = 0.041f; // HMD distance between eye and display in meters - hmd.lensSeparationDistance = 0.07f; // HMD lens separation distance in meters - hmd.interpupillaryDistance = 0.07f; // HMD IPD (distance between pupils) in meters - hmd.lensDistortionValues[0] = 1.0f; // HMD lens distortion constant parameter 0 - hmd.lensDistortionValues[1] = 0.22f; // HMD lens distortion constant parameter 1 - hmd.lensDistortionValues[2] = 0.24f; // HMD lens distortion constant parameter 2 - hmd.lensDistortionValues[3] = 0.0f; // HMD lens distortion constant parameter 3 - hmd.chromaAbCorrection[0] = 0.996f; // HMD chromatic aberration correction parameter 0 - hmd.chromaAbCorrection[1] = -0.004f; // HMD chromatic aberration correction parameter 1 - hmd.chromaAbCorrection[2] = 1.014f; // HMD chromatic aberration correction parameter 2 - hmd.chromaAbCorrection[3] = 0.0f; // HMD chromatic aberration correction parameter 3 - - TraceLog(LOG_INFO, "Initializing VR Simulator (Oculus Rift CV1)"); - } break; - case HMD_OCULUS_RIFT_DK2: - { - // Oculus Rift DK2 parameters - hmd.hResolution = 1280; // HMD horizontal resolution in pixels - hmd.vResolution = 800; // HMD vertical resolution in pixels - hmd.hScreenSize = 0.14976f; // HMD horizontal size in meters - hmd.vScreenSize = 0.09356f; // HMD vertical size in meters - hmd.vScreenCenter = 0.04678f; // HMD screen center in meters - hmd.eyeToScreenDistance = 0.041f; // HMD distance between eye and display in meters - hmd.lensSeparationDistance = 0.0635f; // HMD lens separation distance in meters - hmd.interpupillaryDistance = 0.064f; // HMD IPD (distance between pupils) in meters - hmd.lensDistortionValues[0] = 1.0f; // HMD lens distortion constant parameter 0 - hmd.lensDistortionValues[1] = 0.22f; // HMD lens distortion constant parameter 1 - hmd.lensDistortionValues[2] = 0.24f; // HMD lens distortion constant parameter 2 - hmd.lensDistortionValues[3] = 0.0f; // HMD lens distortion constant parameter 3 - hmd.chromaAbCorrection[0] = 0.996f; // HMD chromatic aberration correction parameter 0 - hmd.chromaAbCorrection[1] = -0.004f; // HMD chromatic aberration correction parameter 1 - hmd.chromaAbCorrection[2] = 1.014f; // HMD chromatic aberration correction parameter 2 - hmd.chromaAbCorrection[3] = 0.0f; // HMD chromatic aberration correction parameter 3 - - TraceLog(LOG_INFO, "Initializing VR Simulator (Oculus Rift DK2)"); - } break; - case HMD_OCULUS_GO: - { - // TODO: Provide device display and lens parameters - } - case HMD_VALVE_HTC_VIVE: - { - // TODO: Provide device display and lens parameters - } - case HMD_SONY_PSVR: - { - // TODO: Provide device display and lens parameters - } - default: break; - } - - return hmd; -} - // Init VR simulator for selected device parameters -// NOTE: It modifies the global variable: VrStereoConfig vrConfig -void InitVrSimulator(VrDeviceInfo info) +// NOTE: It modifies the global variable: stereoFbo +void InitVrSimulator(void) { #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - Shader distortion = { 0 }; - -#if defined(SUPPORT_DISTORTION_SHADER) - // Load distortion shader - distortion = LoadShaderCode(NULL, distortionFShaderStr); - if (distortion.id > 0) SetShaderDefaultLocations(&distortion); -#endif - - // Set VR configutarion parameters, including distortion shader - vrConfig = SetStereoConfig(info, distortion); - + // Initialize framebuffer and textures for stereo rendering + // NOTE: Screen size should match HMD aspect ratio + stereoFbo = rlLoadRenderTexture(screenWidth, screenHeight, UNCOMPRESSED_R8G8B8A8, 24, false); + vrSimulatorReady = true; -#endif - -#if defined(GRAPHICS_API_OPENGL_11) +#else TraceLog(LOG_WARNING, "VR Simulator not supported on OpenGL 1.1"); #endif } @@ -3687,16 +3518,92 @@ void UpdateVrTracking(Camera *camera) void CloseVrSimulator(void) { #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - if (vrSimulatorReady) - { - rlDeleteRenderTextures(vrConfig.stereoFbo); // Unload stereo framebuffer and texture - #if defined(SUPPORT_DISTORTION_SHADER) - UnloadShader(vrConfig.distortionShader); // Unload distortion shader - #endif - } + if (vrSimulatorReady) rlDeleteRenderTextures(stereoFbo); // Unload stereo framebuffer and texture #endif } +// Set stereo rendering configuration parameters +void SetVrConfiguration(VrDeviceInfo hmd, Shader distortion) +{ + // Reset vrConfig for a new values assignment + memset(&vrConfig, 0, sizeof(vrConfig)); + + // Assign distortion shader + vrConfig.distortionShader = distortion; + + // Compute aspect ratio + float aspect = ((float)hmd.hResolution*0.5f)/(float)hmd.vResolution; + + // Compute lens parameters + float lensShift = (hmd.hScreenSize*0.25f - hmd.lensSeparationDistance*0.5f)/hmd.hScreenSize; + float leftLensCenter[2] = { 0.25f + lensShift, 0.5f }; + float rightLensCenter[2] = { 0.75f - lensShift, 0.5f }; + float leftScreenCenter[2] = { 0.25f, 0.5f }; + float rightScreenCenter[2] = { 0.75f, 0.5f }; + + // Compute distortion scale parameters + // NOTE: To get lens max radius, lensShift must be normalized to [-1..1] + float lensRadius = (float)fabs(-1.0f - 4.0f*lensShift); + float lensRadiusSq = lensRadius*lensRadius; + float distortionScale = hmd.lensDistortionValues[0] + + hmd.lensDistortionValues[1]*lensRadiusSq + + hmd.lensDistortionValues[2]*lensRadiusSq*lensRadiusSq + + hmd.lensDistortionValues[3]*lensRadiusSq*lensRadiusSq*lensRadiusSq; + + TraceLog(LOG_DEBUG, "VR: Distortion Scale: %f", distortionScale); + + float normScreenWidth = 0.5f; + float normScreenHeight = 1.0f; + float scaleIn[2] = { 2.0f/normScreenWidth, 2.0f/normScreenHeight/aspect }; + float scale[2] = { normScreenWidth*0.5f/distortionScale, normScreenHeight*0.5f*aspect/distortionScale }; + + TraceLog(LOG_DEBUG, "VR: Distortion Shader: LeftLensCenter = { %f, %f }", leftLensCenter[0], leftLensCenter[1]); + TraceLog(LOG_DEBUG, "VR: Distortion Shader: RightLensCenter = { %f, %f }", rightLensCenter[0], rightLensCenter[1]); + TraceLog(LOG_DEBUG, "VR: Distortion Shader: Scale = { %f, %f }", scale[0], scale[1]); + TraceLog(LOG_DEBUG, "VR: Distortion Shader: ScaleIn = { %f, %f }", scaleIn[0], scaleIn[1]); + + // Fovy is normally computed with: 2*atan2(hmd.vScreenSize, 2*hmd.eyeToScreenDistance) + // ...but with lens distortion it is increased (see Oculus SDK Documentation) + //float fovy = 2.0f*atan2(hmd.vScreenSize*0.5f*distortionScale, hmd.eyeToScreenDistance); // Really need distortionScale? + float fovy = 2.0f*(float)atan2(hmd.vScreenSize*0.5f, hmd.eyeToScreenDistance); + + // Compute camera projection matrices + float projOffset = 4.0f*lensShift; // Scaled to projection space coordinates [-1..1] + Matrix proj = MatrixPerspective(fovy, aspect, 0.01, 1000.0); + vrConfig.eyesProjection[0] = MatrixMultiply(proj, MatrixTranslate(projOffset, 0.0f, 0.0f)); + vrConfig.eyesProjection[1] = MatrixMultiply(proj, MatrixTranslate(-projOffset, 0.0f, 0.0f)); + + // Compute camera transformation matrices + // NOTE: Camera movement might seem more natural if we model the head. + // Our axis of rotation is the base of our head, so we might want to add + // some y (base of head to eye level) and -z (center of head to eye protrusion) to the camera positions. + vrConfig.eyesViewOffset[0] = MatrixTranslate(-hmd.interpupillaryDistance*0.5f, 0.075f, 0.045f); + vrConfig.eyesViewOffset[1] = MatrixTranslate(hmd.interpupillaryDistance*0.5f, 0.075f, 0.045f); + + // Compute eyes Viewports + vrConfig.eyeViewportRight[2] = hmd.hResolution/2; + vrConfig.eyeViewportRight[3] = hmd.vResolution; + + vrConfig.eyeViewportLeft[0] = hmd.hResolution/2; + vrConfig.eyeViewportLeft[1] = 0; + vrConfig.eyeViewportLeft[2] = hmd.hResolution/2; + vrConfig.eyeViewportLeft[3] = hmd.vResolution; + + if (vrConfig.distortionShader.id > 0) + { + // Update distortion shader with lens and distortion-scale parameters + SetShaderValue(vrConfig.distortionShader, GetShaderLocation(vrConfig.distortionShader, "leftLensCenter"), leftLensCenter, UNIFORM_VEC2); + SetShaderValue(vrConfig.distortionShader, GetShaderLocation(vrConfig.distortionShader, "rightLensCenter"), rightLensCenter, UNIFORM_VEC2); + SetShaderValue(vrConfig.distortionShader, GetShaderLocation(vrConfig.distortionShader, "leftScreenCenter"), leftScreenCenter, UNIFORM_VEC2); + SetShaderValue(vrConfig.distortionShader, GetShaderLocation(vrConfig.distortionShader, "rightScreenCenter"), rightScreenCenter, UNIFORM_VEC2); + + SetShaderValue(vrConfig.distortionShader, GetShaderLocation(vrConfig.distortionShader, "scale"), scale, UNIFORM_VEC2); + SetShaderValue(vrConfig.distortionShader, GetShaderLocation(vrConfig.distortionShader, "scaleIn"), scaleIn, UNIFORM_VEC2); + SetShaderValue(vrConfig.distortionShader, GetShaderLocation(vrConfig.distortionShader, "hmdWarpParam"), hmd.lensDistortionValues, UNIFORM_VEC4); + SetShaderValue(vrConfig.distortionShader, GetShaderLocation(vrConfig.distortionShader, "chromaAbParam"), hmd.chromaAbCorrection, UNIFORM_VEC4); + } +} + // Detect if VR simulator is running bool IsVrSimulatorReady(void) { @@ -3733,7 +3640,7 @@ void BeginVrDrawing(void) if (vrSimulatorReady) { // Setup framebuffer for stereo rendering - rlEnableRenderTexture(vrConfig.stereoFbo.id); + rlEnableRenderTexture(stereoFbo.id); // NOTE: If your application is configured to treat the texture as a linear format (e.g. GL_RGBA) // and performs linear-to-gamma conversion in GLSL or does not care about gamma-correction, then: @@ -3771,13 +3678,11 @@ void EndVrDrawing(void) rlMatrixMode(RL_MODELVIEW); // Enable internal modelview matrix rlLoadIdentity(); // Reset internal modelview matrix -#if defined(SUPPORT_DISTORTION_SHADER) - // Draw RenderTexture (stereoFbo) using distortion shader - currentShader = vrConfig.distortionShader; -#else - currentShader = GetShaderDefault(); -#endif - rlEnableTexture(vrConfig.stereoFbo.texture.id); + // Draw RenderTexture (stereoFbo) using distortion shader if available + if (vrConfig.distortionShader.id > 0) currentShader = vrConfig.distortionShader; + else currentShader = GetShaderDefault(); + + rlEnableTexture(stereoFbo.texture.id); rlPushMatrix(); rlBegin(RL_QUADS); @@ -3790,15 +3695,15 @@ void EndVrDrawing(void) // Bottom-right corner for texture and quad rlTexCoord2f(0.0f, 0.0f); - rlVertex2f(0.0f, (float)vrConfig.stereoFbo.texture.height); + rlVertex2f(0.0f, (float)stereoFbo.texture.height); // Top-right corner for texture and quad rlTexCoord2f(1.0f, 0.0f); - rlVertex2f( (float)vrConfig.stereoFbo.texture.width, (float)vrConfig.stereoFbo.texture.height); + rlVertex2f( (float)stereoFbo.texture.width, (float)stereoFbo.texture.height); // Top-left corner for texture and quad rlTexCoord2f(1.0f, 1.0f); - rlVertex2f( (float)vrConfig.stereoFbo.texture.width, 0.0f); + rlVertex2f( (float)stereoFbo.texture.width, 0.0f); rlEnd(); rlPopMatrix(); @@ -4472,87 +4377,6 @@ static void GenDrawCube(void) } #if defined(SUPPORT_VR_SIMULATOR) -// Configure stereo rendering (including distortion shader) with HMD device parameters -static VrStereoConfig SetStereoConfig(VrDeviceInfo hmd, Shader distortion) -{ - VrStereoConfig config = { 0 }; - - // Initialize framebuffer and textures for stereo rendering - // NOTE: Screen size should match HMD aspect ratio - config.stereoFbo = rlLoadRenderTexture(screenWidth, screenHeight, UNCOMPRESSED_R8G8B8A8, 24, false); - - // Assign distortion shader - config.distortionShader = distortion; - - // Compute aspect ratio - float aspect = ((float)hmd.hResolution*0.5f)/(float)hmd.vResolution; - - // Compute lens parameters - float lensShift = (hmd.hScreenSize*0.25f - hmd.lensSeparationDistance*0.5f)/hmd.hScreenSize; - float leftLensCenter[2] = { 0.25f + lensShift, 0.5f }; - float rightLensCenter[2] = { 0.75f - lensShift, 0.5f }; - float leftScreenCenter[2] = { 0.25f, 0.5f }; - float rightScreenCenter[2] = { 0.75f, 0.5f }; - - // Compute distortion scale parameters - // NOTE: To get lens max radius, lensShift must be normalized to [-1..1] - float lensRadius = (float)fabs(-1.0f - 4.0f*lensShift); - float lensRadiusSq = lensRadius*lensRadius; - float distortionScale = hmd.lensDistortionValues[0] + - hmd.lensDistortionValues[1]*lensRadiusSq + - hmd.lensDistortionValues[2]*lensRadiusSq*lensRadiusSq + - hmd.lensDistortionValues[3]*lensRadiusSq*lensRadiusSq*lensRadiusSq; - - TraceLog(LOG_DEBUG, "VR: Distortion Scale: %f", distortionScale); - - float normScreenWidth = 0.5f; - float normScreenHeight = 1.0f; - float scaleIn[2] = { 2.0f/normScreenWidth, 2.0f/normScreenHeight/aspect }; - float scale[2] = { normScreenWidth*0.5f/distortionScale, normScreenHeight*0.5f*aspect/distortionScale }; - - TraceLog(LOG_DEBUG, "VR: Distortion Shader: LeftLensCenter = { %f, %f }", leftLensCenter[0], leftLensCenter[1]); - TraceLog(LOG_DEBUG, "VR: Distortion Shader: RightLensCenter = { %f, %f }", rightLensCenter[0], rightLensCenter[1]); - TraceLog(LOG_DEBUG, "VR: Distortion Shader: Scale = { %f, %f }", scale[0], scale[1]); - TraceLog(LOG_DEBUG, "VR: Distortion Shader: ScaleIn = { %f, %f }", scaleIn[0], scaleIn[1]); - - // Fovy is normally computed with: 2*atan2(hmd.vScreenSize, 2*hmd.eyeToScreenDistance) - // ...but with lens distortion it is increased (see Oculus SDK Documentation) - //float fovy = 2.0f*atan2(hmd.vScreenSize*0.5f*distortionScale, hmd.eyeToScreenDistance); // Really need distortionScale? - float fovy = 2.0f*(float)atan2(hmd.vScreenSize*0.5f, hmd.eyeToScreenDistance); - - // Compute camera projection matrices - float projOffset = 4.0f*lensShift; // Scaled to projection space coordinates [-1..1] - Matrix proj = MatrixPerspective(fovy, aspect, 0.01, 1000.0); - config.eyesProjection[0] = MatrixMultiply(proj, MatrixTranslate(projOffset, 0.0f, 0.0f)); - config.eyesProjection[1] = MatrixMultiply(proj, MatrixTranslate(-projOffset, 0.0f, 0.0f)); - - // Compute camera transformation matrices - // NOTE: Camera movement might seem more natural if we model the head. - // Our axis of rotation is the base of our head, so we might want to add - // some y (base of head to eye level) and -z (center of head to eye protrusion) to the camera positions. - config.eyesViewOffset[0] = MatrixTranslate(-hmd.interpupillaryDistance*0.5f, 0.075f, 0.045f); - config.eyesViewOffset[1] = MatrixTranslate(hmd.interpupillaryDistance*0.5f, 0.075f, 0.045f); - - // Compute eyes Viewports - //config.eyeViewportRight[0] = (int[4]){ 0, 0, hmd.hResolution/2, hmd.vResolution }; - //config.eyeViewportLeft[0] = (int[4]){ hmd.hResolution/2, 0, hmd.hResolution/2, hmd.vResolution }; - -#if defined(SUPPORT_DISTORTION_SHADER) - // Update distortion shader with lens and distortion-scale parameters - SetShaderValue(config.distortionShader, GetShaderLocation(config.distortionShader, "leftLensCenter"), leftLensCenter, UNIFORM_VEC2); - SetShaderValue(config.distortionShader, GetShaderLocation(config.distortionShader, "rightLensCenter"), rightLensCenter, UNIFORM_VEC2); - SetShaderValue(config.distortionShader, GetShaderLocation(config.distortionShader, "leftScreenCenter"), leftScreenCenter, UNIFORM_VEC2); - SetShaderValue(config.distortionShader, GetShaderLocation(config.distortionShader, "rightScreenCenter"), rightScreenCenter, UNIFORM_VEC2); - - SetShaderValue(config.distortionShader, GetShaderLocation(config.distortionShader, "scale"), scale, UNIFORM_VEC2); - SetShaderValue(config.distortionShader, GetShaderLocation(config.distortionShader, "scaleIn"), scaleIn, UNIFORM_VEC2); - SetShaderValue(config.distortionShader, GetShaderLocation(config.distortionShader, "hmdWarpParam"), hmd.lensDistortionValues, UNIFORM_VEC4); - SetShaderValue(config.distortionShader, GetShaderLocation(config.distortionShader, "chromaAbParam"), hmd.chromaAbCorrection, UNIFORM_VEC4); -#endif - - return config; -} - // Set internal projection and modelview matrix depending on eyes tracking data static void SetStereoView(int eye, Matrix matProjection, Matrix matModelView) { From a28dfd4a7b479b0ad8248fe0e25c5e8ce3d9d3cf Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 11 Apr 2019 15:54:10 +0200 Subject: [PATCH 57/74] Corrected standalone usage --- src/rlgl.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/rlgl.h b/src/rlgl.h index 5c0d28230..a4a481f20 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -520,14 +520,14 @@ void BeginBlendMode(int mode); // Begin blending mode (alpha, void EndBlendMode(void); // End blending mode (reset to default: alpha blending) // VR control functions -RLAPI void InitVrSimulator(void); // Init VR simulator for selected device parameters -RLAPI void CloseVrSimulator(void); // Close VR simulator for current device -RLAPI void UpdateVrTracking(Camera *camera); // Update VR tracking (position and orientation) and camera -RLAPI void SetVrConfiguration(VrDeviceInfo info, Shader distortion); // Set stereo rendering configuration parameters -RLAPI bool IsVrSimulatorReady(void); // Detect if VR simulator is ready -RLAPI void ToggleVrMode(void); // Enable/Disable VR experience -RLAPI void BeginVrDrawing(void); // Begin VR simulator stereo rendering -RLAPI void EndVrDrawing(void); // End VR simulator stereo rendering +void InitVrSimulator(void); // Init VR simulator for selected device parameters +void CloseVrSimulator(void); // Close VR simulator for current device +void UpdateVrTracking(Camera *camera); // Update VR tracking (position and orientation) and camera +void SetVrConfiguration(VrDeviceInfo info, Shader distortion); // Set stereo rendering configuration parameters +bool IsVrSimulatorReady(void); // Detect if VR simulator is ready +void ToggleVrMode(void); // Enable/Disable VR experience +void BeginVrDrawing(void); // Begin VR simulator stereo rendering +void EndVrDrawing(void); // End VR simulator stereo rendering void TraceLog(int msgType, const char *text, ...); // Show trace log messages (LOG_INFO, LOG_WARNING, LOG_ERROR, LOG_DEBUG) int GetPixelDataSize(int width, int height, int format);// Get pixel data size in bytes (image or texture) From 01367fcb1e1c9e00a74cbbe146d415fd64110d30 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 11 Apr 2019 16:11:54 +0200 Subject: [PATCH 58/74] Review cubemap generation --- src/rlgl.h | 6 +++--- src/textures.c | 6 ++---- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/rlgl.h b/src/rlgl.h index a4a481f20..048c75da3 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -2027,10 +2027,10 @@ unsigned int rlLoadTextureCubemap(void *data, int size, int format) unsigned int glInternalFormat, glFormat, glType; rlGetGlTextureFormats(format, &glInternalFormat, &glFormat, &glType); - // Load cubemap faces - for (unsigned int i = 0; i < 6; i++) + if (glInternalFormat != -1) { - if (glInternalFormat != -1) + // Load cubemap faces + for (unsigned int i = 0; i < 6; i++) { if (format < COMPRESSED_DXT1_RGB) glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glInternalFormat, size, size, 0, glFormat, glType, (unsigned char *)data + i*dataSize); #if !defined(GRAPHICS_API_OPENGL_11) diff --git a/src/textures.c b/src/textures.c index 169f8f86a..7059fabb1 100644 --- a/src/textures.c +++ b/src/textures.c @@ -1206,12 +1206,9 @@ TextureCubemap LoadTextureCubemap(Image image, int layoutType) cubemap.height = cubemap.width; } - int size = cubemap.width; - if (layoutType != CUBEMAP_AUTO_DETECT) { - //unsigned int dataSize = GetPixelDataSize(size, size, format); - //void *facesData = malloc(size*size*dataSize*6); // Get memory for 6 faces in a column + int size = cubemap.width; Image faces = { 0 }; // Vertical column image Rectangle faceRecs[6] = { 0 }; // Face source rectangles @@ -1225,6 +1222,7 @@ TextureCubemap LoadTextureCubemap(Image image, int layoutType) else if (layoutType == CUBEMAP_PANORAMA) { // TODO: Convert panorama image to square faces... + // Ref: https://github.com/denivip/panorama/blob/master/panorama.cpp } else { From 6fc97643bf564ec2f616ba3114230c78ed1c265e Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 11 Apr 2019 16:53:02 +0200 Subject: [PATCH 59/74] new example: textures_background_scrolling --- .../resources/cyberpunk_street_background.png | Bin 0 -> 7800 bytes .../resources/cyberpunk_street_foreground.png | Bin 0 -> 18100 bytes .../resources/cyberpunk_street_midground.png | Bin 0 -> 7867 bytes .../textures/textures_background_scrolling.c | 87 ++++++++++++++++++ .../textures_background_scrolling.png | Bin 0 -> 42935 bytes 5 files changed, 87 insertions(+) create mode 100644 examples/textures/resources/cyberpunk_street_background.png create mode 100644 examples/textures/resources/cyberpunk_street_foreground.png create mode 100644 examples/textures/resources/cyberpunk_street_midground.png create mode 100644 examples/textures/textures_background_scrolling.c create mode 100644 examples/textures/textures_background_scrolling.png diff --git a/examples/textures/resources/cyberpunk_street_background.png b/examples/textures/resources/cyberpunk_street_background.png new file mode 100644 index 0000000000000000000000000000000000000000..624a4b14bdd222dbdb46f5e3675d13d66cce16d2 GIT binary patch literal 7800 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 literal 0 HcmV?d00001 diff --git a/examples/textures/resources/cyberpunk_street_foreground.png b/examples/textures/resources/cyberpunk_street_foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..fb622db6881a9a83f5314b7c0a4d68ef1993319a GIT binary patch literal 18100 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≶?|=`(>~IBsGIfVM)wsXR=L8= zug8H~mf>!CWQQ}MJ7**roQ)4-9n zskb71TxOA{GQMqSekZwDdCKPv1Q5kzFkEKt`X;?brGDr{l=^?v8`j5w-O6t}=mU%E z6rcK$A=Rb^t|XEpn{j{G#5T2pIYnkid`s2f*z2SGu--$(0|E~fujN+>(kG5hrd@>- z>4onT?X}9?&>9JqNUkLw*|7n5L%z|4$fL){Mz{;PafD%vFW7mg6kx%|+B&rgrUc zp*|JNkn*OaQi+4khf0}Sl^ozM?1Mh;<(*kuM3dzi2D)S~Tsx{{qWl;WdEVg9Ty}`; zG0M zW@9*U90Z3xVlEw$j`03F|`j%^%c_v)cAa>D!?c3qvefml4Qs#^I*rPRTJt2>F+BUM1dNU%!W`CWq`h6A#|k~q_qZUJ+4 zt4)(jS|6D>M6ZFDcP4l6SoNhQNSsBRRWj61(YD*~ZQ`|ZL4^#>@0={7PCnvwvxI{Q zB$9XpCOReS_|zBXyioOa@0*(~2ZINweY^@Q;b@W6EBon%h(vNKCz;T6M25q92@?}J zdBll*=T1&r5q_b^GlSr*5dC$Pf&a7A4<`Uc1;Oaokk<{(0@tXg|JmXkR(DNx{BpYM z`rf(^2PO~U-g3{i(z*c8c;9nbMJxf;A=1p5p5a7mc4O9pk6l5Jh=W*k0guPiS6?D0 zta=(;(SH+rEbXd+bM;mS*CAZm_NBRpg{7S7L**mU96qyRKZjbsUe+o1&+%Ixwkdog zz(8^;jd%xWC{(R^l#F+=t8y_e7emE^*}b9~$2rH9JUvkp;CRRwfR#R4(TB-btZ)i`jfMEtK`W>PjxXKErlYsMc?mirvm;SVcKt;mP zeF)lkB2;G{)?VQ+5IDlmA3YVCD+9Ki(CkydxljKJLSJC8s(uc}sbKq^mK6az=dDc~ zeVE|Dq_y4o6Cq&0MBC6s?~}p^)gLqD3Jt>o_Gb(eJ~-q)y6n+G+B)#JvqOUIY!fEH zB3gDsl*i?A&>>^3=YB@@-EBc)vS7$F_`yRk1tq47xuLTSw7${T?CRiM(j94tk?8{Q zQpj416{v)y9_#2Z&s?)7inZdZCUF&bt{pEV=i z3vha$X4d3h9HR(zh(`Q9b2xwTWTrFw;MzdjIdga5{fz54Cr6IcBM$mKJ5PhIG~ii literal 0 HcmV?d00001 diff --git a/examples/textures/textures_background_scrolling.c b/examples/textures/textures_background_scrolling.c new file mode 100644 index 000000000..2be0810a4 --- /dev/null +++ b/examples/textures/textures_background_scrolling.c @@ -0,0 +1,87 @@ +/******************************************************************************************* +* +* raylib [textures] example - Background scrolling +* +* This example has been created using raylib 2.0 (www.raylib.com) +* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* +* Copyright (c) 2019 Ramon Santamaria (@raysan5) +* +********************************************************************************************/ + +#include "raylib.h" + +int main() +{ + // Initialization + //-------------------------------------------------------------------------------------- + int screenWidth = 800; + int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [textures] example - background scrolling"); + + // NOTE: Be careful, background width must be equal or bigger than screen width + // if not, texture should be draw more than two times for scrolling effect + Texture2D background = LoadTexture("resources/cyberpunk_street_background.png"); + Texture2D midground = LoadTexture("resources/cyberpunk_street_midground.png"); + Texture2D foreground = LoadTexture("resources/cyberpunk_street_foreground.png"); + + float scrollingBack = 0; + float scrollingMid = 0; + float scrollingFore = 0; + + SetTargetFPS(60); + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + scrollingBack -= 0.1f; + scrollingMid -= 0.5f; + scrollingFore -= 1.0f; + + // NOTE: Texture is scaled twice its size, so it sould be considered on scrolling + if (scrollingBack <= -background.width*2) scrollingBack = 0; + if (scrollingMid <= -midground.width*2) scrollingMid = 0; + if (scrollingFore <= -foreground.width*2) scrollingFore = 0; + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(GetColor(0x052c46ff)); + + // Draw background image twice + // NOTE: Texture is scaled twice its size + DrawTextureEx(background, (Vector2){ scrollingBack, 20 }, 0.0f, 2.0f, WHITE); + DrawTextureEx(background, (Vector2){ background.width*2 + scrollingBack, 20 }, 0.0f, 2.0f, WHITE); + + // Draw midground image twice + DrawTextureEx(midground, (Vector2){ scrollingMid, 20 }, 0.0f, 2.0f, WHITE); + DrawTextureEx(midground, (Vector2){ midground.width*2 + scrollingMid, 20 }, 0.0f, 2.0f, WHITE); + + // Draw foreground image twice + DrawTextureEx(foreground, (Vector2){ scrollingFore, 70 }, 0.0f, 2.0f, WHITE); + DrawTextureEx(foreground, (Vector2){ foreground.width*2 + scrollingFore, 70 }, 0.0f, 2.0f, WHITE); + + DrawText("BACKGROUND SCROLLING & PARALLAX", 10, 10, 20, RED); + DrawText("(c) Cyberpunk Street Environment by Luis Zuno (@ansimuz)", screenWidth - 330, screenHeight - 20, 10, RAYWHITE); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + UnloadTexture(background); // Unload background texture + UnloadTexture(midground); // Unload midground texture + UnloadTexture(foreground); // Unload foreground texture + + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} \ No newline at end of file diff --git a/examples/textures/textures_background_scrolling.png b/examples/textures/textures_background_scrolling.png new file mode 100644 index 0000000000000000000000000000000000000000..d21e269d9bc594b69fb238d5b3802f79fd5261a0 GIT binary patch literal 42935 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 literal 0 HcmV?d00001 From 129703fad18b478d015a520524d46ab4afa2cb79 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 11 Apr 2019 16:53:20 +0200 Subject: [PATCH 60/74] new example: shaders_texture_drawing --- .../shaders/glsl330/cubes_panning.fs | 61 +++++++++++++++ examples/shaders/shaders_texture_drawing.c | 71 ++++++++++++++++++ examples/shaders/shaders_texture_drawing.png | Bin 0 -> 16865 bytes 3 files changed, 132 insertions(+) create mode 100644 examples/shaders/resources/shaders/glsl330/cubes_panning.fs create mode 100644 examples/shaders/shaders_texture_drawing.c create mode 100644 examples/shaders/shaders_texture_drawing.png diff --git a/examples/shaders/resources/shaders/glsl330/cubes_panning.fs b/examples/shaders/resources/shaders/glsl330/cubes_panning.fs new file mode 100644 index 000000000..c92418a48 --- /dev/null +++ b/examples/shaders/resources/shaders/glsl330/cubes_panning.fs @@ -0,0 +1,61 @@ +#version 330 + +// Input vertex attributes (from vertex shader) +in vec2 fragTexCoord; +in vec4 fragColor; + +// Output fragment color +out vec4 finalColor; + +// Custom variables +#define PI 3.14159265358979323846 +uniform float uTime = 0.0; + +float divisions = 5.0; +float angle = 0.0; + +vec2 VectorRotateTime(vec2 v, float speed) +{ + float time = uTime*speed; + float localTime = fract(time); // The time domain this works on is 1 sec. + + if ((localTime >= 0.0) && (localTime < 0.25)) angle = 0.0; + else if ((localTime >= 0.25) && (localTime < 0.50)) angle = PI/4*sin(2*PI*localTime - PI/2); + else if ((localTime >= 0.50) && (localTime < 0.75)) angle = PI*0.25; + else if ((localTime >= 0.75) && (localTime < 1.00)) angle = PI/4*sin(2*PI*localTime); + + // Rotate vector by angle + v -= 0.5; + v = mat2(cos(angle), -sin(angle), sin(angle), cos(angle))*v; + v += 0.5; + + return v; +} + +float Rectangle(in vec2 st, in float size, in float fill) +{ + float roundSize = 0.5 - size/2.0; + float left = step(roundSize, st.x); + float top = step(roundSize, st.y); + float bottom = step(roundSize, 1.0 - st.y); + float right = step(roundSize, 1.0 - st.x); + + return (left*bottom*right*top)*fill; +} + +void main() +{ + vec2 fragPos = fragTexCoord; + fragPos.xy += uTime/9.0; + + fragPos *= divisions; + vec2 ipos = floor(fragPos); // Get the integer coords + vec2 fpos = fract(fragPos); // Get the fractional coords + + fpos = VectorRotateTime(fpos, 0.2); + + float alpha = Rectangle(fpos, 0.216, 1.0); + vec3 color = vec3(0.3, 0.3, 0.3); + + finalColor = vec4(color, alpha); +} \ No newline at end of file diff --git a/examples/shaders/shaders_texture_drawing.c b/examples/shaders/shaders_texture_drawing.c new file mode 100644 index 000000000..cb8a9c1eb --- /dev/null +++ b/examples/shaders/shaders_texture_drawing.c @@ -0,0 +1,71 @@ +/******************************************************************************************* +* +* raylib [textures] example - Shader texture drawing +* +* This example illustrates how to draw on a blank texture using a shader +* +* This example has been created using raylib 2.0 (www.raylib.com) +* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* +* Copyright (c) 2019 MichaÅ‚ Ciesielski and Ramon Santamaria (@raysan5) +* +********************************************************************************************/ + +#include "raylib.h" + +int main() +{ + // Initialization + //-------------------------------------------------------------------------------------- + int screenWidth = 800; + int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [shaders] example - shader texture drawing"); + + Image imBlank = GenImageColor(1024, 1024, BLANK); + Texture2D texture = LoadTextureFromImage(imBlank); // Load blank texture to fill on shader + UnloadImage(imBlank); + + // NOTE: Using GLSL 330 shader version, on OpenGL ES 2.0 use GLSL 100 shader version + Shader shader = LoadShader(0, "resources/shaders/glsl330/cubes_panning.fs"); + + float time = 0.0f; + int timeLoc = GetShaderLocation(shader, "uTime"); + SetShaderValue(shader, timeLoc, &time, UNIFORM_FLOAT); + + SetTargetFPS(60); + //-------------------------------------------------------------------------------------- + + while (!WindowShouldClose()) + { + // Update + //---------------------------------------------------------------------------------- + time = GetTime(); + SetShaderValue(shader, timeLoc, &time, UNIFORM_FLOAT); + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + BeginShaderMode(shader); // Enable our custom shader for next shapes/textures drawings + DrawTexture(texture, 0, 0, WHITE); // Drawing BLANK texture, all magic happens on shader + EndShaderMode(); // Disable our custom shader, return to default shader + + DrawText("BACKGROUND is PAINTED and ANIMATED on SHADER!", 10, 10, 20, MAROON); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + UnloadShader(shader); + + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} diff --git a/examples/shaders/shaders_texture_drawing.png b/examples/shaders/shaders_texture_drawing.png new file mode 100644 index 0000000000000000000000000000000000000000..12df6fae39c4154cbcda6987c999606c552cd8a1 GIT binary patch literal 16865 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 literal 0 HcmV?d00001 From 24f07aaf5a58aa1fc1d18a9eae4327f5a3f58014 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 11 Apr 2019 17:40:13 +0200 Subject: [PATCH 61/74] new example: core_window_scale_letterbox --- examples/core/core_window_scale_letterbox.c | 88 ++++++++++++++++++ examples/core/core_window_scale_letterbox.png | Bin 0 -> 17967 bytes 2 files changed, 88 insertions(+) create mode 100644 examples/core/core_window_scale_letterbox.c create mode 100644 examples/core/core_window_scale_letterbox.png diff --git a/examples/core/core_window_scale_letterbox.c b/examples/core/core_window_scale_letterbox.c new file mode 100644 index 000000000..ea4a375a2 --- /dev/null +++ b/examples/core/core_window_scale_letterbox.c @@ -0,0 +1,88 @@ +/******************************************************************************************* +* +* raylib [core] example - window scale letterbox +* +* This example has been created using raylib 2.5 (www.raylib.com) +* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* +* Copyright (c) 2019 Anata and Ramon Santamaria (@raysan5) +* +********************************************************************************************/ + +#include "raylib.h" + +#define max(a, b) ((a)>(b)? (a) : (b)) +#define min(a, b) ((a)<(b)? (a) : (b)) + +int main() +{ + const int windowWidth = 800; + const int windowHeight = 450; + + // Enable config flags for resizable window and vertical synchro + SetConfigFlags(FLAG_WINDOW_RESIZABLE | FLAG_VSYNC_HINT); + InitWindow(windowWidth, windowHeight, "raylib [core] example - window scale letterbox"); + SetWindowMinSize(320, 240); + + int gameScreenWidth = 640; + int gameScreenHeight = 480; + + // Render texture initialization + RenderTexture2D target = LoadRenderTexture(gameScreenWidth, gameScreenHeight); + SetTextureFilter(target.texture, FILTER_BILINEAR); // Texture scale filter to use + + Color colors[10] = { 0 }; + for (int i = 0; i < 10; i++) colors[i] = (Color){ GetRandomValue(100, 250), GetRandomValue(50, 150), GetRandomValue(10, 100), 255 }; + + SetTargetFPS(60); + //-------------------------------------------------------------------------------------- + + // Main game loop + while( !WindowShouldClose() ) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + // Compute required framebuffer scaling + float scale = min((float)GetScreenWidth()/gameScreenWidth, (float)GetScreenHeight()/gameScreenHeight); + + if (IsKeyPressed(KEY_SPACE)) + { + // Recalculate random colors for the bars + for (int i = 0; i < 10; i++) colors[i] = (Color){ GetRandomValue(100, 250), GetRandomValue(50, 150), GetRandomValue(10, 100), 255 }; + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(BLACK); + + // Draw everything in the render texture + BeginTextureMode(target); + + ClearBackground(RAYWHITE); // Clear render texture background color + + for (int i = 0; i < 10; i++) DrawRectangle(0, (gameScreenHeight/10)*i, gameScreenWidth, gameScreenHeight/10, colors[i]); + + DrawText("You can resize the window,\nand see the screen scaling!", 10, 25, 20, WHITE); + + EndTextureMode(); + + // Draw RenderTexture2D to window, properly scaled + DrawTexturePro(target.texture, (Rectangle){ 0.0f, 0.0f, (float)target.texture.width, (float)-target.texture.height }, + (Rectangle){ (GetScreenWidth() - ((float)gameScreenWidth*scale))*0.5, (GetScreenHeight() - ((float)gameScreenHeight*scale))*0.5, + (float)gameScreenWidth*scale, (float)gameScreenHeight*scale }, (Vector2){ 0, 0 }, 0.0f, WHITE); + + EndDrawing(); + //-------------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + UnloadRenderTexture(target); // Unload render texture + + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} diff --git a/examples/core/core_window_scale_letterbox.png b/examples/core/core_window_scale_letterbox.png new file mode 100644 index 0000000000000000000000000000000000000000..5acf2d7cc28aa8c2c465f71cf748b755fd8b042e GIT binary patch literal 17967 zcmeHPXIPWjwoV8n0YVX^1StU&P?2I7Iv832MFz2eh;%U^T@=Ma2}H#p0g<90Q5*pW zHIyS|6cQqcbQBaNB7vx=NE0a|<%EDEVeopMduHa&xhFsPp5%KzzP})Ls_@wv%0DyqCmAO3t073u&z$z%1c}K$YW-S0d0^S>+=>JMxh^n!#w5#Lrf>b39UPfmXATUJgP)klIdrqJKr*a6(p(OLE$$W zPs(fPGdp?1@>^Kw6X(ii`uV>0w@hH+1oX`yQPsK8`%&oapO%jS9tU_~%JOr^H~l;p zB%g0W)omh{+y|@0+>rep#ul7H+ylik{Vdw~EfZKl66h8LV*BjCSu@&T`87u~;!q)o z9}xe@cGuG~MKx04(ziEW$AR9t{d_l+WS~1S8B`8ppzVI;DmzNTsnt#+bfTxs{B=am zLo7eOU~dR8CM=l=gh#J6Gv6C)LTQSN$`lF%tym4Q2iY53oH&JsLTevJ_g=8H2}Q}M z=G_Jzh9Fn1w+)M=K{vZ1bcCL?@RZ3h(1eKG4``2(ZcN3JF-Ml=Qq2Y;Spm{D?H+k2 z01F$FcANP1*G5dToji9%twB%*i-F=P>iE6}^oJL`d>;(FgF(cUL zi@#`&a@!1g5?Y>mJHmE+7R!ZXU14-=QQ7P^<}KZ6tuclgfJ}n2Ow%HK+|^a{htdW3 z_d7aLXg23uj*pRhQae`5mH6mUXJ=ix{|v&IU!y2OM4j zCy{!qXJ^jqw^iLc_S6M=4~QUK@K>}2F+vh8#$LxI^^(0COD4<9mAhe`wbHXt`5O?pxyd(+%}MAI&tY&P*pK656W zuCAeqdX23DUsfiLRgG}j3|4N6fX_@%W78p4LCA9Nw0jXJJ=Dgz%7prDSL@zEGmz|~8JlQMxJfumM=wsDgDDlj`rB4Sl z3QskQeq-xR&}y+r1UHf!#-WrJkCYU$TgqirQdYn~Mo z0LDMhKF}>L=50kQ5I5XT($4y7 zqkJ=sl*)>5vmk#1eux1mE15wUk>Q6xS{g-kU%-;&RAoIp+<~&#{7AaAtvoQ_b|oQo zpWB;P)$7JT)=PlxGDJo$5t6uu7)X~OT15Dm^v@vy-MmhSLs-ooli^d1{{4oAu#N=D#u0#Fgvooj5vQ3}RVxrPB9jBP)l;%R% zi_r879-f&|#91oaQsb4^Wbnqu?~2r+-d1;@xjLpwVnqWyhuO!xy^8 zK#yLhyy0P_dV|Z}N{dj4c~>1C7~?)bep+y4wSaI4vi#Y^)TvwIFB&ICqo?@v+XYf? zp%1)Zqro~B4MrD)9q5$BJtfyjY1%^K3D`Cx!^3g(H*cDc{3Fs^0mWx*bfPJ!bpJ+^ z-d4*81wO=|?jdxlX!rn~hOX470V1Q2hDs7i;aXH0DeBp=;7O-wB)Biw{EW--fCpl` z`of3G;94Lqd!Z0T6=@geigWFC=aH{L`xSM`A;eHqo3yB&D7y}7|2_m4x&NnAmlCJX z*84Eur!-qdgT2FJ^)C=lm=iTtD;X_iafUpgd~N?MMpfXlaGIajCMlVPVFM5ZcE~8? zc&m}aU)EtXzDhJ<39hm?g6F{P2tJnjV(2bpOsp37{%!HV_^xn`WNPPfh!`4qcP+MxDTg(<(SfFNTfqx3MNx%I z579-a4HuPg4hdQ3a39&9pVzJn(;I-lSvU2)4h&%^)wCo#5Ee8jMQ6=rlc$fQZ`Wth zD(XqO*{LQ^>oO=hdY6gnok>NKN4O{8BjKIFH^hWjDCrk7S+wC%HEPxn{3}0Gg6z?!;y8*kLXb{@>3wD&yXFB~6yS?Sn#_xRLm>T5lIxZm_W=HK1n=bv9u zEpgV>7r0-DJ8WwJgsd`-Wpu3P9}QG(@iP?!l&xUwVvs05?f*!Ng{3BBdJ!*N;w?Kw ziV@QeV+t45y{+qJH!e|T3iFg?E!MVzu!BH-KQicA5&255tPBp#i-q5BXs{`EGNqvT zkL`Z;jPfMz;b!EJXve84YV(hw4TZo)3qeKaaFel~jibXK4BXZdTukZH%uiQoN~&vZ zHH>QuQpFl8OQk`Zg%ElkkAzezOniM_d7A>Y9jo{*8Mq$>rrg3@h=8Lhiz5L`oWm20 za;-dnzX&yMX07AiCiT_#?JB(lakQqN)-zN7-mC7z(%{jpuf}h?>`!xcig!ov2$sd3 zDFRu`$i=(}s`wAS^c%VzFT>j&^w$?0w27 zTMyl$aRBbf%R7f)*SP$~sv0Jk(mgJ?w4Z1FuL-`$+7pORrL>Q4o(7h-DZAz5$!qhN zxfjNlLA`NrqsgLykN> z#95J{@!*aAXYw>q%35h3E;8d8cQ}s}MjY3cF6kUV)z0SGe$02=7*{QGLTg+clY&nk zs(It>q@VKKo#!HbT}H#725g?VA*0C_6evJzA&IXiv@RA)kLqdX|up;23$7Z{6$d`7#q-~qmor_VjW_}hcti6Q`CA18p4STW8@txvo zo*fk!__ncL=FOh11FASrD*y=Uc@Ti{CMv0(BX&-07lm8N?9IX5>yQS&gVOl}M@eyO z)$hOE+rJ1Me&*abE03Wr5mJjF4t!ebJJk)JiHjn z(z!McI!zgnChp^u_ai0Eu92@?jl&y)++HZ*n1t)GGvaus8dxwujYMmf+eG%|<$0*A zFi;TrY>UlMlCOH!+T=mv!~=QSn%iYW%GwekLOXfN0_`GYK_K-MmrK%7d+a3(5v~)f8|i zm2Xn3td;mx`;cK#Yd_yP2d-a6YBAb(!fl+TgUio}>L1gSiek%W$@4`EmoH^20xpu7 zQL$!(mzb%=fhUug8je|}jQ&y_GLhmcQFHc4+x=KeY7^Tl=gOUn%2>rhz?Q^nW+HvU z_Eg4Pz&Y_b=8t*~SN(gsEg{ z#I5biS;4yZ-J(Co1Wq8nV3LCc4i^5)u&}`8Lt{f9TUFr8^R)Xa*23qRW6Ow#c?I#^ z?EPIDo?8?AbJt!uCd@62bG*Ru!WSXW!NUI^EU0ED>X({E4mV}}k2Jo8gccoNte_W5 z>%YOi2961H!Qps;bWTHh=znVXo%M*{jLftN91*)m$vJ*hU0rFtExB|M?r2nR`zF^%zH?Tf!O+ z#+u7l#+fwkXU5v(9n4uQ`S-TEe}ARoPiJtB3IE!X5hoC=Gbw+zpy0sq4}xRlJ}rBQ z0~DGiAsL_aWoO=!+V~yjensvBTK@)(|M_QizPsy^W5QpfJvf1wJCDQh0>=wqrapfK z78cYvX2>4CER6$wab(Iz*5Oi?B-dMVYV7Nj?XR$wRWsq7yZlc$cli|qe2Mh-0hOGIDIFLHtjL!CAq3d^-Kj2i*-+ Date: Thu, 11 Apr 2019 18:07:20 +0200 Subject: [PATCH 62/74] new example: shapes_rectangle_scaling_mouse --- .../shapes/shapes_rectangle_scaling_mouse.c | 92 ++++++++++++++++++ .../shapes/shapes_rectangle_scaling_mouse.png | Bin 0 -> 15191 bytes 2 files changed, 92 insertions(+) create mode 100644 examples/shapes/shapes_rectangle_scaling_mouse.c create mode 100644 examples/shapes/shapes_rectangle_scaling_mouse.png diff --git a/examples/shapes/shapes_rectangle_scaling_mouse.c b/examples/shapes/shapes_rectangle_scaling_mouse.c new file mode 100644 index 000000000..d8c521cab --- /dev/null +++ b/examples/shapes/shapes_rectangle_scaling_mouse.c @@ -0,0 +1,92 @@ +/******************************************************************************************* +* +* raylib [shapes] example - rectangle scaling by mouse +* +* This example has been created using raylib 2.5 (www.raylib.com) +* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* +* Copyright (c) 2019 Demioz and Ramon Santamaria (@raysan5) +* +********************************************************************************************/ + +#include "raylib.h" + +#define MOUSE_SCALE_MARK_SIZE 12 + +int main() +{ + // Initialization + //-------------------------------------------------------------------------------------- + int screenWidth = 800; + int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [shapes] example - rectangle scaling mouse"); + + Rectangle rec = { 100, 100, 200, 80 }; + + Vector2 mousePosition = { 0 }; + + bool mouseScaleReady = false; + bool mouseScaleMode = false; + + SetTargetFPS(60); + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + mousePosition = GetMousePosition(); + + if (CheckCollisionPointRec(mousePosition, rec) && + CheckCollisionPointRec(mousePosition, (Rectangle){ rec.x + rec.width - MOUSE_SCALE_MARK_SIZE, rec.y + rec.height - MOUSE_SCALE_MARK_SIZE, MOUSE_SCALE_MARK_SIZE, MOUSE_SCALE_MARK_SIZE })) + { + mouseScaleReady = true; + if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) mouseScaleMode = true; + } + else mouseScaleReady = false; + + if (mouseScaleMode) + { + mouseScaleReady = true; + + rec.width = (mousePosition.x - rec.x); + rec.height = (mousePosition.y - rec.y); + + if (rec.width < MOUSE_SCALE_MARK_SIZE) rec.width = MOUSE_SCALE_MARK_SIZE; + if (rec.height < MOUSE_SCALE_MARK_SIZE) rec.height = MOUSE_SCALE_MARK_SIZE; + + if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) mouseScaleMode = false; + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + DrawText("Scale rectangle dragging from bottom-right corner!", 10, 10, 20, GRAY); + + DrawRectangleRec(rec, Fade(GREEN, 0.5f)); + + if (mouseScaleReady) + { + DrawRectangleLinesEx(rec, 1, RED); + DrawTriangle((Vector2){ rec.x + rec.width - MOUSE_SCALE_MARK_SIZE, rec.y + rec.height }, + (Vector2){ rec.x + rec.width, rec.y + rec.height }, + (Vector2){ rec.x + rec.width, rec.y + rec.height - MOUSE_SCALE_MARK_SIZE }, RED); + } + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} \ No newline at end of file diff --git a/examples/shapes/shapes_rectangle_scaling_mouse.png b/examples/shapes/shapes_rectangle_scaling_mouse.png new file mode 100644 index 0000000000000000000000000000000000000000..83d67de9bf617093d7c927096efc71f1ba588125 GIT binary patch literal 15191 zcmeAS@N?(olHy`uVBq!ia0y~yU{+vYU_8XZ1{4ubUVDOp!CKDK#WAEJF8R-|udnOd z0u7{?7AxW)N;nvoT?~-pZM-NTft6saYCVv_o?^1Lx9va%M-LXlpr_2iP52;oNuY)e z+>I9{CtP%~#pMi#?E(^S8+z2X;dH|;h#NNIbOXo6St&>^2;#c*C z9o0LQ&Yn1n|HWMSOxbfsX4&6)JUi(B#8ves|9iIWf*RSdPtm{%XvHJERxG(?aw$#9 z?ee1x4xUy4iJYI4F7&+2ur!*r{sS;9e@Xbt!;-`v=Y$fr6qD9|EKv)N?hCps5gEC{ z8>`(Dl;f7HI{vgSYbLYt{A>RduJ3*E>w-HU`|8~nALlN3l)d4Y{?3nXd3KA+ejGkD z^@7K@0MGeLmW6wrHg@_c6~1d}`9Jk-{}z;2shPzKbLj`AC$U9W$*ua?ohSmfi08L} z#9T&6-zPAqVI-ImZpKOJ4ku(aRm)U5qm-9z5IB-5xAC7tKWC8p5s$6&qn-(WviFe- zw+J|WcS)P^-bdXb^^<4uv3c*{3$d9L)pSui{@|n5E0SDEkLO?e=$BXNl4qqL64&y{ zeAnKiBJ=t@UVn-4S$bKz-OJYT{THiS+oU0Rf-!=l=UhY2vQ|7Pl;NVW!jZlg6RaNf zz6_`|>RmF(f0Y9wSkr|V^j{e=u!r}+XBz2_g!yySLz`$eG? zP%3G-28q{Wc;mI%bd6{1>+aL?%(hPE5pEnSMdxkt(|68wDP7U>+TrQvi-Mmn-pVmy zX^Z2ypmo1*8vEj%i6GM%#F!QvD=f|gbFd}NhCD@wFFYJ;zn3lO@0pM#?_<02*@Y^X zwh8tcrTQC}&$v+K`#zy}{lQoHNyQd>uP5!lz4aw;7&E3G14KWIF4 ze#tfAD|+8w%E~X|2lym8IMe)R56=yNzM)b z&+NN+L;jhf!7WC~7D1b@9%nL+VYZS4#hGrJdAlXova}p{A++XVirpY?#elqmr@?wy zAJSR`wl2Zl7YqW_S_E|lZ@`+X7;QN>YhaTcTWcI5_5;$qU4^rI4r*V^GHt9*Tqe9V@Fa{SN6N1N$A| zaL&Q$1W;?&1jq0NP}3h^lN{K84aL`g1%-2GE-oh!=&`rOLpp!2@O1vb;XHuD6SQx} zjb;WZrqRs6GMX8{1>FgKVQU188PpG&785hSAIbonafz45OK0G&78523Uu6 zw9hcwOoF6^(VAhjW*Ds*Mr(%AngPBxYP17AI+_Gon>gA`8f_+xfM(JhffL&zjil}> ztAOVYC;!4Wan}G`iTOM^MfCt^=B?2f+YD&}aMbOmL}h0_aDFlB0k-))9uMHupq*RV zA!*#*62UpYSaK&9E`0W0@XnSO>P}CTAW_dw{D$-K^z-vuik)y}l4) zT0FP^VhbA=c$Ufv+uFAUm0;HjOX>hk!?8@UVHwylz6mE34;V?wGy|t!jbCQ0l7TH) zK%RJ9r+DDU*VotoFTQAvE%=~fUpN{sszxk+30yCUeTsGvOiqqE0Tel-p)eW>usk}N z7Dm&;Xj(w7utsylXpR`o5u-T*KCS`{iqUdmv|Jc17tqQD1_rgDjgCuJgv9|j%rbbo L`njxgN@xNArAi7) literal 0 HcmV?d00001 From 8774cb3c242373a2eb9f62af88ff9b51b6a17d9a Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 11 Apr 2019 23:38:13 +0200 Subject: [PATCH 63/74] Removed iqm example Already integrated into raylib --- .../iqm_loader/models_iqm_animation.gif | Bin 593783 -> 0 bytes examples/others/iqm_loader/raymath.h | 1334 ----------------- examples/others/iqm_loader/riqm.h | 98 -- 3 files changed, 1432 deletions(-) delete mode 100644 examples/others/iqm_loader/models_iqm_animation.gif delete mode 100644 examples/others/iqm_loader/raymath.h delete mode 100644 examples/others/iqm_loader/riqm.h diff --git a/examples/others/iqm_loader/models_iqm_animation.gif b/examples/others/iqm_loader/models_iqm_animation.gif deleted file mode 100644 index 3a51ad8543da590cde9af570fb80ae8cff28d59a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 593783 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 z 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�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 // Required for: sinf(), cosf(), tan(), fabs() - -//---------------------------------------------------------------------------------- -// Module Functions Definition - Utils math -//---------------------------------------------------------------------------------- - -// Clamp float value -RMDEF float Clamp(float value, float min, float max) -{ - const float res = value < min ? min : value; - return res > max ? max : res; -} - -//---------------------------------------------------------------------------------- -// Module Functions Definition - Vector2 math -//---------------------------------------------------------------------------------- - -// Vector with components value 0.0f -RMDEF Vector2 Vector2Zero(void) -{ - Vector2 result = { 0.0f, 0.0f }; - return result; -} - -// Vector with components value 1.0f -RMDEF Vector2 Vector2One(void) -{ - Vector2 result = { 1.0f, 1.0f }; - return result; -} - -// Add two vectors (v1 + v2) -RMDEF Vector2 Vector2Add(Vector2 v1, Vector2 v2) -{ - Vector2 result = { v1.x + v2.x, v1.y + v2.y }; - return result; -} - -// Subtract two vectors (v1 - v2) -RMDEF Vector2 Vector2Subtract(Vector2 v1, Vector2 v2) -{ - Vector2 result = { v1.x - v2.x, v1.y - v2.y }; - return result; -} - -// Calculate vector length -RMDEF float Vector2Length(Vector2 v) -{ - float result = sqrtf((v.x*v.x) + (v.y*v.y)); - return result; -} - -// Calculate two vectors dot product -RMDEF float Vector2DotProduct(Vector2 v1, Vector2 v2) -{ - float result = (v1.x*v2.x + v1.y*v2.y); - return result; -} - -// Calculate distance between two vectors -RMDEF float Vector2Distance(Vector2 v1, Vector2 v2) -{ - float result = sqrtf((v1.x - v2.x)*(v1.x - v2.x) + (v1.y - v2.y)*(v1.y - v2.y)); - return result; -} - -// Calculate angle from two vectors in X-axis -RMDEF float Vector2Angle(Vector2 v1, Vector2 v2) -{ - float result = atan2f(v2.y - v1.y, v2.x - v1.x)*(180.0f/PI); - if (result < 0) result += 360.0f; - return result; -} - -// Scale vector (multiply by value) -RMDEF Vector2 Vector2Scale(Vector2 v, float scale) -{ - Vector2 result = { v.x*scale, v.y*scale }; - return result; -} - -// Negate vector -RMDEF Vector2 Vector2Negate(Vector2 v) -{ - Vector2 result = { -v.x, -v.y }; - return result; -} - -// Divide vector by a float value -RMDEF Vector2 Vector2Divide(Vector2 v, float div) -{ - Vector2 result = { v.x/div, v.y/div }; - return result; -} - -// Normalize provided vector -RMDEF Vector2 Vector2Normalize(Vector2 v) -{ - Vector2 result = Vector2Divide(v, Vector2Length(v)); - return result; -} - -//---------------------------------------------------------------------------------- -// Module Functions Definition - Vector3 math -//---------------------------------------------------------------------------------- - -// Vector with components value 0.0f -RMDEF Vector3 Vector3Zero(void) -{ - Vector3 result = { 0.0f, 0.0f, 0.0f }; - return result; -} - -// Vector with components value 1.0f -RMDEF Vector3 Vector3One(void) -{ - Vector3 result = { 1.0f, 1.0f, 1.0f }; - return result; -} - -// Add two vectors -RMDEF Vector3 Vector3Add(Vector3 v1, Vector3 v2) -{ - Vector3 result = { v1.x + v2.x, v1.y + v2.y, v1.z + v2.z }; - return result; -} - -// Substract two vectors -RMDEF Vector3 Vector3Subtract(Vector3 v1, Vector3 v2) -{ - Vector3 result = { v1.x - v2.x, v1.y - v2.y, v1.z - v2.z }; - return result; -} - -// Multiply vector by scalar -RMDEF Vector3 Vector3Multiply(Vector3 v, float scalar) -{ - Vector3 result = { v.x*scalar, v.y*scalar, v.z*scalar }; - return result; -} - -// Multiply vector by vector -RMDEF Vector3 Vector3MultiplyV(Vector3 v1, Vector3 v2) -{ - Vector3 result = { v1.x*v2.x, v1.y*v2.y, v1.z*v2.z }; - return result; -} - -// Calculate two vectors cross product -RMDEF Vector3 Vector3CrossProduct(Vector3 v1, Vector3 v2) -{ - Vector3 result = { v1.y*v2.z - v1.z*v2.y, v1.z*v2.x - v1.x*v2.z, v1.x*v2.y - v1.y*v2.x }; - return result; -} - -// Calculate one vector perpendicular vector -RMDEF Vector3 Vector3Perpendicular(Vector3 v) -{ - Vector3 result = { 0 }; - - float min = fabs(v.x); - Vector3 cardinalAxis = {1.0f, 0.0f, 0.0f}; - - if (fabs(v.y) < min) - { - min = fabs(v.y); - Vector3 tmp = {0.0f, 1.0f, 0.0f}; - cardinalAxis = tmp; - } - - if (fabs(v.z) < min) - { - Vector3 tmp = {0.0f, 0.0f, 1.0f}; - cardinalAxis = tmp; - } - - result = Vector3CrossProduct(v, cardinalAxis); - - return result; -} - -// Calculate vector length -RMDEF float Vector3Length(const Vector3 v) -{ - float result = sqrtf(v.x*v.x + v.y*v.y + v.z*v.z); - return result; -} - -// Calculate two vectors dot product -RMDEF float Vector3DotProduct(Vector3 v1, Vector3 v2) -{ - float result = (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z); - return result; -} - -// Calculate distance between two vectors -RMDEF float Vector3Distance(Vector3 v1, Vector3 v2) -{ - float dx = v2.x - v1.x; - float dy = v2.y - v1.y; - float dz = v2.z - v1.z; - float result = sqrtf(dx*dx + dy*dy + dz*dz); - return result; -} - -// Scale provided vector -RMDEF Vector3 Vector3Scale(Vector3 v, float scale) -{ - Vector3 result = { v.x*scale, v.y*scale, v.z*scale }; - return result; -} - -// Negate provided vector (invert direction) -RMDEF Vector3 Vector3Negate(Vector3 v) -{ - Vector3 result = { -v.x, -v.y, -v.z }; - return result; -} - -// Normalize provided vector -RMDEF Vector3 Vector3Normalize(Vector3 v) -{ - Vector3 result = v; - - float length, ilength; - length = Vector3Length(v); - if (length == 0.0f) length = 1.0f; - ilength = 1.0f/length; - - result.x *= ilength; - result.y *= ilength; - result.z *= ilength; - - return result; -} - -// Orthonormalize provided vectors -// Makes vectors normalized and orthogonal to each other -// Gram-Schmidt function implementation -RMDEF void Vector3OrthoNormalize(Vector3 *v1, Vector3 *v2) -{ - *v1 = Vector3Normalize(*v1); - Vector3 vn = Vector3CrossProduct(*v1, *v2); - vn = Vector3Normalize(vn); - *v2 = Vector3CrossProduct(vn, *v1); -} - -// Transforms a Vector3 by a given Matrix -RMDEF Vector3 Vector3Transform(Vector3 v, Matrix mat) -{ - Vector3 result = { 0 }; - float x = v.x; - float y = v.y; - float z = v.z; - - result.x = mat.m0*x + mat.m4*y + mat.m8*z + mat.m12; - result.y = mat.m1*x + mat.m5*y + mat.m9*z + mat.m13; - result.z = mat.m2*x + mat.m6*y + mat.m10*z + mat.m14; - - return result; -}; - -// Transform a vector by quaternion rotation -RMDEF Vector3 Vector3RotateByQuaternion(Vector3 v, Quaternion q) -{ - Vector3 result = { 0 }; - - result.x = v.x*(q.x*q.x + q.w*q.w - q.y*q.y - q.z*q.z) + v.y*(2*q.x*q.y - 2*q.w*q.z) + v.z*(2*q.x*q.z + 2*q.w*q.y); - result.y = v.x*(2*q.w*q.z + 2*q.x*q.y) + v.y*(q.w*q.w - q.x*q.x + q.y*q.y - q.z*q.z) + v.z*(-2*q.w*q.x + 2*q.y*q.z); - result.z = v.x*(-2*q.w*q.y + 2*q.x*q.z) + v.y*(2*q.w*q.x + 2*q.y*q.z)+ v.z*(q.w*q.w - q.x*q.x - q.y*q.y + q.z*q.z); - - return result; -} - -// Calculate linear interpolation between two vectors -RMDEF Vector3 Vector3Lerp(Vector3 v1, Vector3 v2, float amount) -{ - Vector3 result = { 0 }; - - result.x = v1.x + amount*(v2.x - v1.x); - result.y = v1.y + amount*(v2.y - v1.y); - result.z = v1.z + amount*(v2.z - v1.z); - - return result; -} - -// Calculate reflected vector to normal -RMDEF Vector3 Vector3Reflect(Vector3 v, Vector3 normal) -{ - // I is the original vector - // N is the normal of the incident plane - // R = I - (2*N*( DotProduct[ I,N] )) - - Vector3 result = { 0 }; - - float dotProduct = Vector3DotProduct(v, normal); - - result.x = v.x - (2.0f*normal.x)*dotProduct; - result.y = v.y - (2.0f*normal.y)*dotProduct; - result.z = v.z - (2.0f*normal.z)*dotProduct; - - return result; -} - -// Return min value for each pair of components -RMDEF Vector3 Vector3Min(Vector3 v1, Vector3 v2) -{ - Vector3 result = { 0 }; - - result.x = fminf(v1.x, v2.x); - result.y = fminf(v1.y, v2.y); - result.z = fminf(v1.z, v2.z); - - return result; -} - -// Return max value for each pair of components -RMDEF Vector3 Vector3Max(Vector3 v1, Vector3 v2) -{ - Vector3 result = { 0 }; - - result.x = fmaxf(v1.x, v2.x); - result.y = fmaxf(v1.y, v2.y); - result.z = fmaxf(v1.z, v2.z); - - return result; -} - -// Compute barycenter coordinates (u, v, w) for point p with respect to triangle (a, b, c) -// NOTE: Assumes P is on the plane of the triangle -RMDEF Vector3 Vector3Barycenter(Vector3 p, Vector3 a, Vector3 b, Vector3 c) -{ - //Vector v0 = b - a, v1 = c - a, v2 = p - a; - - Vector3 v0 = Vector3Subtract(b, a); - Vector3 v1 = Vector3Subtract(c, a); - Vector3 v2 = Vector3Subtract(p, a); - float d00 = Vector3DotProduct(v0, v0); - float d01 = Vector3DotProduct(v0, v1); - float d11 = Vector3DotProduct(v1, v1); - float d20 = Vector3DotProduct(v2, v0); - float d21 = Vector3DotProduct(v2, v1); - - float denom = d00*d11 - d01*d01; - - Vector3 result = { 0 }; - - result.y = (d11*d20 - d01*d21)/denom; - result.z = (d00*d21 - d01*d20)/denom; - result.x = 1.0f - (result.z + result.y); - - return result; -} - -// Returns Vector3 as float array -RMDEF float3 Vector3ToFloatV(Vector3 v) -{ - float3 buffer = { 0 }; - - buffer.v[0] = v.x; - buffer.v[1] = v.y; - buffer.v[2] = v.z; - - return buffer; -} - -//---------------------------------------------------------------------------------- -// Module Functions Definition - Matrix math -//---------------------------------------------------------------------------------- - -// Compute matrix determinant -RMDEF float MatrixDeterminant(Matrix mat) -{ - float result = { 0 }; - - // Cache the matrix values (speed optimization) - float a00 = mat.m0, a01 = mat.m1, a02 = mat.m2, a03 = mat.m3; - float a10 = mat.m4, a11 = mat.m5, a12 = mat.m6, a13 = mat.m7; - float a20 = mat.m8, a21 = mat.m9, a22 = mat.m10, a23 = mat.m11; - float a30 = mat.m12, a31 = mat.m13, a32 = mat.m14, a33 = mat.m15; - - result = a30*a21*a12*a03 - a20*a31*a12*a03 - a30*a11*a22*a03 + a10*a31*a22*a03 + - a20*a11*a32*a03 - a10*a21*a32*a03 - a30*a21*a02*a13 + a20*a31*a02*a13 + - a30*a01*a22*a13 - a00*a31*a22*a13 - a20*a01*a32*a13 + a00*a21*a32*a13 + - a30*a11*a02*a23 - a10*a31*a02*a23 - a30*a01*a12*a23 + a00*a31*a12*a23 + - a10*a01*a32*a23 - a00*a11*a32*a23 - a20*a11*a02*a33 + a10*a21*a02*a33 + - a20*a01*a12*a33 - a00*a21*a12*a33 - a10*a01*a22*a33 + a00*a11*a22*a33; - - return result; -} - -// Returns the trace of the matrix (sum of the values along the diagonal) -RMDEF float MatrixTrace(Matrix mat) -{ - float result = (mat.m0 + mat.m5 + mat.m10 + mat.m15); - return result; -} - -// Transposes provided matrix -RMDEF Matrix MatrixTranspose(Matrix mat) -{ - Matrix result = { 0 }; - - result.m0 = mat.m0; - result.m1 = mat.m4; - result.m2 = mat.m8; - result.m3 = mat.m12; - result.m4 = mat.m1; - result.m5 = mat.m5; - result.m6 = mat.m9; - result.m7 = mat.m13; - result.m8 = mat.m2; - result.m9 = mat.m6; - result.m10 = mat.m10; - result.m11 = mat.m14; - result.m12 = mat.m3; - result.m13 = mat.m7; - result.m14 = mat.m11; - result.m15 = mat.m15; - - return result; -} - -// Invert provided matrix -RMDEF Matrix MatrixInvert(Matrix mat) -{ - Matrix result = { 0 }; - - // Cache the matrix values (speed optimization) - float a00 = mat.m0, a01 = mat.m1, a02 = mat.m2, a03 = mat.m3; - float a10 = mat.m4, a11 = mat.m5, a12 = mat.m6, a13 = mat.m7; - float a20 = mat.m8, a21 = mat.m9, a22 = mat.m10, a23 = mat.m11; - float a30 = mat.m12, a31 = mat.m13, a32 = mat.m14, a33 = mat.m15; - - float b00 = a00*a11 - a01*a10; - float b01 = a00*a12 - a02*a10; - float b02 = a00*a13 - a03*a10; - float b03 = a01*a12 - a02*a11; - float b04 = a01*a13 - a03*a11; - float b05 = a02*a13 - a03*a12; - float b06 = a20*a31 - a21*a30; - float b07 = a20*a32 - a22*a30; - float b08 = a20*a33 - a23*a30; - float b09 = a21*a32 - a22*a31; - float b10 = a21*a33 - a23*a31; - float b11 = a22*a33 - a23*a32; - - // Calculate the invert determinant (inlined to avoid double-caching) - float invDet = 1.0f/(b00*b11 - b01*b10 + b02*b09 + b03*b08 - b04*b07 + b05*b06); - - result.m0 = (a11*b11 - a12*b10 + a13*b09)*invDet; - result.m1 = (-a01*b11 + a02*b10 - a03*b09)*invDet; - result.m2 = (a31*b05 - a32*b04 + a33*b03)*invDet; - result.m3 = (-a21*b05 + a22*b04 - a23*b03)*invDet; - result.m4 = (-a10*b11 + a12*b08 - a13*b07)*invDet; - result.m5 = (a00*b11 - a02*b08 + a03*b07)*invDet; - result.m6 = (-a30*b05 + a32*b02 - a33*b01)*invDet; - result.m7 = (a20*b05 - a22*b02 + a23*b01)*invDet; - result.m8 = (a10*b10 - a11*b08 + a13*b06)*invDet; - result.m9 = (-a00*b10 + a01*b08 - a03*b06)*invDet; - result.m10 = (a30*b04 - a31*b02 + a33*b00)*invDet; - result.m11 = (-a20*b04 + a21*b02 - a23*b00)*invDet; - result.m12 = (-a10*b09 + a11*b07 - a12*b06)*invDet; - result.m13 = (a00*b09 - a01*b07 + a02*b06)*invDet; - result.m14 = (-a30*b03 + a31*b01 - a32*b00)*invDet; - result.m15 = (a20*b03 - a21*b01 + a22*b00)*invDet; - - return result; -} - -// Normalize provided matrix -RMDEF Matrix MatrixNormalize(Matrix mat) -{ - Matrix result = { 0 }; - - float det = MatrixDeterminant(mat); - - result.m0 = mat.m0/det; - result.m1 = mat.m1/det; - result.m2 = mat.m2/det; - result.m3 = mat.m3/det; - result.m4 = mat.m4/det; - result.m5 = mat.m5/det; - result.m6 = mat.m6/det; - result.m7 = mat.m7/det; - result.m8 = mat.m8/det; - result.m9 = mat.m9/det; - result.m10 = mat.m10/det; - result.m11 = mat.m11/det; - result.m12 = mat.m12/det; - result.m13 = mat.m13/det; - result.m14 = mat.m14/det; - result.m15 = mat.m15/det; - - return result; -} - -// Returns identity matrix -RMDEF Matrix MatrixIdentity(void) -{ - Matrix result = { 1.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 1.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 1.0f, 0.0f, - 0.0f, 0.0f, 0.0f, 1.0f }; - - return result; -} - -// Add two matrices -RMDEF Matrix MatrixAdd(Matrix left, Matrix right) -{ - Matrix result = MatrixIdentity(); - - result.m0 = left.m0 + right.m0; - result.m1 = left.m1 + right.m1; - result.m2 = left.m2 + right.m2; - result.m3 = left.m3 + right.m3; - result.m4 = left.m4 + right.m4; - result.m5 = left.m5 + right.m5; - result.m6 = left.m6 + right.m6; - result.m7 = left.m7 + right.m7; - result.m8 = left.m8 + right.m8; - result.m9 = left.m9 + right.m9; - result.m10 = left.m10 + right.m10; - result.m11 = left.m11 + right.m11; - result.m12 = left.m12 + right.m12; - result.m13 = left.m13 + right.m13; - result.m14 = left.m14 + right.m14; - result.m15 = left.m15 + right.m15; - - return result; -} - -// Substract two matrices (left - right) -RMDEF Matrix MatrixSubstract(Matrix left, Matrix right) -{ - Matrix result = MatrixIdentity(); - - result.m0 = left.m0 - right.m0; - result.m1 = left.m1 - right.m1; - result.m2 = left.m2 - right.m2; - result.m3 = left.m3 - right.m3; - result.m4 = left.m4 - right.m4; - result.m5 = left.m5 - right.m5; - result.m6 = left.m6 - right.m6; - result.m7 = left.m7 - right.m7; - result.m8 = left.m8 - right.m8; - result.m9 = left.m9 - right.m9; - result.m10 = left.m10 - right.m10; - result.m11 = left.m11 - right.m11; - result.m12 = left.m12 - right.m12; - result.m13 = left.m13 - right.m13; - result.m14 = left.m14 - right.m14; - result.m15 = left.m15 - right.m15; - - return result; -} - -// Returns translation matrix -RMDEF Matrix MatrixTranslate(float x, float y, float z) -{ - Matrix result = { 1.0f, 0.0f, 0.0f, x, - 0.0f, 1.0f, 0.0f, y, - 0.0f, 0.0f, 1.0f, z, - 0.0f, 0.0f, 0.0f, 1.0f }; - - return result; -} - -// Create rotation matrix from axis and angle -// NOTE: Angle should be provided in radians -RMDEF Matrix MatrixRotate(Vector3 axis, float angle) -{ - Matrix result = { 0 }; - - float x = axis.x, y = axis.y, z = axis.z; - - float length = sqrtf(x*x + y*y + z*z); - - if ((length != 1.0f) && (length != 0.0f)) - { - length = 1.0f/length; - x *= length; - y *= length; - z *= length; - } - - float sinres = sinf(angle); - float cosres = cosf(angle); - float t = 1.0f - cosres; - - result.m0 = x*x*t + cosres; - result.m1 = y*x*t + z*sinres; - result.m2 = z*x*t - y*sinres; - result.m3 = 0.0f; - - result.m4 = x*y*t - z*sinres; - result.m5 = y*y*t + cosres; - result.m6 = z*y*t + x*sinres; - result.m7 = 0.0f; - - result.m8 = x*z*t + y*sinres; - result.m9 = y*z*t - x*sinres; - result.m10 = z*z*t + cosres; - result.m11 = 0.0f; - - result.m12 = 0.0f; - result.m13 = 0.0f; - result.m14 = 0.0f; - result.m15 = 1.0f; - - return result; -} - -// Returns x-rotation matrix (angle in radians) -RMDEF Matrix MatrixRotateX(float angle) -{ - Matrix result = MatrixIdentity(); - - float cosres = cosf(angle); - float sinres = sinf(angle); - - result.m5 = cosres; - result.m6 = -sinres; - result.m9 = sinres; - result.m10 = cosres; - - return result; -} - -// Returns y-rotation matrix (angle in radians) -RMDEF Matrix MatrixRotateY(float angle) -{ - Matrix result = MatrixIdentity(); - - float cosres = cosf(angle); - float sinres = sinf(angle); - - result.m0 = cosres; - result.m2 = sinres; - result.m8 = -sinres; - result.m10 = cosres; - - return result; -} - -// Returns z-rotation matrix (angle in radians) -RMDEF Matrix MatrixRotateZ(float angle) -{ - Matrix result = MatrixIdentity(); - - float cosres = cosf(angle); - float sinres = sinf(angle); - - result.m0 = cosres; - result.m1 = -sinres; - result.m4 = sinres; - result.m5 = cosres; - - return result; -} - -// Returns scaling matrix -RMDEF Matrix MatrixScale(float x, float y, float z) -{ - Matrix result = { x, 0.0f, 0.0f, 0.0f, - 0.0f, y, 0.0f, 0.0f, - 0.0f, 0.0f, z, 0.0f, - 0.0f, 0.0f, 0.0f, 1.0f }; - - return result; -} - -// Returns two matrix multiplication -// NOTE: When multiplying matrices... the order matters! -RMDEF Matrix MatrixMultiply(Matrix left, Matrix right) -{ - Matrix result = { 0 }; - - result.m0 = left.m0*right.m0 + left.m1*right.m4 + left.m2*right.m8 + left.m3*right.m12; - result.m1 = left.m0*right.m1 + left.m1*right.m5 + left.m2*right.m9 + left.m3*right.m13; - result.m2 = left.m0*right.m2 + left.m1*right.m6 + left.m2*right.m10 + left.m3*right.m14; - result.m3 = left.m0*right.m3 + left.m1*right.m7 + left.m2*right.m11 + left.m3*right.m15; - result.m4 = left.m4*right.m0 + left.m5*right.m4 + left.m6*right.m8 + left.m7*right.m12; - result.m5 = left.m4*right.m1 + left.m5*right.m5 + left.m6*right.m9 + left.m7*right.m13; - result.m6 = left.m4*right.m2 + left.m5*right.m6 + left.m6*right.m10 + left.m7*right.m14; - result.m7 = left.m4*right.m3 + left.m5*right.m7 + left.m6*right.m11 + left.m7*right.m15; - result.m8 = left.m8*right.m0 + left.m9*right.m4 + left.m10*right.m8 + left.m11*right.m12; - result.m9 = left.m8*right.m1 + left.m9*right.m5 + left.m10*right.m9 + left.m11*right.m13; - result.m10 = left.m8*right.m2 + left.m9*right.m6 + left.m10*right.m10 + left.m11*right.m14; - result.m11 = left.m8*right.m3 + left.m9*right.m7 + left.m10*right.m11 + left.m11*right.m15; - result.m12 = left.m12*right.m0 + left.m13*right.m4 + left.m14*right.m8 + left.m15*right.m12; - result.m13 = left.m12*right.m1 + left.m13*right.m5 + left.m14*right.m9 + left.m15*right.m13; - result.m14 = left.m12*right.m2 + left.m13*right.m6 + left.m14*right.m10 + left.m15*right.m14; - result.m15 = left.m12*right.m3 + left.m13*right.m7 + left.m14*right.m11 + left.m15*right.m15; - - return result; -} - -// Returns perspective projection matrix -RMDEF Matrix MatrixFrustum(double left, double right, double bottom, double top, double near, double far) -{ - Matrix result = { 0 }; - - float rl = (right - left); - float tb = (top - bottom); - float fn = (far - near); - - result.m0 = (near*2.0f)/rl; - result.m1 = 0.0f; - result.m2 = 0.0f; - result.m3 = 0.0f; - - result.m4 = 0.0f; - result.m5 = (near*2.0f)/tb; - result.m6 = 0.0f; - result.m7 = 0.0f; - - result.m8 = (right + left)/rl; - result.m9 = (top + bottom)/tb; - result.m10 = -(far + near)/fn; - result.m11 = -1.0f; - - result.m12 = 0.0f; - result.m13 = 0.0f; - result.m14 = -(far*near*2.0f)/fn; - result.m15 = 0.0f; - - return result; -} - -// Returns perspective projection matrix -// NOTE: Angle should be provided in radians -RMDEF Matrix MatrixPerspective(double fovy, double aspect, double near, double far) -{ - double top = near*tan(fovy*0.5); - double right = top*aspect; - Matrix result = MatrixFrustum(-right, right, -top, top, near, far); - - return result; -} - -// Returns orthographic projection matrix -RMDEF Matrix MatrixOrtho(double left, double right, double bottom, double top, double near, double far) -{ - Matrix result = { 0 }; - - float rl = (right - left); - float tb = (top - bottom); - float fn = (far - near); - - result.m0 = 2.0f/rl; - result.m1 = 0.0f; - result.m2 = 0.0f; - result.m3 = 0.0f; - result.m4 = 0.0f; - result.m5 = 2.0f/tb; - result.m6 = 0.0f; - result.m7 = 0.0f; - result.m8 = 0.0f; - result.m9 = 0.0f; - result.m10 = -2.0f/fn; - result.m11 = 0.0f; - result.m12 = -(left + right)/rl; - result.m13 = -(top + bottom)/tb; - result.m14 = -(far + near)/fn; - result.m15 = 1.0f; - - return result; -} - -// Returns camera look-at matrix (view matrix) -RMDEF Matrix MatrixLookAt(Vector3 eye, Vector3 target, Vector3 up) -{ - Matrix result = { 0 }; - - Vector3 z = Vector3Subtract(eye, target); - z = Vector3Normalize(z); - Vector3 x = Vector3CrossProduct(up, z); - x = Vector3Normalize(x); - Vector3 y = Vector3CrossProduct(z, x); - y = Vector3Normalize(y); - - result.m0 = x.x; - result.m1 = x.y; - result.m2 = x.z; - result.m3 = 0.0f; - result.m4 = y.x; - result.m5 = y.y; - result.m6 = y.z; - result.m7 = 0.0f; - result.m8 = z.x; - result.m9 = z.y; - result.m10 = z.z; - result.m11 = 0.0f; - result.m12 = eye.x; - result.m13 = eye.y; - result.m14 = eye.z; - result.m15 = 1.0f; - - result = MatrixInvert(result); - - return result; -} - -// Returns float array of matrix data -RMDEF float16 MatrixToFloatV(Matrix mat) -{ - float16 buffer = { 0 }; - - buffer.v[0] = mat.m0; - buffer.v[1] = mat.m1; - buffer.v[2] = mat.m2; - buffer.v[3] = mat.m3; - buffer.v[4] = mat.m4; - buffer.v[5] = mat.m5; - buffer.v[6] = mat.m6; - buffer.v[7] = mat.m7; - buffer.v[8] = mat.m8; - buffer.v[9] = mat.m9; - buffer.v[10] = mat.m10; - buffer.v[11] = mat.m11; - buffer.v[12] = mat.m12; - buffer.v[13] = mat.m13; - buffer.v[14] = mat.m14; - buffer.v[15] = mat.m15; - - return buffer; -} - -//---------------------------------------------------------------------------------- -// Module Functions Definition - Quaternion math -//---------------------------------------------------------------------------------- - -// Returns identity quaternion -RMDEF Quaternion QuaternionIdentity(void) -{ - Quaternion result = { 0.0f, 0.0f, 0.0f, 1.0f }; - return result; -} - -// Computes the length of a quaternion -RMDEF float QuaternionLength(Quaternion q) -{ - float result = sqrt(q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w); - return result; -} - -// Normalize provided quaternion -RMDEF Quaternion QuaternionNormalize(Quaternion q) -{ - Quaternion result = { 0 }; - - float length, ilength; - length = QuaternionLength(q); - if (length == 0.0f) length = 1.0f; - ilength = 1.0f/length; - - result.x = q.x*ilength; - result.y = q.y*ilength; - result.z = q.z*ilength; - result.w = q.w*ilength; - - return result; -} - -// Invert provided quaternion -RMDEF Quaternion QuaternionInvert(Quaternion q) -{ - Quaternion result = q; - float length = QuaternionLength(q); - float lengthSq = length*length; - - if (lengthSq != 0.0) - { - float i = 1.0f/lengthSq; - - result.x *= -i; - result.y *= -i; - result.z *= -i; - result.w *= i; - } - - return result; -} - -// Calculate two quaternion multiplication -RMDEF Quaternion QuaternionMultiply(Quaternion q1, Quaternion q2) -{ - Quaternion result = { 0 }; - - float qax = q1.x, qay = q1.y, qaz = q1.z, qaw = q1.w; - float qbx = q2.x, qby = q2.y, qbz = q2.z, qbw = q2.w; - - result.x = qax*qbw + qaw*qbx + qay*qbz - qaz*qby; - result.y = qay*qbw + qaw*qby + qaz*qbx - qax*qbz; - result.z = qaz*qbw + qaw*qbz + qax*qby - qay*qbx; - result.w = qaw*qbw - qax*qbx - qay*qby - qaz*qbz; - - return result; -} - -// Calculate linear interpolation between two quaternions -RMDEF Quaternion QuaternionLerp(Quaternion q1, Quaternion q2, float amount) -{ - Quaternion result = { 0 }; - - result.x = q1.x + amount*(q2.x - q1.x); - result.y = q1.y + amount*(q2.y - q1.y); - result.z = q1.z + amount*(q2.z - q1.z); - result.w = q1.w + amount*(q2.w - q1.w); - - return result; -} - -// Calculate slerp-optimized interpolation between two quaternions -RMDEF Quaternion QuaternionNlerp(Quaternion q1, Quaternion q2, float amount) -{ - Quaternion result = QuaternionLerp(q1, q2, amount); - result = QuaternionNormalize(result); - - return result; -} - -// Calculates spherical linear interpolation between two quaternions -RMDEF Quaternion QuaternionSlerp(Quaternion q1, Quaternion q2, float amount) -{ - Quaternion result = { 0 }; - - float cosHalfTheta = q1.x*q2.x + q1.y*q2.y + q1.z*q2.z + q1.w*q2.w; - - if (fabs(cosHalfTheta) >= 1.0f) result = q1; - else if (cosHalfTheta > 0.95f) result = QuaternionNlerp(q1, q2, amount); - else - { - float halfTheta = acos(cosHalfTheta); - float sinHalfTheta = sqrt(1.0f - cosHalfTheta*cosHalfTheta); - - if (fabs(sinHalfTheta) < 0.001f) - { - result.x = (q1.x*0.5f + q2.x*0.5f); - result.y = (q1.y*0.5f + q2.y*0.5f); - result.z = (q1.z*0.5f + q2.z*0.5f); - result.w = (q1.w*0.5f + q2.w*0.5f); - } - else - { - float ratioA = sinf((1 - amount)*halfTheta)/sinHalfTheta; - float ratioB = sinf(amount*halfTheta)/sinHalfTheta; - - result.x = (q1.x*ratioA + q2.x*ratioB); - result.y = (q1.y*ratioA + q2.y*ratioB); - result.z = (q1.z*ratioA + q2.z*ratioB); - result.w = (q1.w*ratioA + q2.w*ratioB); - } - } - - return result; -} - -// Calculate quaternion based on the rotation from one vector to another -RMDEF Quaternion QuaternionFromVector3ToVector3(Vector3 from, Vector3 to) -{ - Quaternion result = { 0 }; - - float cos2Theta = Vector3DotProduct(from, to); - Vector3 cross = Vector3CrossProduct(from, to); - - result.x = cross.x; - result.y = cross.y; - result.z = cross.y; - result.w = 1.0f + cos2Theta; // NOTE: Added QuaternioIdentity() - - // Normalize to essentially nlerp the original and identity to 0.5 - result = QuaternionNormalize(result); - - // Above lines are equivalent to: - //Quaternion result = QuaternionNlerp(q, QuaternionIdentity(), 0.5f); - - return result; -} - -// Returns a quaternion for a given rotation matrix -RMDEF Quaternion QuaternionFromMatrix(Matrix mat) -{ - Quaternion result = { 0 }; - - float trace = MatrixTrace(mat); - - if (trace > 0.0f) - { - float s = (float)sqrt(trace + 1)*2.0f; - float invS = 1.0f/s; - - result.w = s*0.25f; - result.x = (mat.m6 - mat.m9)*invS; - result.y = (mat.m8 - mat.m2)*invS; - result.z = (mat.m1 - mat.m4)*invS; - } - else - { - float m00 = mat.m0, m11 = mat.m5, m22 = mat.m10; - - if (m00 > m11 && m00 > m22) - { - float s = (float)sqrt(1.0f + m00 - m11 - m22)*2.0f; - float invS = 1.0f/s; - - result.w = (mat.m6 - mat.m9)*invS; - result.x = s*0.25f; - result.y = (mat.m4 + mat.m1)*invS; - result.z = (mat.m8 + mat.m2)*invS; - } - else if (m11 > m22) - { - float s = (float)sqrt(1.0f + m11 - m00 - m22)*2.0f; - float invS = 1.0f/s; - - result.w = (mat.m8 - mat.m2)*invS; - result.x = (mat.m4 + mat.m1)*invS; - result.y = s*0.25f; - result.z = (mat.m9 + mat.m6)*invS; - } - else - { - float s = (float)sqrt(1.0f + m22 - m00 - m11)*2.0f; - float invS = 1.0f/s; - - result.w = (mat.m1 - mat.m4)*invS; - result.x = (mat.m8 + mat.m2)*invS; - result.y = (mat.m9 + mat.m6)*invS; - result.z = s*0.25f; - } - } - - return result; -} - -// Returns a matrix for a given quaternion -RMDEF Matrix QuaternionToMatrix(Quaternion q) -{ - Matrix result = { 0 }; - - float x = q.x, y = q.y, z = q.z, w = q.w; - - float x2 = x + x; - float y2 = y + y; - float z2 = z + z; - - float length = QuaternionLength(q); - float lengthSquared = length*length; - - float xx = x*x2/lengthSquared; - float xy = x*y2/lengthSquared; - float xz = x*z2/lengthSquared; - - float yy = y*y2/lengthSquared; - float yz = y*z2/lengthSquared; - float zz = z*z2/lengthSquared; - - float wx = w*x2/lengthSquared; - float wy = w*y2/lengthSquared; - float wz = w*z2/lengthSquared; - - result.m0 = 1.0f - (yy + zz); - result.m1 = xy - wz; - result.m2 = xz + wy; - result.m3 = 0.0f; - result.m4 = xy + wz; - result.m5 = 1.0f - (xx + zz); - result.m6 = yz - wx; - result.m7 = 0.0f; - result.m8 = xz - wy; - result.m9 = yz + wx; - result.m10 = 1.0f - (xx + yy); - result.m11 = 0.0f; - result.m12 = 0.0f; - result.m13 = 0.0f; - result.m14 = 0.0f; - result.m15 = 1.0f; - - return result; -} - -// Returns rotation quaternion for an angle and axis -// NOTE: angle must be provided in radians -RMDEF Quaternion QuaternionFromAxisAngle(Vector3 axis, float angle) -{ - Quaternion result = { 0.0f, 0.0f, 0.0f, 1.0f }; - - if (Vector3Length(axis) != 0.0f) - - angle *= 0.5f; - - axis = Vector3Normalize(axis); - - float sinres = sinf(angle); - float cosres = cosf(angle); - - result.x = axis.x*sinres; - result.y = axis.y*sinres; - result.z = axis.z*sinres; - result.w = cosres; - - result = QuaternionNormalize(result); - - return result; -} - -// Returns the rotation angle and axis for a given quaternion -RMDEF void QuaternionToAxisAngle(Quaternion q, Vector3 *outAxis, float *outAngle) -{ - if (fabs(q.w) > 1.0f) q = QuaternionNormalize(q); - - Vector3 resAxis = { 0.0f, 0.0f, 0.0f }; - float resAngle = 0.0f; - - resAngle = 2.0f*(float)acos(q.w); - float den = (float)sqrt(1.0f - q.w*q.w); - - if (den > 0.0001f) - { - resAxis.x = q.x/den; - resAxis.y = q.y/den; - resAxis.z = q.z/den; - } - else - { - // This occurs when the angle is zero. - // Not a problem: just set an arbitrary normalized axis. - resAxis.x = 1.0f; - } - - *outAxis = resAxis; - *outAngle = resAngle; -} - -// Returns he quaternion equivalent to Euler angles -RMDEF Quaternion QuaternionFromEuler(float roll, float pitch, float yaw) -{ - Quaternion q = { 0 }; - - float x0 = cosf(roll*0.5f); - float x1 = sinf(roll*0.5f); - float y0 = cosf(pitch*0.5f); - float y1 = sinf(pitch*0.5f); - float z0 = cosf(yaw*0.5f); - float z1 = sinf(yaw*0.5f); - - q.x = x1*y0*z0 - x0*y1*z1; - q.y = x0*y1*z0 + x1*y0*z1; - q.z = x0*y0*z1 - x1*y1*z0; - q.w = x0*y0*z0 + x1*y1*z1; - - return q; -} - -// Return the Euler angles equivalent to quaternion (roll, pitch, yaw) -// NOTE: Angles are returned in a Vector3 struct in degrees -RMDEF Vector3 QuaternionToEuler(Quaternion q) -{ - Vector3 result = { 0 }; - - // roll (x-axis rotation) - float x0 = 2.0f*(q.w*q.x + q.y*q.z); - float x1 = 1.0f - 2.0f*(q.x*q.x + q.y*q.y); - result.x = atan2f(x0, x1)*RAD2DEG; - - // pitch (y-axis rotation) - float y0 = 2.0f*(q.w*q.y - q.z*q.x); - y0 = y0 > 1.0f ? 1.0f : y0; - y0 = y0 < -1.0f ? -1.0f : y0; - result.y = asinf(y0)*RAD2DEG; - - // yaw (z-axis rotation) - float z0 = 2.0f*(q.w*q.z + q.x*q.y); - float z1 = 1.0f - 2.0f*(q.y*q.y + q.z*q.z); - result.z = atan2f(z0, z1)*RAD2DEG; - - return result; -} - -// Transform a quaternion given a transformation matrix -RMDEF Quaternion QuaternionTransform(Quaternion q, Matrix mat) -{ - Quaternion result = { 0 }; - - result.x = mat.m0*q.x + mat.m4*q.y + mat.m8*q.z + mat.m12*q.w; - result.y = mat.m1*q.x + mat.m5*q.y + mat.m9*q.z + mat.m13*q.w; - result.z = mat.m2*q.x + mat.m6*q.y + mat.m10*q.z + mat.m14*q.w; - result.w = mat.m3*q.x + mat.m7*q.y + mat.m11*q.z + mat.m15*q.w; - - return result; -} - -#endif // RAYMATH_H diff --git a/examples/others/iqm_loader/riqm.h b/examples/others/iqm_loader/riqm.h deleted file mode 100644 index 41ef8a140..000000000 --- a/examples/others/iqm_loader/riqm.h +++ /dev/null @@ -1,98 +0,0 @@ -/********************************************************************************************** -* -* riqm - InterQuake Model format (IQM) loader for animated meshes -* -* CONFIGURATION: -* -* #define RIQM_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) 2018 Jonas Daeyaert (@culacant) 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 RIQM_H -#define RIQM_H - -//#define RIQM_STATIC -#ifdef RIQM_STATIC - #define RIQMDEF static // Functions just visible to module including this file -#else - #ifdef __cplusplus - #define RIQMDEF extern "C" // Functions visible from other files (no name mangling of functions in C++) - #else - #define RIQMDEF extern // Functions visible from other files - #endif -#endif - -//---------------------------------------------------------------------------------- -// Types and Structures Definition -//---------------------------------------------------------------------------------- - -#define BONE_NAME_LENGTH 32 // BoneInfo name string length -#define MESH_NAME_LENGTH 32 // Mesh name string length - -//---------------------------------------------------------------------------------- -// Module Functions Declaration -//---------------------------------------------------------------------------------- - - -#endif // RIQM_H - - -/*********************************************************************************** -* -* RIQM IMPLEMENTATION -* -************************************************************************************/ - -#if defined(RIQM_IMPLEMENTATION) - -//#include "utils.h" // Required for: fopen() Android mapping - -#include // Required for: FILE, fopen(), fclose(), feof(), fseek(), fread() -#include // Required for: malloc(), free() -#include // Required for: strncmp(),strcpy() - -#include "raymath.h" // Required for: Vector3, Quaternion functions - -//---------------------------------------------------------------------------------- -// Defines and Macros -//---------------------------------------------------------------------------------- - -//---------------------------------------------------------------------------------- -// Global Variables Definition -//---------------------------------------------------------------------------------- - -//---------------------------------------------------------------------------------- -// Module specific Functions Declaration -//---------------------------------------------------------------------------------- - -#ifdef __cplusplus -extern "C" { // Prevents name mangling of functions -#endif - - - - -#endif From 75384add9882c61a61e42fd2b0178e6667fe7442 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 12 Apr 2019 00:01:32 +0200 Subject: [PATCH 64/74] Added Crystal binding --- BINDINGS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/BINDINGS.md b/BINDINGS.md index 38238f9ac..b84c9064f 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -7,6 +7,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to - [raylib-lua-ffi](https://github.com/raysan5/raylib/issues/693) : raylib **Lua** ffi binding - [raylib-go](https://github.com/gen2brain/raylib-go) : raylib **Go** binding - [raylib-Nim](https://gitlab.com/define-private-public/raylib-Nim) : raylib **Nim** binding + - [raylib-cr](https://github.com/AregevDev/raylib-cr) : raylib **Crystal** binding - [cray](https://gitlab.com/Zatherz/cray) - raylib **Crystal** binding - [Graphics::Raylib](https://metacpan.org/pod/Graphics::Raylib) : raylib **Perl** wrapper - [raylib-pascal](https://github.com/drezgames/raylib-pascal) - raylib **Pascal** binding From 30d51ec26ca8f00bd691c570a78b8eb2e41fa817 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 12 Apr 2019 11:29:01 +0200 Subject: [PATCH 65/74] Reorganize struct --- src/raylib.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index 73661f58d..4162454eb 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -288,12 +288,6 @@ typedef struct Camera2D { float zoom; // Camera zoom (scaling), should be 1.0f by default } Camera2D; -// Bounding box type -typedef struct BoundingBox { - Vector3 min; // Minimum vertex box-corner - Vector3 max; // Maximum vertex box-corner -} BoundingBox; - // Vertex data definning a mesh // NOTE: Data stored in CPU memory (and GPU) typedef struct Mesh { @@ -393,6 +387,12 @@ typedef struct RayHitInfo { Vector3 normal; // Surface normal of hit } RayHitInfo; +// Bounding box type +typedef struct BoundingBox { + Vector3 min; // Minimum vertex box-corner + Vector3 max; // Maximum vertex box-corner +} BoundingBox; + // Wave type, defines audio wave data typedef struct Wave { unsigned int sampleCount; // Number of samples @@ -1237,7 +1237,7 @@ RLAPI void DrawGizmo(Vector3 position); // Model loading/unloading functions RLAPI Model LoadModel(const char *fileName); // Load model from files (meshes and materials) -RLAPI Model LoadModelFromMesh(Mesh mesh); // Load model from generated mesh +RLAPI Model LoadModelFromMesh(Mesh mesh); // Load model from generated mesh (default material) RLAPI void UnloadModel(Model model); // Unload model from memory (RAM and/or VRAM) // Mesh loading/unloading functions From 70fc549818c172a0b6b1ee9037205dc06f471ba5 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 12 Apr 2019 13:03:49 +0200 Subject: [PATCH 66/74] Working on raylib 2.5 changelog... --- CHANGELOG | 120 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 119 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 8546e5b0d..a7a75b60e 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -6,8 +6,126 @@ Current Release: raylib 2.0.0 (July 2018) ----------------------------------------------- Release: raylib 2.5 (May 2019) ----------------------------------------------- +KEY CHANGES: + - New text management API + - Cubemap textures support + - Skeletal model animation support + - New textures drawing: Quad and N-Patch based rectangles + - New drawing shapes available: CircleSector, Ring and RectangleRounded + - VR system redesign to allow custom device parameters and distortion shader -TODO. +Detailed changes: +[build] ... +[build] REVIEWED: Notepad++ NppExec scripts +[build] REVIEWED: VS2015 and VS2017 projects +[build] REVIEWED: Android APK build pipeline + +[core] ADDED: IsWindowResized() - Check if window has been resized +[core] ADDED: IsWindowHidden() - Check if window is currently hidden +[core] ADDED: UnhideWindow() - Show the window +[core] ADDED: HideWindow() - Hide the window +[core] ADDED: GetWindowHandle() - Get native window handle +[core] ADDED: GetMonitorCount() - Get number of connected monitors +[core] ADDED: GetMonitorWidth() - Get primary monitor width +[core] ADDED: GetMonitorHeight() - Get primary monitor height +[core] ADDED: GetMonitorPhysicalWidth() - Get primary monitor physical width in millimetres +[core] ADDED: GetMonitorPhysicalHeight() - Get primary monitor physical height in millimetres +[core] ADDED: GetMonitorName() - Get the human-readable, UTF-8 encoded name of the primary monitor +[core] ADDED: GetClipboardText() - Get clipboard text content +[core] ADDED: SetClipboardText() - Set clipboard text content +[core] ADDED: ColorFromHSV() - Returns a Color from HSV values +[core] ADDED: FileExists() - Check if file exists +[core] ADDED: GetFileNameWithoutExt() - Get filename string without extension (memory should be freed) +[core] ADDED: GetDirectoryFiles() - Get filenames in a directory path (memory should be freed) +[core] ADDED: ClearDirectoryFiles() - Clear directory files paths buffers (free memory) +[core] ADDED: OpenURL() - Open URL with default system browser (if available) +[core] ADDED: SetMouseOffset() - Set mouse offset +[core] ADDED: SetMouseScale() - Set mouse scaling +[core] REMOVED: ShowLogo() - Activate raylib logo at startup (can be done with flags) +[shapes] ADDED: DrawCircleSector() - Draw a piece of a circle +[shapes] ADDED: DrawCircleSectorLines() - Draw circle sector outline +[shapes] ADDED: DrawRing() - Draw ring +[shapes] ADDED: DrawRingLines() - Draw ring outline +[shapes] ADDED: DrawRectangleRounded() - Draw rectangle with rounded edges +[shapes] ADDED: DrawRectangleRoundedLines() - Draw rectangle with rounded edges outline +[shapes] ADDED: SetShapesTexture() - Define default texture used to draw shapes +[textures] REVIEWED: ExportImage() - Reorder function parameters +[textures] REVIEWED: ImageDrawRectangle() - Remove unneeded parameter +[textures] ADDED: ExportImageAsCode() - Export image as code file defining an array of bytes +[textures] ADDED: LoadTextureCubemap() - Load cubemap from image, multiple image cubemap layouts supported +[textures] ADDED: ImageExtractPalette() - Extract color palette from image to maximum size (memory should be freed) +[textures] ADDED: ImageDrawRectangleLines() - Draw rectangle lines within an image +[textures] ADDED: DrawTextureQuad() - Draw texture quad with tiling and offset parameters +[textures] ADDED: DrawTextureNPatch() - Draws a texture (or part of it) that stretches or shrinks nicely +[models] REVIEWED: LoadMesh() -> LoadMeshes() - Support multiple meshes loading +[models] REVIEWED: LoadMaterial() -> LoadMaterials() - Support multiple materials loading +[models] REVIEWED: ExportMesh() - Reorder parameters +[models] ADDED: DrawCubeWiresV() - Draw cube wires (Vector version) +[models] ADDED: GenMeshPoly() - Generate polygonal mesh +[models] ADDED: SetMaterialTexture() - Set texture for a material map type (MAP_DIFFUSE, MAP_SPECULAR...) +[models] ADDED: SetModelMeshMaterial() - Set material for a mesh +[models] ADDED: LoadModelAnimations() - Load model animations from file +[models] ADDED: UpdateModelAnimation() - Update model animation pose +[models] ADDED: UnloadModelAnimation() - Unload animation data +[models] ADDED: IsModelAnimationValid() - Check model animation skeleton match +[rlgl] Improved internal batching mechanism (multibuffering support, triangle texcoords...) +[rlgl] REVIEWED: rlPushMatrix()/rlPopMatrix() - Now works like OpenGL 1.1 +[rlgl] REVIEWED: SetShaderValue() - More generic, now requires uniform type +[rlgl] REMOVED: SetShaderValuei() - Can be acoomplished with new SetShaderValue() +[rlgl] ADDED: SetShaderValueV() - Set shader uniform value vector +[rlgl] ADDED: SetShaderValueTexture() - Set shader uniform value for texture +[rlgl] ADDED: BeginScissorMode() - Begin scissor mode (define screen area for following drawing) +[rlgl] ADDED: EndScissorMode() - End scissor mode +[rlgl] ADDED: SetVrConfiguration() - Set stereo rendering configuration parameters +[rlgl] REVIEWED: InitVrSimulator() - No input parameter required, use SetVrConfiguration() +[text] REVIEWED: LoadFontEx() - Reorder function parameters +[text] REVIEWED: LoadFontData() - Reorder function parameters +[text] REVIEWED: GenImageFontAtlas() - Reorder function parameters +[text] RENAMED: FormatText() -> TextFormat() +[text] RENAMED: SubText() -> TextSubtext() +[text] ADDED: LoadFontFromImage() - Load font from Image (XNA style) +[text] ADDED: DrawTextRec() - Draw text using font inside rectangle limits +[text] ADDED: DrawTextRecEx() - Draw text using font inside rectangle limits with support for text selection +[text] ADDED: TextIsEqual() - Check if two text string are equal +[text] ADDED: TextLength() - Get text length, checks for '\0' ending +[text] ADDED: TextReplace() - Replace text string (memory should be freed!) +[text] ADDED: TextInsert() - Insert text in a position (memory should be freed!) +[text] ADDED: TextJoin() - Join text strings with delimiter +[text] ADDED: TextSplit() - Split text into multiple strings +[text] ADDED: TextAppend() - Append text at specific position and move cursor! +[text] ADDED: TextFindIndex() - Find first text occurrence within a string +[text] ADDED: TextToUpper() - Get upper case version of provided string +[text] ADDED: TextToLower() - Get lower case version of provided string +[text] ADDED: TextToPascal() - Get Pascal case notation version of provided string +[text] ADDED: TextToInteger() - Get integer value from text (negative values not supported) +[raudio] ADDED: ExportWave() - Export wave data to file +[raudio] ADDED: ExportWaveAsCode() - Export wave sample data to code (.h) +[raudio] ADDED: IsAudioStreamPlaying() - Check if audio stream is playing +[raudio] ADDED: SetAudioStreamVolume() - Set volume for audio stream (1.0 is max level) +[raudio] ADDED: SetAudioStreamPitch() - Set pitch for audio stream (1.0 is base level) +[examples] ADDED: core_custom_logging - Custom trace log system +[examples] ADDED: core_input_multitouch - Multitouch input example +[examples] REVIEWED: core_vr_simulator - HMD device parameters and distortion shader should be provided +[examples] ADDED: core_window_scale_letterbox - Windows resizing and letterbox content +[examples] ADDED: shapes_rectangle_scaling_mouse - Scale a rectangle with mouse +TODO: [examples] ADDED: shapes_draw_circle_sector - Circle sector drawing +TODO: [examples] ADDED: shapes_draw_ring - Ring drawing +TODO: [examples] ADDED: shapes_draw_rectangle_rounded - Rounded rectangle drawing +[examples] ADDED: textures_background_scrolling - Scrolling and parallaz background effect +[examples] ADDED: textures_image_npatch - Drawing N-Patch based boxes +TODO: [examples] ADDED: textures_sprite_button +TODO: [examples] ADDED: textures_sprite_explosion +[examples] ADDED: text_draw_inside_rectangle - Drawing text inside a delimited rectangle box +[examples] ADDED: models_animation - Animated models loading and animation playing +[examples] ADDED: models_obj_viewer - Draw and drop models viewer +[examples] ADDED: models_rlgl_solar_system - Solar system simulation using rlgl functionality +[examples] ADDED: shaders_palette_switch - Switching color palette on shader +[examples] ADDED: shaders_raymarching - Raymarching shader +[examples] ADDED: shaders_texture_drawing - Texture drawing on GPU +[examples] REVIEWED: audio_raw_stream - Mostly rewritten +[games] ADDED: GGJ19 game - Cat vs Roomba +[*] Updated external libraries to latest version +[*] Multiple bugs corrected (check github issues) ----------------------------------------------- Release: raylib 2.0 (July 2018) From 4e58d4102c0f466730971e7f7cd685b7359343ab Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 12 Apr 2019 13:29:37 +0200 Subject: [PATCH 67/74] Corrected typo --- src/raylib.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/raylib.h b/src/raylib.h index 4162454eb..6d42cfd14 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -925,7 +925,7 @@ RLAPI Ray GetMouseRay(Vector2 mousePosition, Camera camera); // Returns a r RLAPI Vector2 GetWorldToScreen(Vector3 position, Camera camera); // Returns the screen space position for a 3d world space position RLAPI Matrix GetCameraMatrix(Camera camera); // Returns camera transform matrix (view matrix) -// timing-related functions +// Timing-related functions RLAPI void SetTargetFPS(int fps); // Set target FPS (maximum) RLAPI int GetFPS(void); // Returns current FPS RLAPI float GetFrameTime(void); // Returns time in seconds for last frame drawn From 99537efccf077c0425c1de1188df632bfe96d125 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 12 Apr 2019 13:29:53 +0200 Subject: [PATCH 68/74] Review some examples --- examples/Makefile | 2 +- ...e_multitouch.c => core_input_multitouch.c} | 39 ++++++++---------- ...ltitouch.png => core_input_multitouch.png} | Bin examples/textures/textures_srcrec_dstrec.c | 8 ++-- 4 files changed, 23 insertions(+), 26 deletions(-) rename examples/core/{core_multitouch.c => core_input_multitouch.c} (67%) rename examples/core/{core_multitouch.png => core_input_multitouch.png} (100%) diff --git a/examples/Makefile b/examples/Makefile index b35e39f94..2ceb3f7dc 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -377,7 +377,7 @@ EXAMPLES = \ core/core_2d_camera \ core/core_world_screen \ core/core_vr_simulator \ - core/core_multitouch \ + core/core_input_multitouch \ shapes/shapes_logo_raylib \ shapes/shapes_basic_shapes \ shapes/shapes_colors_palette \ diff --git a/examples/core/core_multitouch.c b/examples/core/core_input_multitouch.c similarity index 67% rename from examples/core/core_multitouch.c rename to examples/core/core_input_multitouch.c index c059ac035..ef8fa7ae2 100644 --- a/examples/core/core_multitouch.c +++ b/examples/core/core_input_multitouch.c @@ -1,17 +1,15 @@ /******************************************************************************************* * -* raylib [core] example - Multitouch input +* raylib [core] example - Input multitouch * * This example has been created using raylib 2.1 (www.raylib.com) * raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) * -* Copyright (c) 2014 Ramon Santamaria (@raysan5) -* Example by Berni +* Copyright (c) 2014-2019 Berni and Ramon Santamaria (@raysan5) * ********************************************************************************************/ #include "raylib.h" -#include int main() { @@ -20,13 +18,13 @@ int main() int screenWidth = 800; int screenHeight = 450; - InitWindow(screenWidth, screenHeight, "raylib [core] example - multitouch input"); + InitWindow(screenWidth, screenHeight, "raylib [core] example - input multitouch"); Vector2 ballPosition = { -100.0f, -100.0f }; - Color ballColor; - int PressedCounter = 0; - Vector2 TouchPos; - char Str[16]; + Color ballColor = BEIGE; + + int touchCounter = 0; + Vector2 touchPosition; SetTargetFPS(60); //--------------------------------------------------------------------------------------- @@ -44,11 +42,11 @@ int main() if (IsMouseButtonDown(MOUSE_MIDDLE_BUTTON)) ballColor = LIME; if (IsMouseButtonDown(MOUSE_RIGHT_BUTTON)) ballColor = DARKBLUE; - if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) PressedCounter = 10; - if (IsMouseButtonPressed(MOUSE_MIDDLE_BUTTON)) PressedCounter = 10; - if (IsMouseButtonPressed(MOUSE_RIGHT_BUTTON)) PressedCounter = 10; - if(PressedCounter > 0) - PressedCounter--; + if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) touchCounter = 10; + if (IsMouseButtonPressed(MOUSE_MIDDLE_BUTTON)) touchCounter = 10; + if (IsMouseButtonPressed(MOUSE_RIGHT_BUTTON)) touchCounter = 10; + + if (touchCounter > 0) touchCounter--; //---------------------------------------------------------------------------------- // Draw @@ -60,19 +58,18 @@ int main() // Multitouch for (int i = 0; i < MAX_TOUCH_POINTS; ++i) { - TouchPos = GetTouchPosition(i); // Get the touch point + touchPosition = GetTouchPosition(i); // Get the touch point - if( (TouchPos.x >= 0) && (TouchPos.y >= 0) ) // Make sure point is not (-1,-1) as this means there is no touch for it + if ((touchPosition.x >= 0) && (touchPosition.y >= 0)) // Make sure point is not (-1,-1) as this means there is no touch for it { - DrawCircleV(TouchPos, 34, ORANGE); // Draw a circle there - - sprintf(Str,"%d",i); - DrawText(Str, TouchPos.x - 10, TouchPos.y - 70, 40, BLACK); // Also show its index number + // Draw circle and touch index number + DrawCircleV(touchPosition, 34, ORANGE); + DrawText(FormatText("%d", i), touchPosition.x - 10, touchPosition.y - 70, 40, BLACK); } } // Draw the normal mouse location - DrawCircleV(ballPosition, 30 + (PressedCounter * 3), ballColor); + DrawCircleV(ballPosition, 30 + (touchCounter*3), ballColor); DrawText("move ball with mouse and click mouse button to change color", 10, 10, 20, DARKGRAY); DrawText("touch the screen at multiple locations to get multiple balls", 10, 30, 20, DARKGRAY); diff --git a/examples/core/core_multitouch.png b/examples/core/core_input_multitouch.png similarity index 100% rename from examples/core/core_multitouch.png rename to examples/core/core_input_multitouch.png diff --git a/examples/textures/textures_srcrec_dstrec.c b/examples/textures/textures_srcrec_dstrec.c index cc08eb58c..298a0c65e 100644 --- a/examples/textures/textures_srcrec_dstrec.c +++ b/examples/textures/textures_srcrec_dstrec.c @@ -27,13 +27,13 @@ int main() int frameHeight = scarfy.height; // NOTE: Source rectangle (part of the texture to use for drawing) - Rectangle sourceRec = { 0.0f, 0.0f, (float)frameWidth, (float)frameHeight }; + Rectangle sourceRec = { 0.0f, 0.0f, frameWidth, frameHeight }; // NOTE: Destination rectangle (screen rectangle where drawing part of texture) - Rectangle destRec = { (float)screenWidth/2, (float)screenHeight/2, (float)frameWidth*2, (float)frameHeight*2 }; + Rectangle destRec = { screenWidth/2, screenHeight/2, frameWidth*2, frameHeight*2 }; // NOTE: Origin of the texture (rotation/scale point), it's relative to destination rectangle size - Vector2 origin = { (float)frameWidth, (float)frameHeight }; + Vector2 origin = { frameWidth, frameHeight }; int rotation = 0; @@ -61,7 +61,7 @@ int main() // rotation defines the texture rotation (using origin as rotation point) DrawTexturePro(scarfy, sourceRec, destRec, origin, (float)rotation, WHITE); - DrawLine((int) destRec.x, 0, (int) destRec.x, screenHeight, GRAY); + DrawLine((int)destRec.x, 0, (int)destRec.x, screenHeight, GRAY); DrawLine(0, (int)destRec.y, screenWidth, (int)destRec.y, GRAY); DrawText("(c) Scarfy sprite by Eiden Marsal", screenWidth - 200, screenHeight - 20, 10, GRAY); From df90ba6e463e5689039821fe93ea93d0a996b035 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 12 Apr 2019 13:31:05 +0200 Subject: [PATCH 69/74] WARNING: Added GLFW hint to support hi-DPI This needs to be tested on a hi-DPI monitor, probably requiring a FLAG to enable it would be a good idea... --- src/core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core.c b/src/core.c index 32773a0d8..e8316213c 100644 --- a/src/core.c +++ b/src/core.c @@ -2347,7 +2347,7 @@ static bool InitGraphicsDevice(int width, int height) displayHeight = screenHeight; #endif // defined(PLATFORM_WEB) - glfwDefaultWindowHints(); // Set default windows hints: + glfwDefaultWindowHints(); // Set default windows hints: //glfwWindowHint(GLFW_RED_BITS, 8); // Framebuffer red color component bits //glfwWindowHint(GLFW_GREEN_BITS, 8); // Framebuffer green color component bits //glfwWindowHint(GLFW_BLUE_BITS, 8); // Framebuffer blue color component bits @@ -2356,6 +2356,7 @@ static bool InitGraphicsDevice(int width, int height) //glfwWindowHint(GLFW_REFRESH_RATE, 0); // Refresh rate for fullscreen window //glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API); // OpenGL API to use. Alternative: GLFW_OPENGL_ES_API //glfwWindowHint(GLFW_AUX_BUFFERS, 0); // Number of auxiliar buffers + glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_TRUE); // Scale content area based on the monitor content scale where window is placed on // Check some Window creation flags if (configFlags & FLAG_WINDOW_HIDDEN) glfwWindowHint(GLFW_VISIBLE, GL_FALSE); // Visible window From fc5dd5d99fb233879885cd02da5010d81f37d0b0 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 12 Apr 2019 13:44:16 +0200 Subject: [PATCH 70/74] FLAG not supported on web GLFW implementation --- src/core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/core.c b/src/core.c index e8316213c..ba41c7730 100644 --- a/src/core.c +++ b/src/core.c @@ -2356,7 +2356,9 @@ static bool InitGraphicsDevice(int width, int height) //glfwWindowHint(GLFW_REFRESH_RATE, 0); // Refresh rate for fullscreen window //glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API); // OpenGL API to use. Alternative: GLFW_OPENGL_ES_API //glfwWindowHint(GLFW_AUX_BUFFERS, 0); // Number of auxiliar buffers +#if defined(PLATFORM_DESKTOP) glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_TRUE); // Scale content area based on the monitor content scale where window is placed on +#endif // Check some Window creation flags if (configFlags & FLAG_WINDOW_HIDDEN) glfwWindowHint(GLFW_VISIBLE, GL_FALSE); // Visible window From c1d3ba67f8c5559864c56984cf5806a7e4b1c9c3 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 12 Apr 2019 13:46:44 +0200 Subject: [PATCH 71/74] Update CHANGELOG --- CHANGELOG | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index a7a75b60e..c133acfda 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -20,6 +20,8 @@ Detailed changes: [build] REVIEWED: VS2015 and VS2017 projects [build] REVIEWED: Android APK build pipeline +[core] Converted most #defined values as enum values +[core] Complete redesign of RPI input system [core] ADDED: IsWindowResized() - Check if window has been resized [core] ADDED: IsWindowHidden() - Check if window is currently hidden [core] ADDED: UnhideWindow() - Show the window From 7cc8faf7daa1e3d0f742bbc118b9705b0546e072 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Wahlstrand?= Date: Fri, 12 Apr 2019 21:41:30 +0200 Subject: [PATCH 72/74] Use typedef rather than #define in order to avoid issues in application code --- src/raylib.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/raylib.h b/src/raylib.h index 6d42cfd14..0afa69d04 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -278,7 +278,7 @@ typedef struct Camera3D { int type; // Camera type, defines projection type: CAMERA_PERSPECTIVE or CAMERA_ORTHOGRAPHIC } Camera3D; -#define Camera Camera3D // Camera type fallback, defaults to Camera3D +typedef Camera3D Camera; // Camera type fallback, defaults to Camera3D // Camera2D type, defines a 2d camera typedef struct Camera2D { From 310d1d15890618f21c7e90d0c98b4f208c9c64ff Mon Sep 17 00:00:00 2001 From: flashjaysan Date: Sat, 13 Apr 2019 14:35:07 +0200 Subject: [PATCH 73/74] Update raymath.h Removed a useless semicolon. --- src/raymath.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/raymath.h b/src/raymath.h index 331165325..389a430f5 100644 --- a/src/raymath.h +++ b/src/raymath.h @@ -447,7 +447,7 @@ RMDEF Vector3 Vector3Transform(Vector3 v, Matrix mat) result.z = mat.m2*x + mat.m6*y + mat.m10*z + mat.m14; return result; -}; +} // Transform a vector by quaternion rotation RMDEF Vector3 Vector3RotateByQuaternion(Vector3 v, Quaternion q) From 8c22f685d168000eabfc994a09c3a2a61f7f633f Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 14 Apr 2019 22:29:14 +0200 Subject: [PATCH 74/74] Check buffer overflow --- src/models.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/models.c b/src/models.c index d8508492f..631c49420 100644 --- a/src/models.c +++ b/src/models.c @@ -295,6 +295,8 @@ void DrawCubeTexture(Texture2D texture, Vector3 position, float width, float hei float x = position.x; float y = position.y; float z = position.z; + + if (rlCheckBufferLimit(36)) rlglDraw(); rlEnableTexture(texture.id); @@ -357,6 +359,9 @@ void DrawSphere(Vector3 centerPos, float radius, Color color) // Draw sphere with extended parameters void DrawSphereEx(Vector3 centerPos, float radius, int rings, int slices, Color color) { + int numVertex = (rings + 2)*slices*6; + if (rlCheckBufferLimit(numVertex)) rlglDraw(); + rlPushMatrix(); // NOTE: Transformation is applied in inverse order (scale -> translate) rlTranslatef(centerPos.x, centerPos.y, centerPos.z); @@ -397,6 +402,9 @@ void DrawSphereEx(Vector3 centerPos, float radius, int rings, int slices, Color // Draw sphere wires void DrawSphereWires(Vector3 centerPos, float radius, int rings, int slices, Color color) { + int numVertex = (rings + 2)*slices*6; + if (rlCheckBufferLimit(numVertex)) rlglDraw(); + rlPushMatrix(); // NOTE: Transformation is applied in inverse order (scale -> translate) rlTranslatef(centerPos.x, centerPos.y, centerPos.z); @@ -440,6 +448,9 @@ void DrawSphereWires(Vector3 centerPos, float radius, int rings, int slices, Col void DrawCylinder(Vector3 position, float radiusTop, float radiusBottom, float height, int sides, Color color) { if (sides < 3) sides = 3; + + int numVertex = sides*6; + if (rlCheckBufferLimit(numVertex)) rlglDraw(); rlPushMatrix(); rlTranslatef(position.x, position.y, position.z); @@ -496,6 +507,9 @@ void DrawCylinder(Vector3 position, float radiusTop, float radiusBottom, float h void DrawCylinderWires(Vector3 position, float radiusTop, float radiusBottom, float height, int sides, Color color) { if (sides < 3) sides = 3; + + int numVertex = sides*8; + if (rlCheckBufferLimit(numVertex)) rlglDraw(); rlPushMatrix(); rlTranslatef(position.x, position.y, position.z); @@ -524,6 +538,8 @@ void DrawCylinderWires(Vector3 position, float radiusTop, float radiusBottom, fl // Draw a plane void DrawPlane(Vector3 centerPos, Vector2 size, Color color) { + if (rlCheckBufferLimit(4)) rlglDraw(); + // NOTE: Plane is always created on XZ ground rlPushMatrix(); rlTranslatef(centerPos.x, centerPos.y, centerPos.z); @@ -560,6 +576,8 @@ void DrawGrid(int slices, float spacing) { int halfSlices = slices/2; + if (rlCheckBufferLimit(slices*4)) rlglDraw(); + rlBegin(RL_LINES); for (int i = -halfSlices; i <= halfSlices; i++) {