From 2e99c6cefbaacf03d71ad10c03a60fbb49c46aa1 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 4 Mar 2019 22:58:20 +0100 Subject: ADDED: IsWindowResized() --- src/raylib.h | 1 + 1 file changed, 1 insertion(+) (limited to 'src/raylib.h') diff --git a/src/raylib.h b/src/raylib.h index 17a6efc6..9fdcf2d8 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -865,6 +865,7 @@ RLAPI bool WindowShouldClose(void); // Check if KE RLAPI void CloseWindow(void); // Close window and unload OpenGL context RLAPI bool IsWindowReady(void); // Check if window has been initialized successfully RLAPI bool IsWindowMinimized(void); // Check if window has been minimized (or lost focus) +RLAPI bool IsWindowResized(void); // Check if window has been resized RLAPI bool IsWindowHidden(void); // Check if window is currently hidden RLAPI void ToggleFullscreen(void); // Toggle fullscreen mode (only PLATFORM_DESKTOP) RLAPI void UnhideWindow(void); // Show the window -- cgit v1.2.3 From 2f97a3f83531867511f65f2b2ed894706cfb2e06 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 5 Mar 2019 16:46:48 +0100 Subject: Proposed Model struct review --- src/raylib.h | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'src/raylib.h') diff --git a/src/raylib.h b/src/raylib.h index 9fdcf2d8..cc461204 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -342,6 +342,15 @@ 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; + + Material *materials; // Shader and textures data + int materialCount; + + int *meshMaterial; // Material assigned to every mesh + */ } Model; // Ray type (useful for raycast) @@ -1180,7 +1189,7 @@ RLAPI const char *TextSubtext(const char *text, int position, int length); RLAPI const char *TextReplace(char *text, const char *replace, const char *by); // Replace text string (memory should be freed!) RLAPI const char *TextInsert(const char *text, const char *insert, int position); // Insert text in a position (memory should be freed!) RLAPI const char *TextJoin(const char **textList, int count, const char *delimiter); // Join text strings with delimiter -RLAPI const char **TextSplit(const char *text, char delimiter, int *count); // Split text into multiple strings +RLAPI const char **TextSplit(const char *text, char delimiter, int *count); // Split text into multiple strings RLAPI void TextAppend(char *text, const char *append, int *position); // Append text at specific position and move cursor! RLAPI int TextFindIndex(const char *text, const char *find); // Find first text occurrence within a string RLAPI const char *TextToUpper(const char *text); // Get upper case version of provided string -- cgit v1.2.3 From 9f54a69cec1e6d2927e1998e75e316ce133e4669 Mon Sep 17 00:00:00 2001 From: Rafael Sachetto Date: Fri, 8 Mar 2019 15:06:17 -0300 Subject: Adding DrawCubeWiresV for convenience --- src/models.c | 6 ++++++ src/raylib.h | 1 + 2 files changed, 7 insertions(+) (limited to 'src/raylib.h') diff --git a/src/models.c b/src/models.c index b261f58d..a5dbf5e7 100644 --- a/src/models.c +++ b/src/models.c @@ -285,6 +285,12 @@ void DrawCubeWires(Vector3 position, float width, float height, float length, Co rlPopMatrix(); } +// Draw cube wires (vector version) +void DrawCubeWiresV(Vector3 position, Vector3 size, Color color) +{ + DrawCubeWires(position, size.x, size.y, size.z, color); +} + // Draw cube // NOTE: Cube position is the center position void DrawCubeTexture(Texture2D texture, Vector3 position, float width, float height, float length, Color color) diff --git a/src/raylib.h b/src/raylib.h index cc461204..ccdb4aab 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1207,6 +1207,7 @@ RLAPI void DrawCircle3D(Vector3 center, float radius, Vector3 rotationAxis, floa RLAPI void DrawCube(Vector3 position, float width, float height, float length, Color color); // Draw cube RLAPI void DrawCubeV(Vector3 position, Vector3 size, Color color); // Draw cube (Vector version) RLAPI void DrawCubeWires(Vector3 position, float width, float height, float length, Color color); // Draw cube wires +RLAPI void DrawCubeWiresV(Vector3 position, Vector3 size, Color color); // Draw cube wires (Vector version) RLAPI void DrawCubeTexture(Texture2D texture, Vector3 position, float width, float height, float length, Color color); // Draw cube textured RLAPI void DrawSphere(Vector3 centerPos, float radius, Color color); // Draw sphere RLAPI void DrawSphereEx(Vector3 centerPos, float radius, int rings, int slices, Color color); // Draw sphere with extended parameters -- cgit v1.2.3 From f02a0334d8f86277916c52b274a0c7c21b6b9cd0 Mon Sep 17 00:00:00 2001 From: raysan5 Date: Sun, 17 Mar 2019 11:58:02 +0100 Subject: ADDED: GetScreenData() --- src/raylib.h | 1 + src/textures.c | 16 ++++++++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) (limited to 'src/raylib.h') diff --git a/src/raylib.h b/src/raylib.h index ccdb4aab..c72f0682 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1095,6 +1095,7 @@ RLAPI Color *GetImageData(Image image); RLAPI Vector4 *GetImageDataNormalized(Image image); // Get pixel data from image as Vector4 array (float normalized) RLAPI int GetPixelDataSize(int width, int height, int format); // Get pixel data size in bytes (image or texture) RLAPI Image GetTextureData(Texture2D texture); // Get pixel data from GPU texture and return an Image +RLAPI Image GetScreenData(void); // Get pixel data from screen buffer and return an Image (screenshot) RLAPI void UpdateTexture(Texture2D texture, const void *pixels); // Update GPU texture with new data // Image manipulation functions diff --git a/src/textures.c b/src/textures.c index 7c942f18..38995f11 100644 --- a/src/textures.c +++ b/src/textures.c @@ -740,6 +740,20 @@ Image GetTextureData(Texture2D texture) return image; } +// Get pixel data from GPU frontbuffer and return an Image (screenshot) +RLAPI Image GetScreenData(void) +{ + Image image = { 0 }; + + image.width = GetScreenWidth(); + image.height = GetScreenHeight(); + image.mipmaps = 1; + image.format = UNCOMPRESSED_R8G8B8A8; + image.data = rlReadScreenPixels(image.width, image.height); + + return image; +} + // Update GPU texture with new data // NOTE: pixels data must match texture.format void UpdateTexture(Texture2D texture, const void *pixels) @@ -1168,8 +1182,6 @@ void ImageAlphaPremultiply(Image *image) ImageFormat(image, prevFormat); } - - #if defined(SUPPORT_IMAGE_MANIPULATION) // Load cubemap from image, multiple image cubemap layouts supported TextureCubemap LoadTextureCubemap(Image image, int layoutType) -- cgit v1.2.3 From 88dfd2ab236a28db3cf1e3715b9e98a426de48d6 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 28 Mar 2019 18:53:41 +0100 Subject: REDESIGNED: DrawCircleSector() --- src/raylib.h | 2 +- src/shapes.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 59 insertions(+), 15 deletions(-) (limited to 'src/raylib.h') diff --git a/src/raylib.h b/src/raylib.h index c72f0682..5db50c04 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 af683f2e..5d93078b 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*(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) + { + 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*(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, 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 -- cgit v1.2.3 From ab9c6da26f79796a04ca79401617a7244ec9c2a8 Mon Sep 17 00:00:00 2001 From: Demizdor Date: Fri, 29 Mar 2019 16:22:09 +0200 Subject: Added DrawRing(), DrawRingLines() and DrawCircleSectorLines() --- src/raylib.h | 3 + src/shapes.c | 240 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 243 insertions(+) (limited to 'src/raylib.h') diff --git a/src/raylib.h b/src/raylib.h index 5db50c04..2f3b7be2 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 5d93078b..7e8c0f4b 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) { -- cgit v1.2.3 From a643dc4ca016ce5791608852ab1357f2d94ebe9b Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 29 Mar 2019 16:48:23 +0100 Subject: 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 | 21 +- 3 files changed, 1685 insertions(+), 74 deletions(-) create mode 100644 src/external/tinyobj_loader_c.h (limited to 'src/raylib.h') diff --git a/src/external/tinyobj_loader_c.h b/src/external/tinyobj_loader_c.h new file mode 100644 index 00000000..e9d015ff --- /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 a5dbf5e7..c9d92315 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) - { - 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 + // Check if meshhas vertex data on CPU for testing + if (model->meshes[i].vertices != NULL) { - 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; + + // Test against all triangles in mesh + for (int i = 0; i < triangleCount; i++) + { + Vector3 a, b, c; + Vector3 *vertdata = (Vector3 *)model->meshes[i].vertices; - a = Vector3Transform(a, model->transform); - b = Vector3Transform(b, model->transform); - c = Vector3Transform(c, model->transform); + 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]; + } - RayHitInfo triHitInfo = GetCollisionRayTriangle(ray, a, b, c); + a = Vector3Transform(a, model->transform); + b = Vector3Transform(b, model->transform); + c = Vector3Transform(c, model->transform); - if (triHitInfo.hit) - { - // Save the closest hit triangle - if ((!result.hit) || (result.distance > triHitInfo.distance)) result = triHitInfo; + 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 5db50c04..1a344306 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; - - Material *materials; // Shader and textures data - int materialCount; - - int *meshMaterial; // Material assigned to every mesh - */ + + int meshCount; // Number of meshes + Mesh *meshes; // Meshes array + + 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) -- cgit v1.2.3 From a28023b58f172559228a07da29d8cac417d2e6bd Mon Sep 17 00:00:00 2001 From: Demizdor Date: Sat, 30 Mar 2019 22:18:29 +0200 Subject: Added DrawRoundedRect() --- src/raylib.h | 1 + src/shapes.c | 194 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 195 insertions(+) (limited to 'src/raylib.h') diff --git a/src/raylib.h b/src/raylib.h index 085f36f6..0a8351c0 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 8c1eb1f0..9eb690d3 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) { -- cgit v1.2.3 From 1415d514ba9fc2b21ce99ceb2f21e7d6dc492219 Mon Sep 17 00:00:00 2001 From: Vlad Adrian Date: Sun, 31 Mar 2019 13:20:45 +0300 Subject: Update raylib.h --- src/raylib.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/raylib.h') diff --git a/src/raylib.h b/src/raylib.h index 0a8351c0..1ad719bd 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) -- cgit v1.2.3 From d89d24c5e8930c18a93a6403e26914a9e2b23b84 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 4 Apr 2019 13:33:54 +0200 Subject: BIG UPDATE: Support model animations! --- src/config.h | 6 +- src/models.c | 382 +++++++++++++++++++++++++++++++++++++++++++++++++++++------ src/raylib.h | 45 +++++-- 3 files changed, 383 insertions(+), 50 deletions(-) (limited to 'src/raylib.h') diff --git a/src/config.h b/src/config.h index d45ff707..4174ed71 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 7c266843..632aca2b 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,30 +689,12 @@ void UnloadModel(Model model) free(model.meshes); free(model.materials); free(model.meshMaterial); - - 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 + // Unload animation data + free(model.bones); + free(model.bindPose); - return mesh; + TraceLog(LOG_INFO, "Unloaded model data from RAM and VRAM"); } // Unload mesh from memory (RAM and/or VRAM) @@ -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 085f36f6..9607191b 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 -- cgit v1.2.3 From a103086443b3d3a3a94ec52ef19ec9be68a0069c Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 4 Apr 2019 13:50:52 +0200 Subject: 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(-) (limited to 'src/raylib.h') diff --git a/src/core.c b/src/core.c index 7f2f7f1c..acc87292 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 632aca2b..239ab5d8 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 1c153009..d360b2d3 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 93965038..c365fa47 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 8cc4c590..3f305462 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 38995f11..cd0bd208 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; } -- cgit v1.2.3 From 92733d6695e0cdab3b42972f2cd6ed48d98ec689 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 5 Apr 2019 13:15:56 +0200 Subject: 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. --- examples/models/models_animation.c | 101 +++ examples/models/resources/guy/guy.blend | Bin 0 -> 665304 bytes examples/models/resources/guy/guy.iqm | Bin 0 -> 39408 bytes examples/models/resources/guy/guyanim.iqm | Bin 0 -> 18244 bytes examples/models/resources/guy/guytex.png | Bin 0 -> 302388 bytes examples/others/iqm_loader/models_iqm_animation.c | 98 --- examples/others/iqm_loader/resources/guy.blend | Bin 665304 -> 0 bytes examples/others/iqm_loader/resources/guy.iqm | Bin 39408 -> 0 bytes examples/others/iqm_loader/resources/guyanim.iqm | Bin 18244 -> 0 bytes examples/others/iqm_loader/resources/guytex.png | Bin 302388 -> 0 bytes examples/others/iqm_loader/riqm.h | 739 +--------------------- src/models.c | 652 ++++++++++++++----- src/raylib.h | 33 +- 13 files changed, 617 insertions(+), 1006 deletions(-) create mode 100644 examples/models/models_animation.c create mode 100644 examples/models/resources/guy/guy.blend create mode 100644 examples/models/resources/guy/guy.iqm create mode 100644 examples/models/resources/guy/guyanim.iqm create mode 100644 examples/models/resources/guy/guytex.png delete mode 100644 examples/others/iqm_loader/models_iqm_animation.c delete mode 100644 examples/others/iqm_loader/resources/guy.blend delete mode 100644 examples/others/iqm_loader/resources/guy.iqm delete mode 100644 examples/others/iqm_loader/resources/guyanim.iqm delete mode 100644 examples/others/iqm_loader/resources/guytex.png (limited to 'src/raylib.h') diff --git a/examples/models/models_animation.c b/examples/models/models_animation.c new file mode 100644 index 00000000..a75241b3 --- /dev/null +++ b/examples/models/models_animation.c @@ -0,0 +1,101 @@ +/******************************************************************************************* +* +* raylib [models] example - Load 3d model with animations and play them +* +* 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 Ramon Santamaria (@raysan5) and @culacant +* +********************************************************************************************/ + +#include "raylib.h" + +int main() +{ + // Initialization + //-------------------------------------------------------------------------------------- + int screenWidth = 800; + int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [models] example - model animation"); + + // Define the camera to look into our 3d world + Camera camera = { 0 }; + camera.position = (Vector3){ 10.0f, 10.0f, 10.0f }; // Camera position + camera.target = (Vector3){ 0.0f, 0.0f, 0.0f }; // Camera looking at point + camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Camera up vector (rotation towards target) + camera.fovy = 45.0f; // Camera field-of-view Y + camera.type = CAMERA_PERSPECTIVE; // Camera mode type + + + 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 + int animsCount = 0; + ModelAnimation *anims = LoadModelAnimations("resources/guy/guyanim.iqm", &animsCount); + int animFrameCounter = 0; + + SetCameraMode(camera, CAMERA_FREE); // Set free camera mode + + SetTargetFPS(60); // Set our game to run at 60 frames-per-second + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + UpdateCamera(&camera); + + // Play animation when spacebar is held down + if (IsKeyDown(KEY_SPACE)) + { + animFrameCounter++; + UpdateModelAnimation(model, anims[0], animFrameCounter); + if (animFrameCounter >= anims[0].frameCount) animFrameCounter = 0; + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + BeginMode3D(camera); + + 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 MODEL ANIMATION", 10, 10, 20, MAROON); + DrawText("(c) Guy IQM 3D model by @culacant", screenWidth - 200, screenHeight - 20, 10, GRAY); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + // Unload model animations data + for (int i = 0; i < animsCount; i++) UnloadModelAnimation(anims[i]); + + UnloadModel(model); // Unload model + + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} diff --git a/examples/models/resources/guy/guy.blend b/examples/models/resources/guy/guy.blend new file mode 100644 index 00000000..3880467d Binary files /dev/null and b/examples/models/resources/guy/guy.blend differ diff --git a/examples/models/resources/guy/guy.iqm b/examples/models/resources/guy/guy.iqm new file mode 100644 index 00000000..36bed5e0 Binary files /dev/null and b/examples/models/resources/guy/guy.iqm differ diff --git a/examples/models/resources/guy/guyanim.iqm b/examples/models/resources/guy/guyanim.iqm new file mode 100644 index 00000000..824a68a3 Binary files /dev/null and b/examples/models/resources/guy/guyanim.iqm differ diff --git a/examples/models/resources/guy/guytex.png b/examples/models/resources/guy/guytex.png new file mode 100644 index 00000000..7f813552 Binary files /dev/null and b/examples/models/resources/guy/guytex.png differ diff --git a/examples/others/iqm_loader/models_iqm_animation.c b/examples/others/iqm_loader/models_iqm_animation.c deleted file mode 100644 index 18dd8577..00000000 --- a/examples/others/iqm_loader/models_iqm_animation.c +++ /dev/null @@ -1,98 +0,0 @@ -/******************************************************************************************* -* -* raylib [models] example - Load IQM 3d model with animations and play them -* -* 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) 2018 @culacant and @raysan5 -* -********************************************************************************************/ - -#include "raylib.h" - -#define RIQM_IMPLEMENTATION -#include "riqm.h" - -int main() -{ - // Initialization - //-------------------------------------------------------------------------------------- - int screenWidth = 800; - int screenHeight = 450; - - InitWindow(screenWidth, screenHeight, "raylib [models] example - iqm animation"); - - // Define the camera to look into our 3d world - Camera camera = { 0 }; - camera.position = (Vector3){ 10.0f, 10.0f, 10.0f }; // Camera position - camera.target = (Vector3){ 0.0f, 0.0f, 0.0f }; // Camera looking at point - camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Camera up vector (rotation towards target) - camera.fovy = 45.0f; // Camera field-of-view Y - camera.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! - - // Load animation data - Animation anim = LoadAnimationFromIQM("resources/guyanim.iqm"); - - int animFrameCounter = 0; - - SetCameraMode(camera, CAMERA_FREE); // Set free camera mode - - SetTargetFPS(60); // Set our game to run at 60 frames-per-second - //-------------------------------------------------------------------------------------- - - // Main game loop - while (!WindowShouldClose()) // Detect window close button or ESC key - { - // Update - //---------------------------------------------------------------------------------- - UpdateCamera(&camera); - - // Play animation when spacebar is held down - if (IsKeyDown(KEY_SPACE)) - { - animFrameCounter++; - AnimateModel(model, anim, animFrameCounter); // Animate the model with animation data and frame - } - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - - ClearBackground(RAYWHITE); - - BeginMode3D(camera); - - DrawAnimatedModel(model, Vector3Zero(), 1.0f, WHITE); // Draw animated model - - DrawGrid(10, 1.0f); // Draw a grid - - EndMode3D(); - - DrawText("PRESS SPACE to PLAY IQM MODEL ANIMATION", 10, 10, 20, MAROON); - - DrawText("(c) Guy IQM 3D model by @culacant", screenWidth - 200, screenHeight - 20, 10, GRAY); - - EndDrawing(); - //---------------------------------------------------------------------------------- - } - - // De-Initialization - //-------------------------------------------------------------------------------------- - UnloadAnimation(anim); // Unload animation data - UnloadAnimatedModel(model); // Unload animated model - - CloseWindow(); // Close window and OpenGL context - //-------------------------------------------------------------------------------------- - - return 0; -} diff --git a/examples/others/iqm_loader/resources/guy.blend b/examples/others/iqm_loader/resources/guy.blend deleted file mode 100644 index 3880467d..00000000 Binary files a/examples/others/iqm_loader/resources/guy.blend and /dev/null differ diff --git a/examples/others/iqm_loader/resources/guy.iqm b/examples/others/iqm_loader/resources/guy.iqm deleted file mode 100644 index 36bed5e0..00000000 Binary files a/examples/others/iqm_loader/resources/guy.iqm and /dev/null differ diff --git a/examples/others/iqm_loader/resources/guyanim.iqm b/examples/others/iqm_loader/resources/guyanim.iqm deleted file mode 100644 index 824a68a3..00000000 Binary files a/examples/others/iqm_loader/resources/guyanim.iqm and /dev/null differ diff --git a/examples/others/iqm_loader/resources/guytex.png b/examples/others/iqm_loader/resources/guytex.png deleted file mode 100644 index 7f813552..00000000 Binary files a/examples/others/iqm_loader/resources/guytex.png and /dev/null differ diff --git a/examples/others/iqm_loader/riqm.h b/examples/others/iqm_loader/riqm.h index 694e3a95..41ef8a14 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 239ab5d8..e437a0ab 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; - - int result = tinyobj_parse_mtl_file(&materials, &materialCount, fileName); - - // TODO: Process materials to return + minVertex = (Vector3){ mesh.vertices[0], mesh.vertices[1], mesh.vertices[2] }; + maxVertex = (Vector3){ mesh.vertices[0], mesh.vertices[1], mesh.vertices[2] }; - 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 + 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] }; - return material; + // 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"); } -// Unload material from memory -void UnloadMaterial(Material material) +// Compute mesh binormals (aka bitangent) +void MeshBinormals(Mesh *mesh) { - // 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++) { - if (material.maps[i].texture.id != GetTextureDefault().id) rlDeleteTextures(material.maps[i].texture.id); + 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 c365fa47..55943baf 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) -- cgit v1.2.3 From 9282b8ba83909b353fd34a9f584597338c1928bd Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 5 Apr 2019 17:08:46 +0200 Subject: 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(-) (limited to 'src/raylib.h') diff --git a/examples/models/models_material_pbr.c b/examples/models/models_material_pbr.c index d393a20d..c1d43a24 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 55943baf..dc864b8d 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 bf50ad0d..81f39d68 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) { -- cgit v1.2.3 From f21761fbbb02f0b58b5b54342f0c3ad3abc0003e Mon Sep 17 00:00:00 2001 From: ChillerDragon Date: Sun, 7 Apr 2019 17:49:12 +0200 Subject: 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(-) (limited to 'src/raylib.h') diff --git a/examples/Makefile b/examples/Makefile index e14762b3..b35e39f9 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 7eee46f6..d7fa5d79 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 0581df34..7d387441 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 4b262bbd..42aec2e2 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 5d60f15d..44e053c6 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 6662a6be..b1304656 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 0d236775..eeee7a71 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 ef2a5f74..466d9b91 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 49a0bb6b..4dd13856 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 9fc704c7..a697013e 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 009fbd0a..6acadce5 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 9cc07eab..0ad4f9af 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 8f1934b5..4cd5033e 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 b6c935f6..af9b31c1 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 8482bf2d..25dbe696 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 fbc34aac..3291bb3e 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 8875ad52..4673a549 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 a1b7e764..f62fe080 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 bb355b8b..0492a0cc 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 ee70632a..3dfce714 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 37543302..dc016423 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 1cd2563b..77777c73 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 2a30a6ba..e9023b08 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 be5e31d9..49698f0d 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 91ca28c1..9fc3d802 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 06e74799..703383b8 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 747718fc..3cffaaba 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 e2fe6c5d..0217c198 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 b0c0658d..42db214f 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 a4546eb1..36775333 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 7a9acdf7..07f8203b 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 dc864b8d..dade7aba 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 853c5200..3463bb96 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 81f39d68..3e428241 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 b7f7e3df..fbc70fc4 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 d44cdd11..cd7a6802 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 cd0bd208..5189b635 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 6b174354..10cce9b9 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 08b33962..d7ab8829 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 2e5d6e26..e475f72c 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 48a34f6d..3fb5d657 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 66b5ddf9..62d1267a 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 8943adb5..8ea61491 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 6282e83e..8badbf52 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 dc8d74fa..4b58da13 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 5727546a..8e74a027 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 4d7f9b53..7c2dfb48 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 86e0dbde..9f27a429 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 028b1da8..8f2dc36a 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 b9c6c188..4333c8b8 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 87196977..1bd5ce98 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 7f108265..427e5ba7 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 c0b60571..435ea241 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 9f6690d1..df68dd26 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 328448ba..5bd33825 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 e961b533..9450c29f 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 8871484e..7918562c 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) * ********************************************************************************************/ -- cgit v1.2.3 From 6168a4ca376d26dcac8a19967987280ed399f337 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 10 Apr 2019 23:50:48 +0200 Subject: Comments review --- src/raylib.h | 54 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 28 insertions(+), 26 deletions(-) (limited to 'src/raylib.h') diff --git a/src/raylib.h b/src/raylib.h index dade7aba..e4e41a15 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 -- cgit v1.2.3 From 1934f2a2f404b08a9dd5541a5407262b6fc4bb6a Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 11 Apr 2019 00:11:11 +0200 Subject: Some tweaks --- src/raylib.h | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'src/raylib.h') diff --git a/src/raylib.h b/src/raylib.h index e4e41a15..e8d927cb 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 -- cgit v1.2.3 From 5bfa67535040ed1cb717908d9f259db0c302d9e6 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 11 Apr 2019 13:53:01 +0200 Subject: 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 | 414 ++++++++++------------------------ 5 files changed, 201 insertions(+), 326 deletions(-) create mode 100644 examples/core/resources/distortion.fs (limited to 'src/raylib.h') diff --git a/examples/core/core_vr_simulator.c b/examples/core/core_vr_simulator.c index 3f59e839..65054b73 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 00000000..207a0aff --- /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 dc30eeb2..2ea4b438 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 e8d927cb..73661f58 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 3e428241..5c0d2823 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,14 +3518,90 @@ void UpdateVrTracking(Camera *camera) void CloseVrSimulator(void) { #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - if (vrSimulatorReady) + 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) { - rlDeleteRenderTextures(vrConfig.stereoFbo); // Unload stereo framebuffer and texture - #if defined(SUPPORT_DISTORTION_SHADER) - UnloadShader(vrConfig.distortionShader); // Unload distortion shader - #endif + // 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); } -#endif } // Detect if VR simulator is running @@ -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) { -- cgit v1.2.3 From 30d51ec26ca8f00bd691c570a78b8eb2e41fa817 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 12 Apr 2019 11:29:01 +0200 Subject: Reorganize struct --- src/raylib.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'src/raylib.h') diff --git a/src/raylib.h b/src/raylib.h index 73661f58..4162454e 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 -- cgit v1.2.3 From 4e58d4102c0f466730971e7f7cd685b7359343ab Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 12 Apr 2019 13:29:37 +0200 Subject: Corrected typo --- src/raylib.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/raylib.h') diff --git a/src/raylib.h b/src/raylib.h index 4162454e..6d42cfd1 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 -- cgit v1.2.3 From 7cc8faf7daa1e3d0f742bbc118b9705b0546e072 Mon Sep 17 00:00:00 2001 From: Björn Wahlstrand Date: Fri, 12 Apr 2019 21:41:30 +0200 Subject: Use typedef rather than #define in order to avoid issues in application code --- src/raylib.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/raylib.h') diff --git a/src/raylib.h b/src/raylib.h index 6d42cfd1..0afa69d0 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 { -- cgit v1.2.3 From f3a5a6871d4ec005026817e5f2579a6f55938dc2 Mon Sep 17 00:00:00 2001 From: Demizdor Date: Sun, 21 Apr 2019 12:27:46 +0300 Subject: Initial unicode implementation for UTF8 encoded text --- src/raylib.h | 2 + src/text.c | 205 +++++++++++++++++++++++++++++++++++++++++------------------ 2 files changed, 145 insertions(+), 62 deletions(-) (limited to 'src/raylib.h') diff --git a/src/raylib.h b/src/raylib.h index 0afa69d0..6a5f0ef8 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1190,11 +1190,13 @@ RLAPI void DrawTextRecEx(Font font, const char *text, Rectangle rec, float fontS RLAPI int MeasureText(const char *text, int fontSize); // Measure string width for default font RLAPI Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing); // Measure string size for Font RLAPI int GetGlyphIndex(Font font, int character); // Get index position for a unicode character on font +RLAPI int GetNextCodepoint(const char* text, int* count); // Returns next codepoint in a UTF8 encoded `text` or 0x3f(`?`) on failure. `count` will hold the total number of bytes processed. // Text strings management functions // NOTE: Some strings allocate memory internally for returned strings, just be careful! RLAPI bool TextIsEqual(const char *text1, const char *text2); // Check if two text string are equal RLAPI unsigned int TextLength(const char *text); // Get text length, checks for '\0' ending +RLAPI unsigned int TextCountCodepoints(const char *text); // Get total number of characters(codepoints) in a UTF8 encoded `text` until '\0' is found. RLAPI const char *TextFormat(const char *text, ...); // Text formatting with variables (sprintf style) RLAPI const char *TextSubtext(const char *text, int position, int length); // Get a piece of a text string RLAPI const char *TextReplace(char *text, const char *replace, const char *by); // Replace text string (memory should be freed!) diff --git a/src/text.c b/src/text.c index c07f807a..bd9e09f0 100644 --- a/src/text.c +++ b/src/text.c @@ -719,6 +719,97 @@ void DrawFPS(int posX, int posY) DrawText(TextFormat("%2i FPS", fps), posX, posY, 20, LIME); } +// Returns next codepoint in a UTF8 encoded `text` scanning until '\0' is found. When a invalid UTF8 byte is encountered we exit as soon +// as possible and a `?`(0x3f) codepoint is returned. `count` will hold the total number of bytes processed. +// NOTE: the standard says U+FFFD should be returned in case of errors but that character is not supported by the default font in raylib +// TODO: optimize this code for speed!! +int GetNextCodepoint(const char* text, int* count) +{ +/* + UTF8 specs from https://www.ietf.org/rfc/rfc3629.txt + + Char. number range | UTF-8 octet sequence + (hexadecimal) | (binary) + --------------------+--------------------------------------------- + 0000 0000-0000 007F | 0xxxxxxx + 0000 0080-0000 07FF | 110xxxxx 10xxxxxx + 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx + 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx +*/ + + // NOTE: on decode errors we return as soon as possible + + int c = 0x3f; // Codepoint (defaults to `?`) + int o = (unsigned char)(text[0]); // The first UTF8 octet + *count = 1; + + if( o <= 0x7f ) + { + // Only one octet (ASCII range x00-7F) + c = text[0]; + } + else if((o & 0xe0) == 0xc0) + { + // Two octets + // [0]xC2-DF [1]UTF8-tail(x80-BF) + unsigned char o1 = text[1]; + if(o1 == '\0' || (o1 >> 6) != 2 ) {*count = 2; return c; } // Unexpected sequence + if(o >= 0xc2 && o <= 0xdf) + { + c = ((o & 0x1f) << 6) | (o1 & 0x3f); + *count = 2; + } + } + else if( (o & 0xf0) == 0xe0 ) + { + // Three octets + unsigned char o1 = text[1], o2 = '\0'; + if(o1 == '\0' || (o1 >> 6) != 2) { *count = 2; return c; } // Unexpected sequence + o2 = text[2]; + if(o2 == '\0' || (o2 >> 6) != 2) {*count = 3; return c; } // Unexpected sequence + + /* [0]xE0 [1]xA0-BF [2]UTF8-tail(x80-BF) + [0]xE1-EC [1]UTF8-tail [2]UTF8-tail(x80-BF) + [0]xED [1]x80-9F [2]UTF8-tail(x80-BF) + [0]xEE-EF [1]UTF8-tail [2]UTF8-tail(x80-BF) + */ + + if((o == 0xe0 && !(o1 >= 0xa0 && o1 <= 0xbf)) || (o == 0xed && !(o1 >= 0x80 && o1 <= 0x9f)) ) {*count = 2; return c;} + if(o >= 0xe0 && 0 <= 0xef) + { + c = ((o & 0xf) << 12) | ((o1 & 0x3f) << 6) | (o2 & 0x3f); + *count = 3; + } + } + else if( (o & 0xf8) == 0xf0 ) + { + // Four octets + if(o > 0xf4) return c; + + unsigned char o1 = text[1], o2 = '\0', o3 = '\0'; + if(o1 == '\0' || (o1 >> 6) != 2) { *count = 2; return c; } // Unexpected sequence + o2 = text[2]; + if(o2 == '\0' || (o2 >> 6) != 2) { *count = 3; return c; } // Unexpected sequence + o3 = text[3]; + if(o3 == '\0' || (o3 >> 6) != 2) { *count = 4; return c; } // Unexpected sequence + + /* [0]xF0 [1]x90-BF [2]UTF8-tail [3]UTF8-tail + [0]xF1-F3 [1]UTF8-tail [2]UTF8-tail [3]UTF8-tail + [0]xF4 [1]x80-8F [2]UTF8-tail [3]UTF8-tail + */ + if((o == 0xf0 && !(o1 >= 0x90 && o1 <= 0xbf)) || (o == 0xf4 && !( o1 >= 0x80 && o1 <= 0x8f)) ) { *count = 2; return c; } // Unexpected sequence + if( o >= 0xf0) + { + c = ((o & 0x7) << 18) | ((o1 & 0x3f) << 12) | ((o2 & 0x3f) << 6) | (o3 & 0x3f); + *count = 4; + } + } + + if(c > 0x10ffff) c = 0x3f; // Codepoints after U+10ffff are invalid + return c; +} + + // Draw text (using default font) // NOTE: fontSize work like in any drawing program but if fontSize is lower than font-base-size, then font-base-size is used // NOTE: chars spacing is proportional to fontSize @@ -746,17 +837,22 @@ void DrawTextEx(Font font, const char *text, Vector2 position, float fontSize, f int textOffsetY = 0; // Required for line break! float scaleFactor = 0.0f; - unsigned char letter = 0; // Current character + int letter = 0; // Current character int index = 0; // Index position in sprite font scaleFactor = fontSize/font.baseSize; - // NOTE: Some ugly hacks are made to support Latin-1 Extended characters directly - // written in C code files (codified by default as UTF-8) - for (int i = 0; i < length; i++) { - if ((unsigned char)text[i] == '\n') + int next = 1; + letter = GetNextCodepoint(&text[i], &next); + // NOTE: normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f) + // but we need to draw all of the bad bytes using the '?' symbol so to not skip any we set `next = 1` + if(letter == 0x3f) next = 1; + index = GetGlyphIndex(font, letter); + i += next - 1; + + if (letter == '\n') { // NOTE: Fixed line spacing of 1.5 lines textOffsetY += (int)((font.baseSize + font.baseSize/2)*scaleFactor); @@ -764,23 +860,7 @@ void DrawTextEx(Font font, const char *text, Vector2 position, float fontSize, f } else { - if ((unsigned char)text[i] == 0xc2) // UTF-8 encoding identification HACK! - { - // Support UTF-8 encoded values from [0xc2 0x80] -> [0xc2 0xbf](¿) - letter = (unsigned char)text[i + 1]; - index = GetGlyphIndex(font, (int)letter); - i++; - } - else if ((unsigned char)text[i] == 0xc3) // UTF-8 encoding identification HACK! - { - // Support UTF-8 encoded values from [0xc3 0x80](À) -> [0xc3 0xbf](ÿ) - letter = (unsigned char)text[i + 1]; - index = GetGlyphIndex(font, (int)letter + 64); - i++; - } - else index = GetGlyphIndex(font, (unsigned char)text[i]); - - if ((unsigned char)text[i] != ' ') + if (letter != ' ') { DrawTexturePro(font.texture, font.chars[index].rec, (Rectangle){ position.x + textOffsetX + font.chars[index].offsetX*scaleFactor, @@ -810,7 +890,7 @@ void DrawTextRecEx(Font font, const char *text, Rectangle rec, float fontSize, f int textOffsetY = 0; // Required for line break! float scaleFactor = 0.0f; - unsigned char letter = 0; // Current character + int letter = 0; // Current character int index = 0; // Index position in sprite font scaleFactor = fontSize/font.baseSize; @@ -823,26 +903,16 @@ void DrawTextRecEx(Font font, const char *text, Rectangle rec, float fontSize, f for (int i = 0; i < length; i++) { int glyphWidth = 0; - letter = (unsigned char)text[i]; + int next = 1; + letter = GetNextCodepoint(&text[i], &next); + // NOTE: normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f) + // but we need to draw all of the bad bytes using the '?' symbol so to not skip any we set `next = 1` + if(letter == 0x3f) next = 1; + index = GetGlyphIndex(font, letter); + i += next - 1; if (letter != '\n') - { - if ((unsigned char)text[i] == 0xc2) // UTF-8 encoding identification HACK! - { - // Support UTF-8 encoded values from [0xc2 0x80] -> [0xc2 0xbf](¿) - letter = (unsigned char)text[i + 1]; - index = GetGlyphIndex(font, (int)letter); - i++; - } - else if ((unsigned char)text[i] == 0xc3) // UTF-8 encoding identification HACK! - { - // Support UTF-8 encoded values from [0xc3 0x80](À) -> [0xc3 0xbf](ÿ) - letter = (unsigned char)text[i + 1]; - index = GetGlyphIndex(font, (int)letter + 64); - i++; - } - else index = GetGlyphIndex(font, (unsigned char)text[i]); - + { glyphWidth = (font.chars[index].advanceX == 0)? (int)(font.chars[index].rec.width*scaleFactor + spacing): (int)(font.chars[index].advanceX*scaleFactor + spacing); @@ -858,13 +928,15 @@ void DrawTextRecEx(Font font, const char *text, Rectangle rec, float fontSize, f // the container. if (state == MEASURE_STATE) { + // TODO: there are multiple types of `spaces` in UNICODE, maybe it's a good idea to add support for more + // see: http://jkorpela.fi/chars/spaces.html if ((letter == ' ') || (letter == '\t') || (letter == '\n')) endLine = i; if ((textOffsetX + glyphWidth + 1) >= rec.width) { endLine = (endLine < 1)? i : endLine; - if (i == endLine) endLine -= 1; - if ((startLine + 1) == endLine) endLine = i - 1; + if (i == endLine) endLine -= next; + if ((startLine + next) == endLine) endLine = i - next; state = !state; } else if ((i + 1) == length) @@ -972,31 +1044,23 @@ Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing float textHeight = (float)font.baseSize; float scaleFactor = fontSize/(float)font.baseSize; - unsigned char letter = 0; // Current character + int letter = 0; // Current character int index = 0; // Index position in sprite font for (int i = 0; i < len; i++) { lenCounter++; - - if (text[i] != '\n') + + int next = 1; + letter = GetNextCodepoint(&text[i], &next); + // NOTE: normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f) + // but we need to draw all of the bad bytes using the '?' symbol so to not skip any we set `next = 1` + if(letter == 0x3f) next = 1; + i += next - 1; + + if (letter != '\n') { - if ((unsigned char)text[i] == 0xc2) // UTF-8 encoding identification - { - // Support UTF-8 encoded values from [0xc2 0x80] -> [0xc2 0xbf](¿) - letter = (unsigned char)text[i + 1]; - index = GetGlyphIndex(font, (int)letter); - i++; - } - else if ((unsigned char)text[i] == 0xc3) // UTF-8 encoding identification - { - // Support UTF-8 encoded values from [0xc3 0x80](À) -> [0xc3 0xbf](ÿ) - letter = (unsigned char)text[i + 1]; - index = GetGlyphIndex(font, (int)letter + 64); - i++; - } - else index = GetGlyphIndex(font, (unsigned char)text[i]); - + index = GetGlyphIndex(font, letter); if (font.chars[index].advanceX != 0) textWidth += font.chars[index].advanceX; else textWidth += (font.chars[index].rec.width + font.chars[index].offsetX); } @@ -1065,6 +1129,23 @@ unsigned int TextLength(const char *text) return length; } +// Returns total number of characters(codepoints) in a UTF8 encoded `text` until `\0` is found. +// NOTE: If a invalid UTF8 sequence is encountered a `?`(0x3f) codepoint is counted instead. +unsigned int TextCountCodepoints(const char *text) +{ + unsigned int len = 0; + char* ptr = (char*)&text[0]; + while(*ptr != '\0') + { + int next = 0; + int letter = GetNextCodepoint(ptr, &next); + if(letter == 0x3f) ptr += 1; + else ptr += next; + ++len; + } + return len; +} + // Formatting of text with variables to 'embed' const char *TextFormat(const char *text, ...) { -- cgit v1.2.3 From 4b8d06f50139b33d7670c286e2e3f006843ac8ba Mon Sep 17 00:00:00 2001 From: Jak <5613046+Syphonx@users.noreply.github.com> Date: Mon, 22 Apr 2019 19:03:00 +0100 Subject: [rnet] module WIP (#809) Added experimental network module --- .gitignore | 1 + examples/network/network_ping_pong.c | 225 ++++ examples/network/network_resolve_host.c | 80 ++ examples/network/network_tcp_client.c | 151 +++ examples/network/network_tcp_server.c | 165 +++ examples/network/network_test.c | 148 +++ examples/network/network_udp_client.c | 128 ++ examples/network/network_udp_server.c | 134 ++ src/raylib.h | 176 +++ src/rnet.c | 2023 +++++++++++++++++++++++++++++++ src/rnet.h | 228 ++++ 11 files changed, 3459 insertions(+) create mode 100644 examples/network/network_ping_pong.c create mode 100644 examples/network/network_resolve_host.c create mode 100644 examples/network/network_tcp_client.c create mode 100644 examples/network/network_tcp_server.c create mode 100644 examples/network/network_test.c create mode 100644 examples/network/network_udp_client.c create mode 100644 examples/network/network_udp_server.c create mode 100644 src/rnet.c create mode 100644 src/rnet.h (limited to 'src/raylib.h') diff --git a/.gitignore b/.gitignore index 963342a0..08e661af 100644 --- a/.gitignore +++ b/.gitignore @@ -133,3 +133,4 @@ templates/android_project/output GPATH GRTAGS GTAGS +.vs diff --git a/examples/network/network_ping_pong.c b/examples/network/network_ping_pong.c new file mode 100644 index 00000000..719f6739 --- /dev/null +++ b/examples/network/network_ping_pong.c @@ -0,0 +1,225 @@ +/******************************************************************************************* + * + * raylib [network] example - Client/Server ping-pong + * + * Welcome to raylib! + * + * To test examples, just press F6 and execute raylib_compile_execute script + * Note that compiled executable is placed in the same folder as .c file + * + * You can find all basic examples on C:\raylib\raylib\examples folder or + * raylib official webpage: www.raylib.com + * + * Enjoy using raylib. :) + * + * 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) 2013-2016 Ramon Santamaria (@raysan5) + * + ********************************************************************************************/ + +#include "raylib.h" +#include "rnet.h" + +#include +#include +#include + +float elapsed = 0.0f; +float delay = 1.0f; +bool ping = false; +bool pong = false; +bool connected = false; +bool client_connected = false; +const char * pingmsg = "Ping!"; +const char * pongmsg = "Pong!"; +int msglen = 0; +SocketConfig server_cfg = {.host = "127.0.0.1", .port = "4950", .type = SOCKET_TCP, .server = true, .nonblocking = true}; +SocketConfig client_cfg = {.host = "127.0.0.1", .port = "4950", .type = SOCKET_TCP, .nonblocking = true}; +SocketConfig connection_cfg = {.nonblocking = true}; +SocketResult *server_res = NULL; +SocketResult *client_res = NULL; +SocketSet * socket_set = NULL; +Socket * connection = NULL; +char recvBuffer[512]; + +// Attempt to connect to the network (Either TCP, or UDP) +void NetworkConnect() +{ + // If the server is configured as UDP, ignore connection requests + if (server_cfg.type == SOCKET_UDP && client_cfg.type == SOCKET_UDP) { + ping = true; + connected = true; + } else { + // If the client is connected, run the server code to check for a connection + if (client_connected) { + int active = CheckSockets(socket_set, 0); + if (active != 0) { + TraceLog(LOG_DEBUG, + "There are currently %d socket(s) with data to be processed.", active); + } + if (active > 0) { + if ((connection = SocketAccept(server_res->socket, &connection_cfg)) != NULL) { + AddSocket(socket_set, connection); + ping = true; + connected = true; + } + } + } else { + // Check if we're connected every _delay_ seconds + elapsed += GetFrameTime(); + if (elapsed > delay) { + if (IsSocketConnected(client_res->socket)) { + client_connected = true; + } + elapsed = 0.0f; + } + } + } +} + +// Once connected to the network, check the sockets for pending information +// and when information is ready, send either a Ping or a Pong. +void NetworkUpdate() +{ + // CheckSockets + // + // If any of the sockets in the socket_set are pending (received data, or requests) + // then mark the socket as being ready. You can check this with IsSocketReady(client_res->socket) + int active = CheckSockets(socket_set, 0); + if (active != 0) { + TraceLog(LOG_DEBUG, + "There are currently %d socket(s) with data to be processed.", active); + } + + // IsSocketReady + // + // If the socket is ready, attempt to receive data from the socket + int bytesRecv = 0; + if (server_cfg.type == SOCKET_UDP && client_cfg.type == SOCKET_UDP) { + if (IsSocketReady(client_res->socket)) { + bytesRecv = SocketReceive(client_res->socket, recvBuffer, msglen); + } + if (IsSocketReady(server_res->socket)) { + bytesRecv = SocketReceive(server_res->socket, recvBuffer, msglen); + } + } else { + if (IsSocketReady(connection)) { + bytesRecv = SocketReceive(connection, recvBuffer, msglen); + } + } + + // If we received data, was that data a "Ping!" or a "Pong!" + if (bytesRecv > 0) { + if (strcmp(recvBuffer, pingmsg) == 0) { pong = true; } + if (strcmp(recvBuffer, pongmsg) == 0) { ping = true; } + } + + // After each delay has expired, send a response "Ping!" for a "Pong!" and vice versa + elapsed += GetFrameTime(); + if (elapsed > delay) { + if (ping) { + ping = false; + if (server_cfg.type == SOCKET_UDP && client_cfg.type == SOCKET_UDP) { + SocketSend(client_res->socket, pingmsg, msglen); + } else { + SocketSend(client_res->socket, pingmsg, msglen); + } + } else if (pong) { + pong = false; + if (server_cfg.type == SOCKET_UDP && client_cfg.type == SOCKET_UDP) { + SocketSend(client_res->socket, pongmsg, msglen); + } else { + SocketSend(client_res->socket, pongmsg, msglen); + } + } + elapsed = 0.0f; + } +} + +int main() +{ + // Setup + int screenWidth = 800; + int screenHeight = 450; + InitWindow( + screenWidth, screenHeight, "raylib [network] example - ping pong"); + SetTargetFPS(60); + SetTraceLogLevel(LOG_DEBUG); + + // Networking + InitNetwork(); + + // Create the server + // + // Performs + // getaddrinfo + // socket + // setsockopt + // bind + // listen + server_res = AllocSocketResult(); + if (!SocketCreate(&server_cfg, server_res)) { + TraceLog(LOG_WARNING, "Failed to open server: status %d, errno %d", + server_res->status, server_res->socket->status); + } else { + if (!SocketBind(&server_cfg, server_res)) { + TraceLog(LOG_WARNING, "Failed to bind server: status %d, errno %d", + server_res->status, server_res->socket->status); + } else { + if (!(server_cfg.type == SOCKET_UDP)) { + if (!SocketListen(&server_cfg, server_res)) { + TraceLog(LOG_WARNING, + "Failed to start listen server: status %d, errno %d", + server_res->status, server_res->socket->status); + } + } + } + } + + // Create the client + // + // Performs + // getaddrinfo + // socket + // setsockopt + // connect (TCP only) + client_res = AllocSocketResult(); + if (!SocketCreate(&client_cfg, client_res)) { + TraceLog(LOG_WARNING, "Failed to open client: status %d, errno %d", + client_res->status, client_res->socket->status); + } else { + if (!(client_cfg.type == SOCKET_UDP)) { + if (!SocketConnect(&client_cfg, client_res)) { + TraceLog(LOG_WARNING, + "Failed to connect to server: status %d, errno %d", + client_res->status, client_res->socket->status); + } + } + } + + // Create & Add sockets to the socket set + socket_set = AllocSocketSet(3); + msglen = strlen(pingmsg) + 1; + memset(recvBuffer, '\0', sizeof(recvBuffer)); + AddSocket(socket_set, server_res->socket); + AddSocket(socket_set, client_res->socket); + + // Main game loop + while (!WindowShouldClose()) { + BeginDrawing(); + ClearBackground(RAYWHITE); + if (connected) { + NetworkUpdate(); + } else { + NetworkConnect(); + } + EndDrawing(); + } + + // Cleanup + CloseWindow(); + return 0; +} \ No newline at end of file diff --git a/examples/network/network_resolve_host.c b/examples/network/network_resolve_host.c new file mode 100644 index 00000000..195e03b5 --- /dev/null +++ b/examples/network/network_resolve_host.c @@ -0,0 +1,80 @@ +/******************************************************************************************* + * + * raylib [network] example - Resolve Host + * + * Welcome to raylib! + * + * To test examples, just press F6 and execute raylib_compile_execute script + * Note that compiled executable is placed in the same folder as .c file + * + * You can find all basic examples on C:\raylib\raylib\examples folder or + * raylib official webpage: www.raylib.com + * + * Enjoy using raylib. :) + * + * 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) 2013-2016 Ramon Santamaria (@raysan5) + * + ********************************************************************************************/ + +#include "raylib.h" +#include "rnet.h" + +char buffer[ADDRESS_IPV6_ADDRSTRLEN]; +uint16_t port = 0; + +int main() +{ + // Setup + int screenWidth = 800; + int screenHeight = 450; + InitWindow( + screenWidth, screenHeight, "raylib [network] example - ping pong"); + SetTargetFPS(60); + + SetTraceLogLevel(LOG_DEBUG); + + // Networking + InitNetwork(); + + AddressInformation* addr = AllocAddressList(1); + int count = ResolveHost( + NULL, + "5210", + ADDRESS_TYPE_IPV4, + 0 // Uncomment any of these flags + // ADDRESS_INFO_NUMERICHOST // or try them in conjunction to + // ADDRESS_INFO_NUMERICSERV // specify custom behaviour from + // ADDRESS_INFO_DNS_ONLY // the function getaddrinfo() + // ADDRESS_INFO_ALL // + // ADDRESS_INFO_FQDN // e.g. ADDRESS_INFO_CANONNAME | ADDRESS_INFO_NUMERICSERV + , + addr + ); + + if (count > 0) + { + GetAddressHostAndPort(addr[0], buffer, &port); + TraceLog(LOG_INFO, "Resolved to ip %s::%d\n", buffer, port); + } + + // Main game loop + while (!WindowShouldClose()) + { + // Draw + BeginDrawing(); + + // Clear + ClearBackground(RAYWHITE); + + // End draw + EndDrawing(); + } + + // Cleanup + CloseWindow(); + return 0; +} \ No newline at end of file diff --git a/examples/network/network_tcp_client.c b/examples/network/network_tcp_client.c new file mode 100644 index 00000000..6eed205a --- /dev/null +++ b/examples/network/network_tcp_client.c @@ -0,0 +1,151 @@ +/******************************************************************************************* + * + * raylib [network] example - TCP Client + * + * Welcome to raylib! + * + * To test examples, just press F6 and execute raylib_compile_execute script + * Note that compiled executable is placed in the same folder as .c file + * + * You can find all basic examples on C:\raylib\raylib\examples folder or + * raylib official webpage: www.raylib.com + * + * Enjoy using raylib. :) + * + * 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) 2013-2016 Ramon Santamaria (@raysan5) + * + ********************************************************************************************/ + +#include "raylib.h" +#include "rnet.h" + +#include +#include +#include + +float elapsed = 0.0f; +float delay = 1.0f; +bool ping = false; +bool pong = false; +bool connected = false; +const char * pingmsg = "Ping!"; +const char * pongmsg = "Pong!"; +int msglen = 0; +SocketConfig client_cfg = {.host = "127.0.0.1", .port = "4950", .type = SOCKET_TCP, .nonblocking = true}; +SocketResult *client_res = NULL; +SocketSet * socket_set = NULL; +char recvBuffer[512]; + +// Attempt to connect to the network (Either TCP, or UDP) +void NetworkConnect() +{ + // Check if we're connected every _delay_ seconds + elapsed += GetFrameTime(); + if (elapsed > delay) { + if (IsSocketConnected(client_res->socket)) { connected = true; } + elapsed = 0.0f; + } +} + +// Once connected to the network, check the sockets for pending information +// and when information is ready, send either a Ping or a Pong. +void NetworkUpdate() +{ + // CheckSockets + // + // If any of the sockets in the socket_set are pending (received data, or requests) + // then mark the socket as being ready. You can check this with IsSocketReady(client_res->socket) + int active = CheckSockets(socket_set, 0); + if (active != 0) { + TraceLog(LOG_DEBUG, + "There are currently %d socket(s) with data to be processed.", active); + } + + // IsSocketReady + // + // If the socket is ready, attempt to receive data from the socket + int bytesRecv = 0; + if (IsSocketReady(client_res->socket)) { + bytesRecv = SocketReceive(client_res->socket, recvBuffer, msglen); + } + + // If we received data, was that data a "Ping!" or a "Pong!" + if (bytesRecv > 0) { + if (strcmp(recvBuffer, pingmsg) == 0) { pong = true; } + if (strcmp(recvBuffer, pongmsg) == 0) { ping = true; } + } + + // After each delay has expired, send a response "Ping!" for a "Pong!" and vice versa + elapsed += GetFrameTime(); + if (elapsed > delay) { + if (ping) { + ping = false; + SocketSend(client_res->socket, pingmsg, msglen); + } else if (pong) { + pong = false; + SocketSend(client_res->socket, pongmsg, msglen); + } + elapsed = 0.0f; + } +} + +int main() +{ + // Setup + int screenWidth = 800; + int screenHeight = 450; + InitWindow( + screenWidth, screenHeight, "raylib [network] example - tcp client"); + SetTargetFPS(60); + SetTraceLogLevel(LOG_DEBUG); + + // Networking + InitNetwork(); + + // Create the client + // + // Performs + // getaddrinfo + // socket + // setsockopt + // connect (TCP only) + client_res = AllocSocketResult(); + if (!SocketCreate(&client_cfg, client_res)) { + TraceLog(LOG_WARNING, "Failed to open client: status %d, errno %d", + client_res->status, client_res->socket->status); + } else { + if (!(client_cfg.type == SOCKET_UDP)) { + if (!SocketConnect(&client_cfg, client_res)) { + TraceLog(LOG_WARNING, + "Failed to connect to server: status %d, errno %d", + client_res->status, client_res->socket->status); + } + } + } + + // Create & Add sockets to the socket set + socket_set = AllocSocketSet(1); + msglen = strlen(pingmsg) + 1; + memset(recvBuffer, '\0', sizeof(recvBuffer)); + AddSocket(socket_set, client_res->socket); + + // Main game loop + while (!WindowShouldClose()) { + BeginDrawing(); + ClearBackground(RAYWHITE); + if (connected) { + NetworkUpdate(); + } else { + NetworkConnect(); + } + EndDrawing(); + } + + // Cleanup + CloseWindow(); + return 0; +} \ No newline at end of file diff --git a/examples/network/network_tcp_server.c b/examples/network/network_tcp_server.c new file mode 100644 index 00000000..89e9c181 --- /dev/null +++ b/examples/network/network_tcp_server.c @@ -0,0 +1,165 @@ +/******************************************************************************************* + * + * raylib [network] example - TCP Server + * + * Welcome to raylib! + * + * To test examples, just press F6 and execute raylib_compile_execute script + * Note that compiled executable is placed in the same folder as .c file + * + * You can find all basic examples on C:\raylib\raylib\examples folder or + * raylib official webpage: www.raylib.com + * + * Enjoy using raylib. :) + * + * 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) 2013-2016 Ramon Santamaria (@raysan5) + * + ********************************************************************************************/ + +#include "raylib.h" +#include "rnet.h" + +#include +#include +#include + +float elapsed = 0.0f; +float delay = 1.0f; +bool ping = false; +bool pong = false; +bool connected = false; +const char * pingmsg = "Ping!"; +const char * pongmsg = "Pong!"; +int msglen = 0; +SocketConfig server_cfg = {.host = "127.0.0.1", .port = "4950", .type = SOCKET_TCP, .server = true, .nonblocking = true}; +SocketConfig connection_cfg = {.nonblocking = true}; +SocketResult *server_res = NULL; +SocketSet * socket_set = NULL; +Socket * connection = NULL; +char recvBuffer[512]; + +// Attempt to connect to the network (Either TCP, or UDP) +void NetworkConnect() +{ + int active = CheckSockets(socket_set, 0); + if (active != 0) { + TraceLog(LOG_DEBUG, + "There are currently %d socket(s) with data to be processed.", active); + } + if (active > 0) { + if ((connection = SocketAccept(server_res->socket, &connection_cfg)) != NULL) { + AddSocket(socket_set, connection); + ping = true; + connected = true; + } + } +} + +// Once connected to the network, check the sockets for pending information +// and when information is ready, send either a Ping or a Pong. +void NetworkUpdate() +{ + // CheckSockets + // + // If any of the sockets in the socket_set are pending (received data, or requests) + // then mark the socket as being ready. You can check this with IsSocketReady(client_res->socket) + int active = CheckSockets(socket_set, 0); + if (active != 0) { + TraceLog(LOG_DEBUG, + "There are currently %d socket(s) with data to be processed.", active); + } + + // IsSocketReady + // + // If the socket is ready, attempt to receive data from the socket + int bytesRecv = 0; + if (IsSocketReady(connection)) { + bytesRecv = SocketReceive(connection, recvBuffer, msglen); + } + + // If we received data, was that data a "Ping!" or a "Pong!" + if (bytesRecv > 0) { + if (strcmp(recvBuffer, pingmsg) == 0) { pong = true; } + if (strcmp(recvBuffer, pongmsg) == 0) { ping = true; } + } + + // After each delay has expired, send a response "Ping!" for a "Pong!" and vice versa + elapsed += GetFrameTime(); + if (elapsed > delay) { + if (ping) { + ping = false; + SocketSend(connection, pingmsg, msglen); + } else if (pong) { + pong = false; + SocketSend(connection, pongmsg, msglen); + } + elapsed = 0.0f; + } +} + +int main() +{ + // Setup + int screenWidth = 800; + int screenHeight = 450; + InitWindow( + screenWidth, screenHeight, "raylib [network] example - tcp server"); + SetTargetFPS(60); + SetTraceLogLevel(LOG_DEBUG); + + // Networking + InitNetwork(); + + // Create the server + // + // Performs + // getaddrinfo + // socket + // setsockopt + // bind + // listen + server_res = AllocSocketResult(); + if (!SocketCreate(&server_cfg, server_res)) { + TraceLog(LOG_WARNING, "Failed to open server: status %d, errno %d", + server_res->status, server_res->socket->status); + } else { + if (!SocketBind(&server_cfg, server_res)) { + TraceLog(LOG_WARNING, "Failed to bind server: status %d, errno %d", + server_res->status, server_res->socket->status); + } else { + if (!(server_cfg.type == SOCKET_UDP)) { + if (!SocketListen(&server_cfg, server_res)) { + TraceLog(LOG_WARNING, + "Failed to start listen server: status %d, errno %d", + server_res->status, server_res->socket->status); + } + } + } + } + + // Create & Add sockets to the socket set + socket_set = AllocSocketSet(2); + msglen = strlen(pingmsg) + 1; + memset(recvBuffer, '\0', sizeof(recvBuffer)); + AddSocket(socket_set, server_res->socket); + + // Main game loop + while (!WindowShouldClose()) { + BeginDrawing(); + ClearBackground(RAYWHITE); + if (connected) { + NetworkUpdate(); + } else { + NetworkConnect(); + } + EndDrawing(); + } + + // Cleanup + CloseWindow(); + return 0; +} \ No newline at end of file diff --git a/examples/network/network_test.c b/examples/network/network_test.c new file mode 100644 index 00000000..f18a8b13 --- /dev/null +++ b/examples/network/network_test.c @@ -0,0 +1,148 @@ +/******************************************************************************************* + * + * raylib [network] example - Network Test + * + * Welcome to raylib! + * + * To test examples, just press F6 and execute raylib_compile_execute script + * Note that compiled executable is placed in the same folder as .c file + * + * You can find all basic examples on C:\raylib\raylib\examples folder or + * raylib official webpage: www.raylib.com + * + * Enjoy using raylib. :) + * + * 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) 2013-2016 Ramon Santamaria (@raysan5) + * + ********************************************************************************************/ + +#include "raylib.h" +#include "rnet.h" + +#include + +void test_network_initialise() +{ + assert(InitNetwork() == true); +} + +void test_socket_result() +{ + SocketResult *result = AllocSocketResult(); + assert(result != NULL); + FreeSocketResult(&result); + assert(result == NULL); +} + +void test_socket() +{ + Socket *socket = AllocSocket(); + assert(socket != NULL); + FreeSocket(&socket); + assert(socket == NULL); +} + +void test_resolve_ip() +{ + const char *host = "8.8.8.8"; + const char *port = "8080"; + char ip[ADDRESS_IPV6_ADDRSTRLEN]; + char service[ADDRESS_MAXSERV]; + + memset(ip, '\0', ADDRESS_IPV6_ADDRSTRLEN); + ResolveIP(host, port, NAME_INFO_NUMERICHOST, ip, service); + TraceLog(LOG_INFO, "Resolved %s to %s", host, ip); + assert(strcmp(ip, "8.8.8.8") == 0); + + memset(ip, '\0', ADDRESS_IPV6_ADDRSTRLEN); + ResolveIP(host, port, NAME_INFO_DEFAULT, ip, service); + TraceLog(LOG_INFO, "Resolved %s to %s", host, ip); + assert(strcmp(ip, "google-public-dns-a.google.com") == 0); + + memset(ip, '\0', ADDRESS_IPV6_ADDRSTRLEN); + ResolveIP(host, port, NAME_INFO_NOFQDN, ip, service); + TraceLog(LOG_INFO, "Resolved %s to %s", host, ip); + assert(strcmp(ip, "google-public-dns-a") == 0); + + memset(ip, '\0', ADDRESS_IPV6_ADDRSTRLEN); + ResolveIP(host, port, NAME_INFO_NUMERICHOST, ip, service); + TraceLog(LOG_INFO, "Resolved %s to %s", host, ip); + assert(strcmp(ip, "8.8.8.8") == 0); + + memset(ip, '\0', ADDRESS_IPV6_ADDRSTRLEN); + ResolveIP(host, port, NAME_INFO_NAMEREQD, ip, service); + TraceLog(LOG_INFO, "Resolved %s to %s", host, ip); + assert(strcmp(ip, "google-public-dns-a.google.com") == 0); + + memset(ip, '\0', ADDRESS_IPV6_ADDRSTRLEN); + ResolveIP(host, port, NAME_INFO_NUMERICSERV, ip, service); + TraceLog(LOG_INFO, "Resolved %s to %s", host, ip); + assert(strcmp(ip, "google-public-dns-a.google.com") == 0); + + memset(ip, '\0', ADDRESS_IPV6_ADDRSTRLEN); + ResolveIP(host, port, NAME_INFO_DGRAM, ip, service); + TraceLog(LOG_INFO, "Resolved %s to %s", host, ip); + assert(strcmp(ip, "google-public-dns-a.google.com") == 0); +} + +void test_resolve_host() +{ + const char * address = "localhost"; + const char * port = "80"; + AddressInformation *addr = AllocAddressList(3); + int count = ResolveHost(address, port, ADDRESS_TYPE_ANY, 0, addr); + + assert(GetAddressFamily(addr[0]) == ADDRESS_TYPE_IPV6); + assert(GetAddressFamily(addr[1]) == ADDRESS_TYPE_IPV4); + assert(GetAddressSocketType(addr[0]) == 0); + assert(GetAddressProtocol(addr[0]) == 0); + // for (size_t i = 0; i < count; i++) { PrintAddressInfo(addr[i]); } +} + +void test_address() +{ +} + +void test_address_list() +{ +} + +void test_socket_create() +{ + SocketConfig server_cfg = {.host = "127.0.0.1", .port = "8080", .server = true, .nonblocking = true}; + Socket * socket = AllocSocket(); + SocketResult *server_res = AllocSocketResult(); + SocketSet * socket_set = AllocSocketSet(1); + assert(SocketCreate(&server_cfg, server_res)); + assert(AddSocket(socket_set, server_res->socket)); + assert(SocketListen(&server_cfg, server_res)); +} + +int main() +{ + int screenWidth = 800; + int screenHeight = 450; + InitWindow( + screenWidth, screenHeight, "raylib [network] example - network test"); + SetTargetFPS(60); + + // Run the tests + test_network_initialise(); + test_resolve_host(); + // test_socket_create(); + + // Main game loop + while (!WindowShouldClose()) { + BeginDrawing(); + ClearBackground(RAYWHITE); + DrawText("Congrats! You created your first window!", 190, 200, 20, LIGHTGRAY); + EndDrawing(); + } + CloseWindow(); + + return 0; +} \ No newline at end of file diff --git a/examples/network/network_udp_client.c b/examples/network/network_udp_client.c new file mode 100644 index 00000000..c1c89c8d --- /dev/null +++ b/examples/network/network_udp_client.c @@ -0,0 +1,128 @@ +/******************************************************************************************* + * + * raylib [network] example - UDP Client + * + * Welcome to raylib! + * + * To test examples, just press F6 and execute raylib_compile_execute script + * Note that compiled executable is placed in the same folder as .c file + * + * You can find all basic examples on C:\raylib\raylib\examples folder or + * raylib official webpage: www.raylib.com + * + * Enjoy using raylib. :) + * + * 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) 2013-2016 Ramon Santamaria (@raysan5) + * + ********************************************************************************************/ + +#include "raylib.h" +#include "rnet.h" + +#include +#include +#include + +float elapsed = 0.0f; +float delay = 1.0f; +bool ping = false; +bool pong = false; +const char * pingmsg = "Ping!"; +const char * pongmsg = "Pong!"; +int msglen = 0; +SocketConfig client_cfg = {.host = "127.0.0.1", .port = "4950", .type = SOCKET_UDP, .nonblocking = true}; +SocketResult *client_res = NULL; +SocketSet * socket_set = NULL; +char recvBuffer[512]; + +// Once connected to the network, check the sockets for pending information +// and when information is ready, send either a Ping or a Pong. +void NetworkUpdate() +{ + // CheckSockets + // + // If any of the sockets in the socket_set are pending (received data, or requests) + // then mark the socket as being ready. You can check this with IsSocketReady(client_res->socket) + int active = CheckSockets(socket_set, 0); + if (active != 0) { + TraceLog(LOG_DEBUG, + "There are currently %d socket(s) with data to be processed.", active); + } + + // IsSocketReady + // + // If the socket is ready, attempt to receive data from the socket + int bytesRecv = 0; + if (IsSocketReady(client_res->socket)) { + bytesRecv = SocketReceive(client_res->socket, recvBuffer, msglen); + } + + // If we received data, was that data a "Ping!" or a "Pong!" + if (bytesRecv > 0) { + if (strcmp(recvBuffer, pingmsg) == 0) { pong = true; } + if (strcmp(recvBuffer, pongmsg) == 0) { ping = true; } + } + + // After each delay has expired, send a response "Ping!" for a "Pong!" and vice versa + elapsed += GetFrameTime(); + if (elapsed > delay) { + if (ping) { + ping = false; + SocketSend(client_res->socket, pingmsg, msglen); + } else if (pong) { + pong = false; + SocketSend(client_res->socket, pongmsg, msglen); + } + elapsed = 0.0f; + } +} + +int main() +{ + // Setup + int screenWidth = 800; + int screenHeight = 450; + InitWindow( + screenWidth, screenHeight, "raylib [network] example - udp client"); + SetTargetFPS(60); + SetTraceLogLevel(LOG_DEBUG); + + // Networking + InitNetwork(); + + // Create the client + // + // Performs + // getaddrinfo + // socket + // setsockopt + // connect (TCP only) + client_res = AllocSocketResult(); + if (!SocketCreate(&client_cfg, client_res)) { + TraceLog(LOG_WARNING, "Failed to open client: status %d, errno %d", + client_res->status, client_res->socket->status); + } + + // Create & Add sockets to the socket set + socket_set = AllocSocketSet(1); + msglen = strlen(pingmsg) + 1; + ping = true; + memset(recvBuffer, '\0', sizeof(recvBuffer)); + AddSocket(socket_set, client_res->socket); + + // Main game loop + while (!WindowShouldClose()) { + BeginDrawing(); + ClearBackground(RAYWHITE); + NetworkUpdate(); + EndDrawing(); + } + + // Cleanup + CloseWindow(); + return 0; +} \ No newline at end of file diff --git a/examples/network/network_udp_server.c b/examples/network/network_udp_server.c new file mode 100644 index 00000000..982cdf63 --- /dev/null +++ b/examples/network/network_udp_server.c @@ -0,0 +1,134 @@ +/******************************************************************************************* + * + * raylib [network] example - UDP Server + * + * Welcome to raylib! + * + * To test examples, just press F6 and execute raylib_compile_execute script + * Note that compiled executable is placed in the same folder as .c file + * + * You can find all basic examples on C:\raylib\raylib\examples folder or + * raylib official webpage: www.raylib.com + * + * Enjoy using raylib. :) + * + * 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) 2013-2016 Ramon Santamaria (@raysan5) + * + ********************************************************************************************/ + +#include "raylib.h" +#include "rnet.h" + +#include +#include +#include + +float elapsed = 0.0f; +float delay = 1.0f; +bool ping = false; +bool pong = false; +const char * pingmsg = "Ping!"; +const char * pongmsg = "Pong!"; +int msglen = 0; +SocketConfig server_cfg = {.host = "127.0.0.1", .port = "4950", .server = true, .type = SOCKET_UDP, .nonblocking = true}; +SocketResult *server_res = NULL; +SocketSet * socket_set = NULL; +char recvBuffer[512]; + +// Once connected to the network, check the sockets for pending information +// and when information is ready, send either a Ping or a Pong. +void NetworkUpdate() +{ + // CheckSockets + // + // If any of the sockets in the socket_set are pending (received data, or requests) + // then mark the socket as being ready. You can check this with IsSocketReady(client_res->socket) + int active = CheckSockets(socket_set, 0); + if (active != 0) { + TraceLog(LOG_DEBUG, + "There are currently %d socket(s) with data to be processed.", active); + } + + // IsSocketReady + // + // If the socket is ready, attempt to receive data from the socket + // int bytesRecv = 0; + // if (IsSocketReady(server_res->socket)) { + // bytesRecv = SocketReceive(server_res->socket, recvBuffer, msglen); + // } + int bytesRecv = SocketReceive(server_res->socket, recvBuffer, msglen); + + // If we received data, was that data a "Ping!" or a "Pong!" + if (bytesRecv > 0) { + if (strcmp(recvBuffer, pingmsg) == 0) { pong = true; } + if (strcmp(recvBuffer, pongmsg) == 0) { ping = true; } + } + + // After each delay has expired, send a response "Ping!" for a "Pong!" and vice versa + elapsed += GetFrameTime(); + if (elapsed > delay) { + if (ping) { + ping = false; + SocketSend(server_res->socket, pingmsg, msglen); + } else if (pong) { + pong = false; + SocketSend(server_res->socket, pongmsg, msglen); + } + elapsed = 0.0f; + } +} + +int main() +{ + // Setup + int screenWidth = 800; + int screenHeight = 450; + InitWindow( + screenWidth, screenHeight, "raylib [network] example - udp server"); + SetTargetFPS(60); + SetTraceLogLevel(LOG_DEBUG); + + // Networking + InitNetwork(); + + // Create the server + // + // Performs + // getaddrinfo + // socket + // setsockopt + // bind + // listen + server_res = AllocSocketResult(); + if (!SocketCreate(&server_cfg, server_res)) { + TraceLog(LOG_WARNING, "Failed to open server: status %d, errno %d", + server_res->status, server_res->socket->status); + } else { + if (!SocketBind(&server_cfg, server_res)) { + TraceLog(LOG_WARNING, "Failed to bind server: status %d, errno %d", + server_res->status, server_res->socket->status); + } + } + + // Create & Add sockets to the socket set + socket_set = AllocSocketSet(1); + msglen = strlen(pingmsg) + 1; + memset(recvBuffer, '\0', sizeof(recvBuffer)); + AddSocket(socket_set, server_res->socket); + + // Main game loop + while (!WindowShouldClose()) { + BeginDrawing(); + ClearBackground(RAYWHITE); + NetworkUpdate(); + EndDrawing(); + } + + // Cleanup + CloseWindow(); + return 0; +} \ No newline at end of file diff --git a/src/raylib.h b/src/raylib.h index 6a5f0ef8..710f69b6 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -75,6 +75,7 @@ #define RAYLIB_H #include // Required for: va_list - Only used by TraceLogCallback +#include // Required for rnet #if defined(_WIN32) && defined(BUILD_LIBTYPE_SHARED) #define RLAPI __declspec(dllexport) // We are building raylib as a Win32 shared library (.dll) @@ -99,6 +100,11 @@ // Shader and material limits #define MAX_SHADER_LOCATIONS 32 // Maximum number of predefined locations stored in shader struct #define MAX_MATERIAL_MAPS 12 // Maximum number of texture maps stored in shader struct + +// Network connection related defines +#define SOCKET_MAX_SOCK_OPTS (4) // Maximum socket options +#define SOCKET_MAX_UDPCHANNELS (32) // Maximum UDP channels +#define SOCKET_MAX_UDPADDRESSES (4) // Maximum bound UDP addresses // NOTE: MSC C++ compiler does not support compound literals (C99 feature) // Plain structures in C++ (without constructors) can be initialized from { } initializers. @@ -442,6 +448,100 @@ typedef struct VrDeviceInfo { float lensDistortionValues[4]; // HMD lens distortion constant parameters float chromaAbCorrection[4]; // HMD chromatic aberration correction parameters } VrDeviceInfo; + +// Network typedefs +typedef uint32_t SocketChannel; +typedef struct _AddressInformation * AddressInformation; +typedef struct _SocketAddress * SocketAddress; +typedef struct _SocketAddressIPv4 * SocketAddressIPv4; +typedef struct _SocketAddressIPv6 * SocketAddressIPv6; +typedef struct _SocketAddressStorage *SocketAddressStorage; + +// IPAddress definition (in network byte order) +typedef struct IPAddress +{ + unsigned long host; /* 32-bit IPv4 host address */ + unsigned short port; /* 16-bit protocol port */ +} IPAddress; + +// An option ID, value, sizeof(value) tuple for setsockopt(2). +typedef struct SocketOpt +{ + int id; + void *value; + int valueLen; +} SocketOpt; + +typedef enum +{ + SOCKET_TCP = 0, // SOCK_STREAM + SOCKET_UDP = 1 // SOCK_DGRAM +} SocketType; + +typedef struct UDPChannel +{ + int numbound; // The total number of addresses this channel is bound to + IPAddress address[SOCKET_MAX_UDPADDRESSES]; // The list of remote addresses this channel is bound to +} UDPChannel; + +typedef struct Socket +{ + int ready; // Is the socket ready? i.e. has information + int status; // The last status code to have occured using this socket + bool isServer; // Is this socket a server socket (i.e. TCP/UDP Listen Server) + SocketChannel channel; // The socket handle id + SocketType type; // Is this socket a TCP or UDP socket? + bool isIPv6; // Is this socket address an ipv6 address? + SocketAddressIPv4 addripv4; // The host/target IPv4 for this socket (in network byte order) + SocketAddressIPv6 addripv6; // The host/target IPv6 for this socket (in network byte order) + + struct UDPChannel binding[SOCKET_MAX_UDPCHANNELS]; // The amount of channels (if UDP) this socket is bound to +} Socket; + +typedef struct SocketSet +{ + int numsockets; + int maxsockets; + struct Socket **sockets; +} SocketSet; + +typedef struct SocketDataPacket +{ + int channel; // The src/dst channel of the packet + unsigned char *data; // The packet data + int len; // The length of the packet data + int maxlen; // The size of the data buffer + int status; // packet status after sending + IPAddress address; // The source/dest address of an incoming/outgoing packet +} SocketDataPacket; + +// Configuration for a socket. +typedef struct SocketConfig +{ + char * host; // The host address in xxx.xxx.xxx.xxx form + char * port; // The target port/service in the form "http" or "25565" + bool server; // Listen for incoming clients? + SocketType type; // The type of socket, TCP/UDP + bool nonblocking; // non-blocking operation? + int backlog_size; // set a custom backlog size + SocketOpt sockopts[SOCKET_MAX_SOCK_OPTS]; +} SocketConfig; + +// Result from calling open with a given config. +typedef struct SocketResult +{ + int status; + Socket *socket; +} SocketResult; + +// +typedef struct Packet +{ + uint32_t size; // The total size of bytes in data + uint32_t offs; // The offset to data access + uint32_t maxs; // The max size of data + uint8_t *data; // Data stored in network byte order +} Packet; //---------------------------------------------------------------------------------- // Enumerators Definition @@ -1407,6 +1507,82 @@ RLAPI bool IsAudioStreamPlaying(AudioStream stream); // Check i RLAPI void StopAudioStream(AudioStream stream); // Stop audio stream RLAPI void SetAudioStreamVolume(AudioStream stream, float volume); // Set volume for audio stream (1.0 is max level) RLAPI void SetAudioStreamPitch(AudioStream stream, float pitch); // Set pitch for audio stream (1.0 is base level) + +//------------------------------------------------------------------------------------ +// Network (Module: network) +//------------------------------------------------------------------------------------ + +// Initialisation and cleanup +RLAPI bool InitNetwork(void); +RLAPI void CloseNetwork(void); + +// Address API +RLAPI void ResolveIP(const char *ip, const char *service, int flags, char *outhost, char *outserv); +RLAPI int ResolveHost(const char *address, const char *service, int addressType, int flags, AddressInformation *outAddr); +RLAPI int GetAddressFamily(AddressInformation address); +RLAPI int GetAddressSocketType(AddressInformation address); +RLAPI int GetAddressProtocol(AddressInformation address); +RLAPI char* GetAddressCanonName(AddressInformation address); +RLAPI char* GetAddressHostAndPort(AddressInformation address, char *outhost, int *outport); +RLAPI void PrintAddressInfo(AddressInformation address); + +// Address Memory API +RLAPI AddressInformation AllocAddress(); +RLAPI void FreeAddress(AddressInformation *addressInfo); +RLAPI AddressInformation *AllocAddressList(int size); + +// Socket API +RLAPI bool SocketCreate(SocketConfig *config, SocketResult *result); +RLAPI bool SocketBind(SocketConfig *config, SocketResult *result); +RLAPI bool SocketListen(SocketConfig *config, SocketResult *result); +RLAPI bool SocketConnect(SocketConfig *config, SocketResult *result); +RLAPI Socket *SocketAccept(Socket *server, SocketConfig *config); + +// UDP Socket API +RLAPI int SocketSetChannel(Socket *socket, int channel, const IPAddress *address); +RLAPI void SocketUnsetChannel(Socket *socket, int channel); + +// UDP DataPacket API +RLAPI SocketDataPacket *AllocPacket(int size); +RLAPI int ResizePacket(SocketDataPacket *packet, int newsize); +RLAPI void FreePacket(SocketDataPacket *packet); +RLAPI SocketDataPacket **AllocPacketList(int count, int size); +RLAPI void FreePacketList(SocketDataPacket **packets); + +// General Socket API +RLAPI int SocketSend(Socket *sock, const void *datap, int len); +RLAPI int SocketReceive(Socket *sock, void *data, int maxlen); +RLAPI void SocketClose(Socket *sock); +RLAPI SocketAddressStorage SocketGetPeerAddress(Socket *sock); +RLAPI char* GetSocketAddressHost(SocketAddressStorage storage); +RLAPI short GetSocketAddressPort(SocketAddressStorage storage); + +// Socket Memory API +RLAPI Socket *AllocSocket(); +RLAPI void FreeSocket(Socket **sock); +RLAPI SocketResult *AllocSocketResult(); +RLAPI void FreeSocketResult(SocketResult **result); +RLAPI SocketSet *AllocSocketSet(int max); +RLAPI void FreeSocketSet(SocketSet *sockset); + +// Socket I/O API +RLAPI bool IsSocketReady(Socket *sock); +RLAPI bool IsSocketConnected(Socket *sock); +RLAPI int AddSocket(SocketSet *set, Socket *sock); +RLAPI int RemoveSocket(SocketSet *set, Socket *sock); +RLAPI int CheckSockets(SocketSet *set, unsigned int timeout); + +// Packet API +void PacketSend(Packet *packet); +void PacketReceive(Packet *packet); +void PacketWrite8(Packet *packet, uint16_t value); +void PacketWrite16(Packet *packet, uint16_t value); +void PacketWrite32(Packet *packet, uint32_t value); +void PacketWrite64(Packet *packet, uint64_t value); +uint16_t PacketRead8(Packet *packet); +uint16_t PacketRead16(Packet *packet); +uint32_t PacketRead32(Packet *packet); +uint64_t PacketRead64(Packet *packet); #if defined(__cplusplus) } diff --git a/src/rnet.c b/src/rnet.c new file mode 100644 index 00000000..0a47b44f --- /dev/null +++ b/src/rnet.c @@ -0,0 +1,2023 @@ +/********************************************************************************************* +* +* rnet - A simple and easy-to-use network module for raylib +* +* FEATURES: +* - Provides a simple and (hopefully) easy to use wrapper around the Berkeley socket API +* +* DEPENDENCIES: +* raylib.h - TraceLog +* rnet.h - platform-specific network includes +* +* CONTRIBUTORS: +* Jak Barnes (github: @syphonx) (Feb. 2019): +* - Initial version +* +* +* LICENSE: zlib/libpng +* +* 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. +* +* 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. +* +**********************************************************************************************/ + +//---------------------------------------------------------------------------------- +// Check if config flags have been externally provided on compilation line +//---------------------------------------------------------------------------------- + +#include "rnet.h" + +#include "raylib.h" + +#include // Required for: assert() +#include // Required for: FILE, fopen(), fclose(), fread() +#include // Required for: malloc(), free() +#include // Required for: strcmp(), strncmp() + +//---------------------------------------------------------------------------------- +// Module defines +//---------------------------------------------------------------------------------- + +#define NET_DEBUG_ENABLED (1) + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- + +typedef struct _SocketAddress +{ + struct sockaddr address; +} _SocketAddress; + +typedef struct _SocketAddressIPv4 +{ + struct sockaddr_in address; +} _SocketAddressIPv4; + +typedef struct _SocketAddressIPv6 +{ + struct sockaddr_in6 address; +} _SocketAddressIPv6; + +typedef struct _SocketAddressStorage +{ + struct sockaddr_storage address; +} _SocketAddressStorage; + +typedef struct _AddressInformation +{ + struct addrinfo addr; +} _AddressInformation; + + + +//---------------------------------------------------------------------------------- +// Global module forward declarations +//---------------------------------------------------------------------------------- + +static void PrintSocket(struct sockaddr_storage *addr, const int family, const int socktype, const int protocol); +static const char *SocketAddressToString(struct sockaddr_storage *sockaddr); +static bool IsIPv4Address(const char *ip); +static bool IsIPv6Address(const char *ip); +static void * GetSocketPortPtr(struct sockaddr_storage *sa); +static void * GetSocketAddressPtr(struct sockaddr_storage *sa); +static bool IsSocketValid(Socket *sock); +static void SocketSetLastError(int err); +static int SocketGetLastError(); +static char * SocketGetLastErrorString(); +static char * SocketErrorCodeToString(int err); +static bool SocketSetDefaults(SocketConfig *config); +static bool InitSocket(Socket *sock, struct addrinfo *addr); +static bool CreateSocket(SocketConfig *config, SocketResult *outresult); +static bool SocketSetBlocking(Socket *sock); +static bool SocketSetNonBlocking(Socket *sock); +static bool SocketSetOptions(SocketConfig *config, Socket *sock); +static void SocketSetHints(SocketConfig *config, struct addrinfo *hints); + +//---------------------------------------------------------------------------------- +// Global module implementation +//---------------------------------------------------------------------------------- + +// Print socket information +static void PrintSocket(struct sockaddr_storage *addr, const int family, const int socktype, const int protocol) +{ + switch (family) + { + case AF_UNSPEC: { TraceLog(LOG_DEBUG, "\tFamily: Unspecified"); + } + break; + case AF_INET: + { + TraceLog(LOG_DEBUG, "\tFamily: AF_INET (IPv4)"); + TraceLog(LOG_INFO, "\t- IPv4 address %s", SocketAddressToString(addr)); + } + break; + case AF_INET6: + { + TraceLog(LOG_DEBUG, "\tFamily: AF_INET6 (IPv6)"); + TraceLog(LOG_INFO, "\t- IPv6 address %s", SocketAddressToString(addr)); + } + break; + case AF_NETBIOS: + { + TraceLog(LOG_DEBUG, "\tFamily: AF_NETBIOS (NetBIOS)"); + } + break; + default: { TraceLog(LOG_DEBUG, "\tFamily: Other %ld", family); + } + break; + } + TraceLog(LOG_DEBUG, "\tSocket type:"); + switch (socktype) + { + case 0: TraceLog(LOG_DEBUG, "\t- Unspecified"); break; + case SOCK_STREAM: + TraceLog(LOG_DEBUG, "\t- SOCK_STREAM (stream)"); + break; + case SOCK_DGRAM: + TraceLog(LOG_DEBUG, "\t- SOCK_DGRAM (datagram)"); + break; + case SOCK_RAW: TraceLog(LOG_DEBUG, "\t- SOCK_RAW (raw)"); break; + case SOCK_RDM: + TraceLog(LOG_DEBUG, "\t- SOCK_RDM (reliable message datagram)"); + break; + case SOCK_SEQPACKET: + TraceLog(LOG_DEBUG, "\t- SOCK_SEQPACKET (pseudo-stream packet)"); + break; + default: TraceLog(LOG_DEBUG, "\t- Other %ld", socktype); break; + } + TraceLog(LOG_DEBUG, "\tProtocol:"); + switch (protocol) + { + case 0: TraceLog(LOG_DEBUG, "\t- Unspecified"); break; + case IPPROTO_TCP: TraceLog(LOG_DEBUG, "\t- IPPROTO_TCP (TCP)"); break; + case IPPROTO_UDP: TraceLog(LOG_DEBUG, "\t- IPPROTO_UDP (UDP)"); break; + default: TraceLog(LOG_DEBUG, "\t- Other %ld", protocol); break; + } +} + +// Convert network ordered socket address to human readable string (127.0.0.1) +static const char *SocketAddressToString(struct sockaddr_storage *sockaddr) +{ + static const char* ipv6[INET6_ADDRSTRLEN]; + assert(sockaddr != NULL); + assert(sockaddr->ss_family == AF_INET || sockaddr->ss_family == AF_INET6); + switch (sockaddr->ss_family) + { + case AF_INET: + { + struct sockaddr_in *s = ((struct sockaddr_in *) sockaddr); + return inet_ntop(AF_INET, &s->sin_addr, ipv6, INET_ADDRSTRLEN); + } + break; + case AF_INET6: + { + struct sockaddr_in6 *s = ((struct sockaddr_in6 *) sockaddr); + return inet_ntop(AF_INET6, &s->sin6_addr, ipv6, INET6_ADDRSTRLEN); + } + break; + } + return NULL; +} + +// Check if the null terminated string ip is a valid IPv4 address +static bool IsIPv4Address(const char *ip) +{ + struct sockaddr_in sa; + int result = inet_pton(AF_INET, ip, &(sa.sin_addr)); + return result != 0; +} + +// Check if the null terminated string ip is a valid IPv6 address +static bool IsIPv6Address(const char *ip) +{ + struct sockaddr_in6 sa; + int result = inet_pton(AF_INET6, ip, &(sa.sin6_addr)); + return result != 0; +} + +// Return a pointer to the port from the correct address family (IPv4, or IPv6) +static void *GetSocketPortPtr(struct sockaddr_storage *sa) +{ + if (sa->ss_family == AF_INET) + { + return &(((struct sockaddr_in *) sa)->sin_port); + } + + return &(((struct sockaddr_in6 *) sa)->sin6_port); +} + +// Return a pointer to the address from the correct address family (IPv4, or IPv6) +static void *GetSocketAddressPtr(struct sockaddr_storage *sa) +{ + if (sa->ss_family == AF_INET) + { + return &(((struct sockaddr_in *) sa)->sin_addr); + } + + return &(((struct sockaddr_in6 *) sa)->sin6_addr); +} + +// Is the socket in a valid state? +static bool IsSocketValid(Socket *sock) +{ + if (sock != NULL) + { + return (sock->channel != INVALID_SOCKET); + } + return false; +} + +// Sets the error code that can be retrieved through the WSAGetLastError function. +static void SocketSetLastError(int err) +{ +#if PLATFORM == PLATFORM_WINDOWS + WSASetLastError(err); +#else + errno = err; +#endif +} + +// Returns the error status for the last Sockets operation that failed +static int SocketGetLastError() +{ +#if PLATFORM == PLATFORM_WINDOWS + return WSAGetLastError(); +#else + return errno; +#endif +} + +// Returns a human-readable string representing the last error message +static char *SocketGetLastErrorString() +{ + return SocketErrorCodeToString(SocketGetLastError()); +} + +// Returns a human-readable string representing the error message (err) +static char *SocketErrorCodeToString(int err) +{ +#if PLATFORM == PLATFORM_WINDOWS + static char gaiStrErrorBuffer[GAI_STRERROR_BUFFER_SIZE]; + sprintf(gaiStrErrorBuffer, "%ws", gai_strerror(err)); + return gaiStrErrorBuffer; +#else + return gai_strerror(err); +#endif +} + +// Set the defaults in the supplied SocketConfig if they're not already set +static bool SocketSetDefaults(SocketConfig *config) +{ + if (config->backlog_size == 0) + { + config->backlog_size = SOCKET_MAX_QUEUE_SIZE; + } + + return true; +} + +// Create the socket channel +static bool InitSocket(Socket *sock, struct addrinfo *addr) +{ + switch (sock->type) + { + case SOCKET_TCP: + if (addr->ai_family == AF_INET) + { + sock->channel = socket(AF_INET, SOCK_STREAM, 0); + } + else + { + sock->channel = socket(AF_INET6, SOCK_STREAM, 0); + } + break; + case SOCKET_UDP: + if (addr->ai_family == AF_INET) + { + sock->channel = socket(AF_INET, SOCK_DGRAM, 0); + } + else + { + sock->channel = socket(AF_INET6, SOCK_DGRAM, 0); + } + break; + default: + TraceLog(LOG_WARNING, "Invalid socket type specified."); + break; + } + return IsSocketValid(sock); +} + +// CreateSocket() - Interally called by CreateSocket() +// +// This here is the bread and butter of the socket API, This function will +// attempt to open a socket, bind and listen to it based on the config passed in +// +// SocketConfig* config - Configuration for which socket to open +// SocketResult* result - The results of this function (if any, including errors) +// +// e.g. +// SocketConfig server_config = { SocketConfig client_config = { +// .host = "127.0.0.1", .host = "127.0.0.1", +// .port = 8080, .port = 8080, +// .server = true, }; +// .nonblocking = true, +// }; +// SocketResult server_res; SocketResult client_res; +static bool CreateSocket(SocketConfig *config, SocketResult *outresult) +{ + bool success = true; + int addrstatus; + struct addrinfo hints; // Address flags (IPV4, IPV6, UDP?) + struct addrinfo *res; // A pointer to the resulting address list + outresult->socket->channel = INVALID_SOCKET; + outresult->status = RESULT_FAILURE; + + // Set the socket type + outresult->socket->type = config->type; + + // Set the hints based on information in the config + // + // AI_CANONNAME Causes the ai_canonname of the result to the filled out with the host's canonical (real) name. + // AI_PASSIVE: Causes the result's IP address to be filled out with INADDR_ANY (IPv4)or in6addr_any (IPv6); + // Note: This causes a subsequent call to bind() to auto-fill the IP address + // of the struct sockaddr with the address of the current host. + // + SocketSetHints(config, &hints); + + // Populate address information + addrstatus = getaddrinfo(config->host, // e.g. "www.example.com" or IP (Can be null if AI_PASSIVE flag is set + config->port, // e.g. "http" or port number + &hints, // e.g. SOCK_STREAM/SOCK_DGRAM + &res // The struct to populate + ); + + // Did we succeed? + if (addrstatus != 0) + { + outresult->socket->status = SocketGetLastError(); + TraceLog(LOG_WARNING, + "Socket Error: %s", + SocketErrorCodeToString(outresult->socket->status)); + SocketSetLastError(0); + TraceLog(LOG_WARNING, + "Failed to get resolve host %s:%s: %s", + config->host, + config->port, + SocketGetLastErrorString()); + return (success = false); + } + else + { + char hoststr[NI_MAXHOST]; + char portstr[NI_MAXSERV]; + socklen_t client_len = sizeof(struct sockaddr_storage); + int rc = getnameinfo((struct sockaddr *) res->ai_addr, client_len, hoststr, sizeof(hoststr), portstr, sizeof(portstr), NI_NUMERICHOST | NI_NUMERICSERV); + TraceLog(LOG_INFO, "Successfully resolved host %s:%s", hoststr, portstr); + } + + // Walk the address information linked-list + struct addrinfo *it; + for (it = res; it != NULL; it = it->ai_next) + { + // Initialise the socket + if (!InitSocket(outresult->socket, it)) + { + outresult->socket->status = SocketGetLastError(); + TraceLog(LOG_WARNING, + "Socket Error: %s", + SocketErrorCodeToString(outresult->socket->status)); + SocketSetLastError(0); + continue; + } + + // Set socket options + if (!SocketSetOptions(config, outresult->socket)) + { + outresult->socket->status = SocketGetLastError(); + TraceLog(LOG_WARNING, + "Socket Error: %s", + SocketErrorCodeToString(outresult->socket->status)); + SocketSetLastError(0); + freeaddrinfo(res); + return (success = false); + } + } + + if (!IsSocketValid(outresult->socket)) + { + outresult->socket->status = SocketGetLastError(); + TraceLog( + LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(outresult->status)); + SocketSetLastError(0); + freeaddrinfo(res); + return (success = false); + } + + if (success) + { + outresult->status = RESULT_SUCCESS; + outresult->socket->ready = 0; + outresult->socket->status = 0; + if (!(config->type == SOCKET_UDP)) + { + outresult->socket->isServer = config->server; + } + switch (res->ai_addr->sa_family) + { + case AF_INET: + { + outresult->socket->addripv4 = (struct _SocketAddressIPv4 *) malloc( + sizeof(*outresult->socket->addripv4)); + if (outresult->socket->addripv4 != NULL) + { + memset(outresult->socket->addripv4, 0, + sizeof(*outresult->socket->addripv4)); + if (outresult->socket->addripv4 != NULL) + { + memcpy(&outresult->socket->addripv4->address, + (struct sockaddr_in *) res->ai_addr, sizeof(struct sockaddr_in)); + outresult->socket->isIPv6 = false; + char hoststr[NI_MAXHOST]; + char portstr[NI_MAXSERV]; + socklen_t client_len = sizeof(struct sockaddr_storage); + getnameinfo( + (struct sockaddr *) &outresult->socket->addripv4->address, client_len, hoststr, sizeof(hoststr), portstr, sizeof(portstr), NI_NUMERICHOST | NI_NUMERICSERV); + TraceLog(LOG_INFO, "Socket address set to %s:%s", hoststr, portstr); + } + } + } + break; + case AF_INET6: + { + outresult->socket->addripv6 = (struct _SocketAddressIPv6 *) malloc( + sizeof(*outresult->socket->addripv6)); + if (outresult->socket->addripv6 != NULL) + { + memset(outresult->socket->addripv6, 0, + sizeof(*outresult->socket->addripv6)); + if (outresult->socket->addripv6 != NULL) + { + memcpy(&outresult->socket->addripv6->address, + (struct sockaddr_in6 *) res->ai_addr, sizeof(struct sockaddr_in6)); + outresult->socket->isIPv6 = true; + char hoststr[NI_MAXHOST]; + char portstr[NI_MAXSERV]; + socklen_t client_len = sizeof(struct sockaddr_storage); + getnameinfo( + (struct sockaddr *) &outresult->socket->addripv6->address, client_len, hoststr, sizeof(hoststr), portstr, sizeof(portstr), NI_NUMERICHOST | NI_NUMERICSERV); + TraceLog(LOG_INFO, "Socket address set to %s:%s", hoststr, portstr); + } + } + } + break; + } + } + freeaddrinfo(res); + return success; +} + +// Set the state of the Socket sock to blocking +static bool SocketSetBlocking(Socket *sock) +{ + bool ret = true; +#if PLATFORM == PLATFORM_WINDOWS + unsigned long mode = 0; + ret = ioctlsocket(sock->channel, FIONBIO, &mode); +#else + const int flags = fcntl(sock->channel, F_GETFL, 0); + if (!(flags & O_NONBLOCK)) + { + TraceLog(LOG_DEBUG, "Socket was already in blocking mode"); + return ret; + } + + ret = (0 == fcntl(sock->channel, F_SETFL, (flags ^ O_NONBLOCK))); +#endif + return ret; +} + +// Set the state of the Socket sock to non-blocking +static bool SocketSetNonBlocking(Socket *sock) +{ + bool ret = true; +#if PLATFORM == PLATFORM_WINDOWS + unsigned long mode = 1; + ret = ioctlsocket(sock->channel, FIONBIO, &mode); +#else + const int flags = fcntl(sock->channel, F_GETFL, 0); + if ((flags & O_NONBLOCK)) + { + TraceLog(LOG_DEBUG, "Socket was already in non-blocking mode"); + return ret; + } + ret = (0 == fcntl(sock->channel, F_SETFL, (flags | O_NONBLOCK))); +#endif + return ret; +} + +// Set options specified in SocketConfig to Socket sock +static bool SocketSetOptions(SocketConfig *config, Socket *sock) +{ + for (int i = 0; i < SOCKET_MAX_SOCK_OPTS; i++) + { + SocketOpt *opt = &config->sockopts[i]; + if (opt->id == 0) + { + break; + } + + if (setsockopt(sock->channel, SOL_SOCKET, opt->id, opt->value, opt->valueLen) < 0) + { + return false; + } + } + + return true; +} + +// Set "hints" in an addrinfo struct, to be passed to getaddrinfo. +static void SocketSetHints(SocketConfig *config, struct addrinfo *hints) +{ + if (config == NULL || hints == NULL) + { + return; + } + memset(hints, 0, sizeof(*hints)); + + // Check if the ip supplied in the config is a valid ipv4 ip ipv6 address + if (IsIPv4Address(config->host)) + { + hints->ai_family = AF_INET; + hints->ai_flags |= AI_NUMERICHOST; + } + else + { + if (IsIPv6Address(config->host)) + { + hints->ai_family = AF_INET6; + hints->ai_flags |= AI_NUMERICHOST; + } + else + { + hints->ai_family = AF_UNSPEC; + } + } + + if (config->type == SOCKET_UDP) + { + hints->ai_socktype = SOCK_DGRAM; + } + else + { + hints->ai_socktype = SOCK_STREAM; + } + + // Set passive unless UDP client + if (!(config->type == SOCKET_UDP) || config->server) + { + hints->ai_flags = AI_PASSIVE; + } +} + +//---------------------------------------------------------------------------------- +// Module implementation +//---------------------------------------------------------------------------------- + +// Initialise the network (requires for windows platforms only) +bool InitNetwork() +{ +#if PLATFORM == PLATFORM_WINDOWS + WORD wVersionRequested; + WSADATA wsaData; + int err; + + wVersionRequested = MAKEWORD(2, 2); + err = WSAStartup(wVersionRequested, &wsaData); + if (err != 0) + { + TraceLog(LOG_WARNING, "WinSock failed to initialise."); + return false; + } + else + { + TraceLog(LOG_INFO, "WinSock initialised."); + } + + if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) + { + TraceLog(LOG_WARNING, "WinSock failed to initialise."); + WSACleanup(); + return false; + } + + return true; +#else + return true; +#endif +} + +// Cleanup, and close the network +void CloseNetwork() +{ +#if PLATFORM == PLATFORM_WINDOWS + WSACleanup(); +#endif +} + +// Protocol-independent name resolution from an address to an ANSI host name +// and from a port number to the ANSI service name. +// +// The flags parameter can be used to customize processing of the getnameinfo function +// +// The following flags are available: +// +// NAME_INFO_DEFAULT 0x00 // No flags set +// NAME_INFO_NOFQDN 0x01 // Only return nodename portion for local hosts +// NAME_INFO_NUMERICHOST 0x02 // Return numeric form of the host's address +// NAME_INFO_NAMEREQD 0x04 // Error if the host's name not in DNS +// NAME_INFO_NUMERICSERV 0x08 // Return numeric form of the service (port #) +// NAME_INFO_DGRAM 0x10 // Service is a datagram service +void ResolveIP(const char *ip, const char *port, int flags, char *host, char *serv) +{ + // Variables + int status; // Status value to return (0) is success + struct addrinfo hints; // Address flags (IPV4, IPV6, UDP?) + struct addrinfo *res; // A pointer to the resulting address list + + // Set the hints + memset(&hints, 0, sizeof hints); + hints.ai_family = AF_UNSPEC; // Either IPv4 or IPv6 (AF_INET, AF_INET6) + hints.ai_protocol = 0; // Automatically select correct protocol (IPPROTO_TCP), (IPPROTO_UDP) + + // Populate address information + status = getaddrinfo(ip, // e.g. "www.example.com" or IP + port, // e.g. "http" or port number + &hints, // e.g. SOCK_STREAM/SOCK_DGRAM + &res // The struct to populate + ); + + // Did we succeed? + if (status != 0) + { + TraceLog(LOG_WARNING, "Failed to get resolve host %s:%s: %s", ip, port, gai_strerror(errno)); + } + else + { + TraceLog(LOG_DEBUG, "Resolving... %s::%s", ip, port); + } + + // Attempt to resolve network byte order ip to hostname + switch (res->ai_family) + { + case AF_INET: + status = getnameinfo(&*((struct sockaddr *) res->ai_addr), + sizeof(*((struct sockaddr_in *) res->ai_addr)), + host, + NI_MAXHOST, + serv, + NI_MAXSERV, + flags); + break; + case AF_INET6: + status = getnameinfo(&*((struct sockaddr_in6 *) res->ai_addr), + sizeof(*((struct sockaddr_in6 *) res->ai_addr)), + host, + NI_MAXHOST, + serv, + NI_MAXSERV, + flags); + break; + default: break; + } + + if (status != 0) + { + TraceLog(LOG_WARNING, "Failed to resolve ip %s: %s", ip, SocketGetLastErrorString()); + } + else + { + TraceLog(LOG_DEBUG, "Successfully resolved %s::%s to %s", ip, port, host); + } + + // Free the pointer to the data returned by addrinfo + freeaddrinfo(res); +} + +// Protocol-independent translation from an ANSI host name to an address +// +// e.g. +// const char* address = "127.0.0.1" (local address) +// const char* port = "80" +// +// Parameters: +// const char* address - A pointer to a NULL-terminated ANSI string that contains a host (node) name or a numeric host address string. +// const char* service - A pointer to a NULL-terminated ANSI string that contains either a service name or port number represented as a string. +// +// Returns: +// The total amount of addresses found, -1 on error +// +int ResolveHost(const char *address, const char *service, int addressType, int flags, AddressInformation *outAddr) +{ + // Variables + int status; // Status value to return (0) is success + struct addrinfo hints; // Address flags (IPV4, IPV6, UDP?) + struct addrinfo *res; // will point to the results + struct addrinfo *iterator; + assert(((address != NULL || address != 0) || (service != NULL || service != 0))); + assert(((addressType == AF_INET) || (addressType == AF_INET6) || (addressType == AF_UNSPEC))); + + // Set the hints + memset(&hints, 0, sizeof hints); + hints.ai_family = addressType; // Either IPv4 or IPv6 (ADDRESS_TYPE_IPV4, ADDRESS_TYPE_IPV6) + hints.ai_protocol = 0; // Automatically select correct protocol (IPPROTO_TCP), (IPPROTO_UDP) + hints.ai_flags = flags; + assert(hints.ai_addrlen == NULL || hints.ai_addrlen == 0); + assert(hints.ai_canonname == NULL || hints.ai_canonname == 0); + assert(hints.ai_addr == NULL || hints.ai_addr == 0); + assert(hints.ai_next == NULL || hints.ai_next == 0); + + // When the address is NULL, populate the IP for me + if (address == NULL) + { + if ((hints.ai_flags & AI_PASSIVE) == 0) + { + hints.ai_flags |= AI_PASSIVE; + } + } + + TraceLog(LOG_INFO, "Resolving host..."); + + // Populate address information + status = getaddrinfo(address, // e.g. "www.example.com" or IP + service, // e.g. "http" or port number + &hints, // e.g. SOCK_STREAM/SOCK_DGRAM + &res // The struct to populate + ); + + // Did we succeed? + if (status != 0) + { + int error = SocketGetLastError(); + SocketSetLastError(0); + TraceLog(LOG_WARNING, "Failed to get resolve host: %s", SocketErrorCodeToString(error)); + return -1; + } + else + { + TraceLog(LOG_INFO, "Successfully resolved host %s:%s", address, service); + } + + // Calculate the size of the address information list + int size = 0; + for (iterator = res; iterator != NULL; iterator = iterator->ai_next) + { + size++; + } + + // Validate the size is > 0, otherwise return + if (size <= 0) + { + TraceLog(LOG_WARNING, "Error, no addresses found."); + return -1; + } + + // If not address list was allocated, allocate it dynamically with the known address size + if (outAddr == NULL) + { + outAddr = (AddressInformation *) malloc(size * sizeof(AddressInformation)); + } + + // Dynamically allocate an array of address information structs + if (outAddr != NULL) + { + int i; + for (i = 0; i < size; ++i) + { + outAddr[i] = AllocAddress(); + if (outAddr[i] == NULL) + { + break; + } + } + outAddr[i] = NULL; + if (i != size) + { + outAddr = NULL; + } + } + else + { + TraceLog(LOG_WARNING, + "Error, failed to dynamically allocate memory for the address list"); + return -1; + } + + // Copy all the address information from res into outAddrList + int i = 0; + for (iterator = res; iterator != NULL; iterator = iterator->ai_next) + { + if (i < size) + { + outAddr[i]->addr.ai_flags = iterator->ai_flags; + outAddr[i]->addr.ai_family = iterator->ai_family; + outAddr[i]->addr.ai_socktype = iterator->ai_socktype; + outAddr[i]->addr.ai_protocol = iterator->ai_protocol; + outAddr[i]->addr.ai_addrlen = iterator->ai_addrlen; + *outAddr[i]->addr.ai_addr = *iterator->ai_addr; +#if NET_DEBUG_ENABLED + TraceLog(LOG_DEBUG, "GetAddressInformation"); + TraceLog(LOG_DEBUG, "\tFlags: 0x%x", iterator->ai_flags); + PrintSocket(outAddr[i]->addr.ai_addr, + outAddr[i]->addr.ai_family, + outAddr[i]->addr.ai_socktype, + outAddr[i]->addr.ai_protocol); + TraceLog(LOG_DEBUG, "Length of this sockaddr: %d", outAddr[i]->addr.ai_addrlen); + TraceLog(LOG_DEBUG, "Canonical name: %s", iterator->ai_canonname); +#endif + i++; + } + } + + // Free the pointer to the data returned by addrinfo + freeaddrinfo(res); + + // Return the total count of addresses found + return size; +} + +// This here is the bread and butter of the socket API, This function will +// attempt to open a socket, bind and listen to it based on the config passed in +// +// SocketConfig* config - Configuration for which socket to open +// SocketResult* result - The results of this function (if any, including errors) +// +// e.g. +// SocketConfig server_config = { SocketConfig client_config = { +// .host = "127.0.0.1", .host = "127.0.0.1", +// .port = 8080, .port = 8080, +// .server = true, }; +// .nonblocking = true, +// }; +// SocketResult server_res; SocketResult client_res; +bool SocketCreate(SocketConfig *config, SocketResult *result) +{ + // Socket creation result + bool success = true; + + // Make sure we've not received a null config or result pointer + if (config == NULL || result == NULL) + { + return (success = false); + } + + // Set the defaults based on the config + if (!SocketSetDefaults(config)) + { + TraceLog(LOG_WARNING, "Configuration Error."); + success = false; + } + else + { + // Create the socket + if (CreateSocket(config, result)) + { + if (config->nonblocking) + { + SocketSetNonBlocking(result->socket); + } + else + { + SocketSetBlocking(result->socket); + } + } + else + { + success = false; + } + } + return success; +} + +// Bind a socket to a local address +// Note: The bind function is required on an unconnected socket before subsequent calls to the listen function. +bool SocketBind(SocketConfig *config, SocketResult *result) +{ + bool success = false; + result->status = RESULT_FAILURE; + struct sockaddr_storage *sock_addr = NULL; + + // Don't bind to a socket that isn't configured as a server + if (!IsSocketValid(result->socket) || !config->server) + { + TraceLog(LOG_WARNING, + "Cannot bind to socket marked as \"Client\" in SocketConfig."); + success = false; + } + else + { + if (result->socket->isIPv6) + { + sock_addr = (struct sockaddr_storage *) &result->socket->addripv6->address; + } + else + { + sock_addr = (struct sockaddr_storage *) &result->socket->addripv4->address; + } + if (sock_addr != NULL) + { + if (bind(result->socket->channel, (struct sockaddr *) sock_addr, sizeof(*sock_addr)) != SOCKET_ERROR) + { + TraceLog(LOG_INFO, "Successfully bound socket."); + success = true; + } + else + { + result->socket->status = SocketGetLastError(); + TraceLog(LOG_WARNING, "Socket Error: %s", + SocketErrorCodeToString(result->socket->status)); + SocketSetLastError(0); + success = false; + } + } + } + // Was the bind a success? + if (success) + { + result->status = RESULT_SUCCESS; + result->socket->ready = 0; + result->socket->status = 0; + socklen_t sock_len = sizeof(*sock_addr); + if (getsockname(result->socket->channel, (struct sockaddr *) sock_addr, &sock_len) < 0) + { + TraceLog(LOG_WARNING, "Couldn't get socket address"); + } + else + { + struct sockaddr_in *s = (struct sockaddr_in *) sock_addr; + // result->socket->address.host = s->sin_addr.s_addr; + // result->socket->address.port = s->sin_port; + + // + result->socket->addripv4 + = (struct _SocketAddressIPv4 *) malloc(sizeof(*result->socket->addripv4)); + if (result->socket->addripv4 != NULL) + { + memset(result->socket->addripv4, 0, sizeof(*result->socket->addripv4)); + } + memcpy(&result->socket->addripv4->address, (struct sockaddr_in *) &s->sin_addr, sizeof(struct sockaddr_in)); + // + } + } + return success; +} + +// Listens (and queues) incoming connections requests for a bound port. +bool SocketListen(SocketConfig *config, SocketResult *result) +{ + bool success = false; + result->status = RESULT_FAILURE; + + // Don't bind to a socket that isn't configured as a server + if (!IsSocketValid(result->socket) || !config->server) + { + TraceLog(LOG_WARNING, + "Cannot listen on socket marked as \"Client\" in SocketConfig."); + success = false; + } + else + { + // Don't listen on UDP sockets + if (!(config->type == SOCKET_UDP)) + { + if (listen(result->socket->channel, config->backlog_size) != SOCKET_ERROR) + { + TraceLog(LOG_INFO, "Started listening on socket..."); + success = true; + } + else + { + success = false; + result->socket->status = SocketGetLastError(); + TraceLog(LOG_WARNING, "Socket Error: %s", + SocketErrorCodeToString(result->socket->status)); + SocketSetLastError(0); + } + } + else + { + TraceLog(LOG_WARNING, + "Cannot listen on socket marked as \"UDP\" (datagram) in SocketConfig."); + success = false; + } + } + + // Was the listen a success? + if (success) + { + result->status = RESULT_SUCCESS; + result->socket->ready = 0; + result->socket->status = 0; + } + return success; +} + +// Connect the socket to the destination specified by "host" and "port" in SocketConfig +bool SocketConnect(SocketConfig *config, SocketResult *result) +{ + bool success = true; + result->status = RESULT_FAILURE; + + // Only bind to sockets marked as server + if (config->server) + { + TraceLog(LOG_WARNING, + "Cannot connect to socket marked as \"Server\" in SocketConfig."); + success = false; + } + else + { + if (IsIPv4Address(config->host)) + { + struct sockaddr_in ip4addr; + ip4addr.sin_family = AF_INET; + unsigned long hport; + hport = strtoul(config->port, NULL, 0); + ip4addr.sin_port = htons(hport); + inet_pton(AF_INET, config->host, &ip4addr.sin_addr); + int connect_result = connect(result->socket->channel, (struct sockaddr *) &ip4addr, sizeof(ip4addr)); + if (connect_result == SOCKET_ERROR) + { + result->socket->status = SocketGetLastError(); + SocketSetLastError(0); + switch (result->socket->status) + { + case WSAEWOULDBLOCK: + { + success = true; + break; + } + default: + { + TraceLog(LOG_WARNING, "Socket Error: %s", + SocketErrorCodeToString(result->socket->status)); + success = false; + break; + } + } + } + else + { + TraceLog(LOG_INFO, "Successfully connected to socket."); + success = true; + } + } + else + { + if (IsIPv6Address(config->host)) + { + struct sockaddr_in6 ip6addr; + ip6addr.sin6_family = AF_INET6; + unsigned long hport; + hport = strtoul(config->port, NULL, 0); + ip6addr.sin6_port = htons(hport); + inet_pton(AF_INET6, config->host, &ip6addr.sin6_addr); + int connect_result = connect(result->socket->channel, (struct sockaddr *) &ip6addr, sizeof(ip6addr)); + if (connect_result == SOCKET_ERROR) + { + result->socket->status = SocketGetLastError(); + SocketSetLastError(0); + switch (result->socket->status) + { + case WSAEWOULDBLOCK: + { + success = true; + break; + } + default: + { + TraceLog(LOG_WARNING, "Socket Error: %s", + SocketErrorCodeToString(result->socket->status)); + success = false; + break; + } + } + } + else + { + TraceLog(LOG_INFO, "Successfully connected to socket."); + success = true; + } + } + } + } + + if (success) + { + result->status = RESULT_SUCCESS; + result->socket->ready = 0; + result->socket->status = 0; + } + + return success; +} + +// Closes an existing socket +// +// SocketChannel socket - The id of the socket to close +void SocketClose(Socket *sock) +{ + if (sock != NULL) + { + if (sock->channel != INVALID_SOCKET) + { + closesocket(sock->channel); + } + } +} + +// Returns the sockaddress for a specific socket in a generic storage struct +SocketAddressStorage SocketGetPeerAddress(Socket *sock) +{ + if (sock->isServer) + { + return NULL; + } + if (sock->isIPv6) + { + return sock->addripv6; + } + else + { + return sock->addripv4; + } +} + +// Return the address-type appropriate host portion of a socket address +char *GetSocketAddressHost(SocketAddressStorage storage) +{ + assert(storage->address.ss_family == AF_INET || storage->address.ss_family == AF_INET6); + return SocketAddressToString((struct sockaddr_storage *) storage); +} + +// Return the address-type appropriate port(service) portion of a socket address +short GetSocketAddressPort(SocketAddressStorage storage) +{ + return ntohs(GetSocketPortPtr(storage)); +} + +// The accept function permits an incoming connection attempt on a socket. +// +// SocketChannel listener - The socket to listen for incoming connections on (i.e. server) +// SocketResult* out - The result of this function (if any, including errors) +// +// e.g. +// +// SocketResult connection; +// bool connected = false; +// if (!connected) +// { +// if (SocketAccept(server_res.socket.channel, &connection)) +// { +// connected = true; +// } +// } +Socket *SocketAccept(Socket *server, SocketConfig *config) +{ + if (!server->isServer || server->type == SOCKET_UDP) + { + return NULL; + } + struct sockaddr_storage sock_addr; + socklen_t sock_alen; + Socket * sock; + sock = AllocSocket(); + server->ready = 0; + sock_alen = sizeof(sock_addr); + sock->channel = accept(server->channel, (struct sockaddr *) &sock_addr, &sock_alen); + if (sock->channel == INVALID_SOCKET) + { + sock->status = SocketGetLastError(); + TraceLog(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(sock->status)); + SocketSetLastError(0); + SocketClose(sock); + return NULL; + } + (config->nonblocking) ? SocketSetNonBlocking(sock) : SocketSetBlocking(sock); + sock->isServer = false; + sock->ready = 0; + sock->type = server->type; + switch (sock_addr.ss_family) + { + case AF_INET: + { + struct sockaddr_in *s = ((struct sockaddr_in *) &sock_addr); + sock->addripv4 = (struct _SocketAddressIPv4 *) malloc(sizeof(*sock->addripv4)); + if (sock->addripv4 != NULL) + { + memset(sock->addripv4, 0, sizeof(*sock->addripv4)); + memcpy(&sock->addripv4->address, (struct sockaddr_in *) &s->sin_addr, sizeof(struct sockaddr_in)); + TraceLog(LOG_INFO, "Server: Got connection from %s::%hu", SocketAddressToString((struct sockaddr_storage *) s), + ntohs(sock->addripv4->address.sin_port)); + } + } + break; + case AF_INET6: + { + struct sockaddr_in6 *s = ((struct sockaddr_in6 *) &sock_addr); + sock->addripv6 = (struct _SocketAddressIPv6 *) malloc(sizeof(*sock->addripv6)); + if (sock->addripv6 != NULL) + { + memset(sock->addripv6, 0, sizeof(*sock->addripv6)); + memcpy(&sock->addripv6->address, (struct sockaddr_in6 *) &s->sin6_addr, sizeof(struct sockaddr_in6)); + TraceLog(LOG_INFO, "Server: Got connection from %s::%hu", SocketAddressToString((struct sockaddr_storage *) s), + ntohs(sock->addripv6->address.sin6_port)); + } + } + break; + } + return sock; +} + +// Verify that the channel is in the valid range +static int ValidChannel(int channel) +{ + if ((channel < 0) || (channel >= SOCKET_MAX_UDPCHANNELS)) + { + TraceLog(LOG_WARNING, "Invalid channel"); + return 0; + } + return 1; +} + +// Set the socket channel +int SocketSetChannel(Socket *socket, int channel, const IPAddress *address) +{ + struct UDPChannel *binding; + if (socket == NULL) + { + TraceLog(LOG_WARNING, "Passed a NULL socket"); + return (-1); + } + if (channel == -1) + { + for (channel = 0; channel < SOCKET_MAX_UDPCHANNELS; ++channel) + { + binding = &socket->binding[channel]; + if (binding->numbound < SOCKET_MAX_UDPADDRESSES) + { + break; + } + } + } + else + { + if (!ValidChannel(channel)) + { + return (-1); + } + binding = &socket->binding[channel]; + } + if (binding->numbound == SOCKET_MAX_UDPADDRESSES) + { + TraceLog(LOG_WARNING, "No room for new addresses"); + return (-1); + } + binding->address[binding->numbound++] = *address; + return (channel); +} + +// Remove the socket channel +void SocketUnsetChannel(Socket *socket, int channel) +{ + if ((channel >= 0) && (channel < SOCKET_MAX_UDPCHANNELS)) + { + socket->binding[channel].numbound = 0; + } +} + +/* Allocate/free a single UDP packet 'size' bytes long. + The new packet is returned, or NULL if the function ran out of memory. + */ +SocketDataPacket *AllocPacket(int size) +{ + SocketDataPacket *packet; + int error; + + error = 1; + packet = (SocketDataPacket *) malloc(sizeof(*packet)); + if (packet != NULL) + { + packet->maxlen = size; + packet->data = (uint8_t *) malloc(size); + if (packet->data != NULL) + { + error = 0; + } + } + if (error) + { + FreePacket(packet); + packet = NULL; + } + return (packet); +} + +int ResizePacket(SocketDataPacket *packet, int newsize) +{ + uint8_t *newdata; + + newdata = (uint8_t *) malloc(newsize); + if (newdata != NULL) + { + free(packet->data); + packet->data = newdata; + packet->maxlen = newsize; + } + return (packet->maxlen); +} + +void FreePacket(SocketDataPacket *packet) +{ + if (packet) + { + free(packet->data); + free(packet); + } +} + +/* Allocate/Free a UDP packet vector (array of packets) of 'howmany' packets, + each 'size' bytes long. + A pointer to the packet array is returned, or NULL if the function ran out + of memory. + */ +SocketDataPacket **AllocPacketList(int howmany, int size) +{ + SocketDataPacket **packetV; + + packetV = (SocketDataPacket **) malloc((howmany + 1) * sizeof(*packetV)); + if (packetV != NULL) + { + int i; + for (i = 0; i < howmany; ++i) + { + packetV[i] = AllocPacket(size); + if (packetV[i] == NULL) + { + break; + } + } + packetV[i] = NULL; + + if (i != howmany) + { + FreePacketList(packetV); + packetV = NULL; + } + } + return (packetV); +} + +void FreePacketList(SocketDataPacket **packetV) +{ + if (packetV) + { + int i; + for (i = 0; packetV[i]; ++i) + { + FreePacket(packetV[i]); + } + free(packetV); + } +} + +// Send 'len' bytes of 'data' over the non-server socket 'sock' +// +// Example +int SocketSend(Socket *sock, const void *datap, int length) +{ + int sent = 0; + int left = length; + int status = -1; + int numsent = 0; + const unsigned char *data = (const unsigned char *) datap; + + // Server sockets are for accepting connections only + if (sock->isServer) + { + TraceLog(LOG_WARNING, "Cannot send information on a server socket"); + return -1; + } + + // Which socket are we trying to send data on + switch (sock->type) + { + case SOCKET_TCP: + { + SocketSetLastError(0); + do + { + length = send(sock->channel, (const char *) data, left, 0); + if (length > 0) + { + sent += length; + left -= length; + data += length; + } + } while ((left > 0) && // While we still have bytes left to send + ((length > 0) || // The amount of bytes we actually sent is > 0 + (SocketGetLastError() == WSAEINTR)) // The socket was interupted + ); + + if (length == SOCKET_ERROR) + { + sock->status = SocketGetLastError(); + TraceLog(LOG_DEBUG, "Socket Error: %s", SocketErrorCodeToString(sock->status)); + SocketSetLastError(0); + } + else + { + TraceLog(LOG_DEBUG, "Successfully sent \"%s\" (%d bytes)", datap, sent); + } + + return sent; + } + break; + case SOCKET_UDP: + { + SocketSetLastError(0); + if (sock->isIPv6) + { + status = sendto(sock->channel, (const char *) data, left, 0, + (struct sockaddr *) &sock->addripv6->address, + sizeof(sock->addripv6->address)); + } + else + { + status = sendto(sock->channel, (const char *) data, left, 0, + (struct sockaddr *) &sock->addripv4->address, + sizeof(sock->addripv4->address)); + } + if (sent >= 0) + { + sock->status = 0; + ++numsent; + TraceLog(LOG_DEBUG, "Successfully sent \"%s\" (%d bytes)", datap, status); + } + else + { + sock->status = SocketGetLastError(); + TraceLog(LOG_DEBUG, "Socket Error: %s", SocketGetLastErrorString(sock->status)); + SocketSetLastError(0); + return 0; + } + return numsent; + } + break; + default: break; + } + return -1; +} + +// Receive up to 'maxlen' bytes of data over the non-server socket 'sock', +// and store them in the buffer pointed to by 'data'. +// This function returns the actual amount of data received. If the return +// value is less than or equal to zero, then either the remote connection was +// closed, or an unknown socket error occurred. +int SocketReceive(Socket *sock, void *data, int maxlen) +{ + int len = 0; + int numrecv = 0; + int status = 0; + socklen_t sock_len; + struct sockaddr_storage sock_addr; + char ip[INET6_ADDRSTRLEN]; + + // Server sockets are for accepting connections only + if (sock->isServer && sock->type == SOCKET_TCP) + { + sock->status = SocketGetLastError(); + TraceLog(LOG_DEBUG, "Socket Error: %s", + "Server sockets cannot be used to receive data"); + SocketSetLastError(0); + return 0; + } + + // Which socket are we trying to send data on + switch (sock->type) + { + case SOCKET_TCP: + { + SocketSetLastError(0); + do + { + len = recv(sock->channel, (char *) data, maxlen, 0); + } while (SocketGetLastError() == WSAEINTR); + + if (len > 0) + { + // Who sent the packet? + if (sock->type == SOCKET_UDP) + { + TraceLog( + LOG_DEBUG, "Received data from: %s", inet_ntop(sock_addr.ss_family, GetSocketAddressPtr((struct sockaddr *) &sock_addr), ip, sizeof(ip))); + } + ((unsigned char *) data)[len] = '\0'; // Add null terminating character to the end of the stream + TraceLog(LOG_DEBUG, "Received \"%s\" (%d bytes)", data, len); + } + sock->ready = 0; + return len; + } + break; + case SOCKET_UDP: + { + SocketSetLastError(0); + sock_len = sizeof(sock_addr); + status = recvfrom(sock->channel, // The receving channel + data, // A pointer to the data buffer to fill + maxlen, // The max length of the data to fill + 0, // Flags + (struct sockaddr *) &sock_addr, // The address of the recevied data + &sock_len // The length of the received data address + ); + if (status >= 0) + { + ++numrecv; + } + else + { + sock->status = SocketGetLastError(); + switch (sock->status) + { + case WSAEWOULDBLOCK: { break; + } + default: + { + TraceLog(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(sock->status)); + break; + } + } + SocketSetLastError(0); + return 0; + } + sock->ready = 0; + return numrecv; + } + break; + } + return -1; +} + +// Does the socket have it's 'ready' flag set? +bool IsSocketReady(Socket *sock) +{ + return (sock != NULL) && (sock->ready); +} + +// Check if the socket is considered connected +bool IsSocketConnected(Socket *sock) +{ +#if PLATFORM_WINDOWS + FD_SET writefds; + FD_ZERO(&writefds); + FD_SET(sock->channel, &writefds); + struct timeval timeout; + timeout.tv_sec = 1; + timeout.tv_usec = 1000000000UL; + int total = select(0, NULL, &writefds, NULL, &timeout); + if (total == -1) + { // Error + sock->status = SocketGetLastError(); + TraceLog(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(sock->status)); + SocketSetLastError(0); + } + else if (total == 0) + { // Timeout + return false; + } + else + { + if (FD_ISSET(sock->channel, &writefds)) + { + return true; + } + } + return false; +#else + return true; +#endif +} + +// Allocate and return a SocketResult struct +SocketResult *AllocSocketResult() +{ + struct SocketResult *res; + res = (struct SocketResult *) malloc(sizeof(*res)); + if (res != NULL) + { + memset(res, 0, sizeof(*res)); + if ((res->socket = AllocSocket()) == NULL) + { + free(res); + res = NULL; + } + } + return res; +} + +// Free an allocated SocketResult +void FreeSocketResult(SocketResult **result) +{ + if (*result != NULL) + { + if ((*result)->socket != NULL) + { + FreeSocket(&((*result)->socket)); + } + free(*result); + *result = NULL; + } +} + +// Allocate a Socket +Socket *AllocSocket() +{ + // Allocate a socket if one already hasn't been + struct Socket *sock; + sock = (Socket *) malloc(sizeof(*sock)); + if (sock != NULL) + { + memset(sock, 0, sizeof(*sock)); + } + else + { + TraceLog( + LOG_WARNING, "Ran out of memory attempting to allocate a socket"); + SocketClose(sock); + free(sock); + sock = NULL; + } + return sock; +} + +// Free an allocated Socket +void FreeSocket(Socket **sock) +{ + if (*sock != NULL) + { + free(*sock); + *sock = NULL; + } +} + +// Allocate a SocketSet +SocketSet *AllocSocketSet(int max) +{ + struct SocketSet *set; + int i; + + set = (struct SocketSet *) malloc(sizeof(*set)); + if (set != NULL) + { + set->numsockets = 0; + set->maxsockets = max; + set->sockets = (struct Socket **) malloc(max * sizeof(*set->sockets)); + if (set->sockets != NULL) + { + for (i = 0; i < max; ++i) + { + set->sockets[i] = NULL; + } + } + else + { + free(set); + set = NULL; + } + } + return (set); +} + +// Free an allocated SocketSet +void FreeSocketSet(SocketSet *set) +{ + if (set) + { + free(set->sockets); + free(set); + } +} + +// Add a Socket "sock" to the SocketSet "set" +int AddSocket(SocketSet *set, Socket *sock) +{ + if (sock != NULL) + { + if (set->numsockets == set->maxsockets) + { + TraceLog(LOG_DEBUG, "Socket Error: %s", "SocketSet is full"); + SocketSetLastError(0); + return (-1); + } + set->sockets[set->numsockets++] = (struct Socket *) sock; + } + else + { + TraceLog(LOG_DEBUG, "Socket Error: %s", "Socket was null"); + SocketSetLastError(0); + return (-1); + } + return (set->numsockets); +} + +// Remove a Socket "sock" to the SocketSet "set" +int RemoveSocket(SocketSet *set, Socket *sock) +{ + int i; + + if (sock != NULL) + { + for (i = 0; i < set->numsockets; ++i) + { + if (set->sockets[i] == (struct Socket *) sock) + { + break; + } + } + if (i == set->numsockets) + { + TraceLog(LOG_DEBUG, "Socket Error: %s", "Socket not found"); + SocketSetLastError(0); + return (-1); + } + --set->numsockets; + for (; i < set->numsockets; ++i) + { + set->sockets[i] = set->sockets[i + 1]; + } + } + return (set->numsockets); +} + +// Check the sockets in the socket set for pending information +int CheckSockets(SocketSet *set, unsigned int timeout) +{ + int i; + SOCKET maxfd; + int retval; + struct timeval tv; + fd_set mask; + + /* Find the largest file descriptor */ + maxfd = 0; + for (i = set->numsockets - 1; i >= 0; --i) + { + if (set->sockets[i]->channel > maxfd) + { + maxfd = set->sockets[i]->channel; + } + } + + // Check the file descriptors for available data + do + { + SocketSetLastError(0); + + // Set up the mask of file descriptors + FD_ZERO(&mask); + for (i = set->numsockets - 1; i >= 0; --i) + { + FD_SET(set->sockets[i]->channel, &mask); + } // Set up the timeout + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000; + + /* Look! */ + retval = select(maxfd + 1, &mask, NULL, NULL, &tv); + } while (SocketGetLastError() == WSAEINTR); + + // Mark all file descriptors ready that have data available + if (retval > 0) + { + for (i = set->numsockets - 1; i >= 0; --i) + { + if (FD_ISSET(set->sockets[i]->channel, &mask)) + { + set->sockets[i]->ready = 1; + } + } + } + return (retval); +} + +// Allocate an AddressInformation +AddressInformation AllocAddress() +{ + AddressInformation addressInfo = NULL; + addressInfo = (AddressInformation) calloc(1, sizeof(*addressInfo)); + if (addressInfo != NULL) + { + addressInfo->addr.ai_addr = (struct sockaddr *) calloc(1, sizeof(struct sockaddr)); + if (addressInfo->addr.ai_addr == NULL) + { + TraceLog(LOG_WARNING, + "Failed to allocate memory for \"struct sockaddr\""); + } + } + else + { + TraceLog(LOG_WARNING, + "Failed to allocate memory for \"struct AddressInformation\""); + } + return addressInfo; +} + +// Free an AddressInformation struct +void FreeAddress(AddressInformation *addressInfo) +{ + if (*addressInfo != NULL) + { + if ((*addressInfo)->addr.ai_addr != NULL) + { + free((*addressInfo)->addr.ai_addr); + (*addressInfo)->addr.ai_addr = NULL; + } + free(*addressInfo); + *addressInfo = NULL; + } +} + +// Allocate a list of AddressInformation +AddressInformation *AllocAddressList(int size) +{ + AddressInformation *addr; + addr = (AddressInformation *) malloc(size * sizeof(AddressInformation)); + return addr; +} + +// Opaque datatype accessor addrinfo->ai_family +int GetAddressFamily(AddressInformation address) +{ + return address->addr.ai_family; +} + +// Opaque datatype accessor addrinfo->ai_socktype +int GetAddressSocketType(AddressInformation address) +{ + return address->addr.ai_socktype; +} + +// Opaque datatype accessor addrinfo->ai_protocol +int GetAddressProtocol(AddressInformation address) +{ + return address->addr.ai_protocol; +} + +// Opaque datatype accessor addrinfo->ai_canonname +char *GetAddressCanonName(AddressInformation address) +{ + return address->addr.ai_canonname; +} + +// Opaque datatype accessor addrinfo->ai_addr +char *GetAddressHostAndPort(AddressInformation address, char *outhost, int *outport) +{ + char * ip[INET6_ADDRSTRLEN]; + char * result = NULL; + struct sockaddr_storage *storage = (struct sockaddr_storage *) address->addr.ai_addr; + switch (storage->ss_family) + { + case AF_INET: + { + struct sockaddr_in *s = ((struct sockaddr_in *) address->addr.ai_addr); + result = inet_ntop(AF_INET, &s->sin_addr, ip, INET_ADDRSTRLEN); + *outport = ntohs(s->sin_port); + } + break; + case AF_INET6: + { + struct sockaddr_in6 *s = ((struct sockaddr_in6 *) address->addr.ai_addr); + result = inet_ntop(AF_INET6, &s->sin6_addr, ip, INET6_ADDRSTRLEN); + *outport = ntohs(s->sin6_port); + } + break; + } + if (result == NULL) + { + TraceLog(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(SocketGetLastError())); + SocketSetLastError(0); + } + else + { + strcpy(outhost, result); + } + return result; +} + +// +void PacketSend(Packet *packet) +{ + printf("Sending packet (%s) with size %d\n", packet->data, packet->size); +} + +// +void PacketReceive(Packet *packet) +{ + printf("Receiving packet (%s) with size %d\n", packet->data, packet->size); +} + +// +void PacketWrite16(Packet *packet, uint16_t value) +{ + printf("Original: 0x%04" PRIX16 " - %" PRIu16 "\n", value, value); + uint8_t *data = packet->data + packet->offs; + *data++ = (uint8_t)(value >> 8); + *data++ = (uint8_t)(value); + packet->size += sizeof(uint16_t); + packet->offs += sizeof(uint16_t); + printf("Network: 0x%04" PRIX16 " - %" PRIu16 "\n", (uint16_t) *data, (uint16_t) *data); +} + +// +void PacketWrite32(Packet *packet, uint32_t value) +{ + printf("Original: 0x%08" PRIX32 " - %" PRIu32 "\n", value, value); + uint8_t *data = packet->data + packet->offs; + *data++ = (uint8_t)(value >> 24); + *data++ = (uint8_t)(value >> 16); + *data++ = (uint8_t)(value >> 8); + *data++ = (uint8_t)(value); + packet->size += sizeof(uint32_t); + packet->offs += sizeof(uint32_t); + printf("Network: 0x%08" PRIX32 " - %" PRIu32 "\n", + (uint32_t)(((intptr_t) packet->data) - packet->offs), + (uint32_t)(((intptr_t) packet->data) - packet->offs)); +} + +// +void PacketWrite64(Packet *packet, uint64_t value) +{ + printf("Original: 0x%016" PRIX64 " - %" PRIu64 "\n", value, value); + uint8_t *data = packet->data + packet->offs; + *data++ = (uint8_t)(value >> 56); + *data++ = (uint8_t)(value >> 48); + *data++ = (uint8_t)(value >> 40); + *data++ = (uint8_t)(value >> 32); + *data++ = (uint8_t)(value >> 24); + *data++ = (uint8_t)(value >> 16); + *data++ = (uint8_t)(value >> 8); + *data++ = (uint8_t)(value); + packet->size += sizeof(uint64_t); + packet->offs += sizeof(uint64_t); + printf("Network: 0x%016" PRIX64 " - %" PRIu64 "\n", + (uint64_t)(packet->data - packet->offs), + (uint64_t)(packet->data - packet->offs)); +} + +// +uint16_t PacketRead16(Packet *packet) +{ + uint8_t *data = packet->data + packet->offs; + packet->size += sizeof(uint16_t); + packet->offs += sizeof(uint16_t); + uint16_t value = ((uint16_t) data[0] << 8) | data[1]; + printf("Original: 0x%04" PRIX16 " - %" PRIu16 "\n", value, value); + return value; +} + +// +uint32_t PacketRead32(Packet *packet) +{ + uint8_t *data = packet->data + packet->offs; + packet->size += sizeof(uint32_t); + packet->offs += sizeof(uint32_t); + uint32_t value = ((uint32_t) data[0] << 24) | ((uint32_t) data[1] << 16) | ((uint32_t) data[2] << 8) | data[3]; + printf("Original: 0x%08" PRIX32 " - %" PRIu32 "\n", value, value); + return value; +} + +// +uint64_t PacketRead64(Packet *packet) +{ + uint8_t *data = packet->data + packet->offs; + packet->size += sizeof(uint64_t); + packet->offs += sizeof(uint64_t); + uint64_t value = ((uint64_t) data[0] << 56) | ((uint64_t) data[1] << 48) | ((uint64_t) data[2] << 40) | ((uint64_t) data[3] << 32) | ((uint64_t) data[4] << 24) | ((uint64_t) data[5] << 16) | ((uint64_t) data[6] << 8) | data[7]; + printf("Original: 0x%016" PRIX64 " - %" PRIu64 "\n", value, value); + return value; +} diff --git a/src/rnet.h b/src/rnet.h new file mode 100644 index 00000000..c2a14015 --- /dev/null +++ b/src/rnet.h @@ -0,0 +1,228 @@ +/********************************************************************************************** +* +* rnet - Provides cross-platform network defines, macros etc +* +* DEPENDENCIES: +* - Used for cross-platform type specifiers +* +* INSPIRED BY: +* SFML Sockets - https://www.sfml-dev.org/documentation/2.5.1/classsf_1_1Socket.php +* SDL_net - https://www.libsdl.org/projects/SDL_net/ +* BSD Sockets - https://www.gnu.org/software/libc/manual/html_node/Sockets.html +* BEEJ - https://beej.us/guide/bgnet/html/single/bgnet.html +* Winsock2 - https://docs.microsoft.com/en-us/windows/desktop/api/winsock2 +* +* +* CONTRIBUTORS: +* Jak Barnes (github: @syphonx) (Feb. 2019): +* - Initial version +* +* LICENSE: zlib/libpng +* +* 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. +* +* 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. +* +**********************************************************************************************/ + +//---------------------------------------------------------------------------------- +// Platform type sizes +//---------------------------------------------------------------------------------- + +#include + +//---------------------------------------------------------------------------------- +// Undefine any conflicting windows.h symbols +//---------------------------------------------------------------------------------- + +// If defined, the following flags inhibit definition of the indicated items. +#define NOGDICAPMASKS // CC_*, LC_*, PC_*, CP_*, TC_*, RC_ +#define NOVIRTUALKEYCODES // VK_* +#define NOWINMESSAGES // WM_*, EM_*, LB_*, CB_* +#define NOWINSTYLES // WS_*, CS_*, ES_*, LBS_*, SBS_*, CBS_* +#define NOSYSMETRICS // SM_* +#define NOMENUS // MF_* +#define NOICONS // IDI_* +#define NOKEYSTATES // MK_* +#define NOSYSCOMMANDS // SC_* +#define NORASTEROPS // Binary and Tertiary raster ops +#define NOSHOWWINDOW // SW_* +#define OEMRESOURCE // OEM Resource values +#define NOATOM // Atom Manager routines +#define NOCLIPBOARD // Clipboard routines +#define NOCOLOR // Screen colors +#define NOCTLMGR // Control and Dialog routines +#define NODRAWTEXT // DrawText() and DT_* +#define NOGDI // All GDI defines and routines +#define NOKERNEL // All KERNEL defines and routines +#define NOUSER // All USER defines and routines +#define NONLS // All NLS defines and routines +#define NOMB // MB_* and MessageBox() +#define NOMEMMGR // GMEM_*, LMEM_*, GHND, LHND, associated routines +#define NOMETAFILE // typedef METAFILEPICT +#define NOMINMAX // Macros min(a,b) and max(a,b) +#define NOMSG // typedef MSG and associated routines +#define NOOPENFILE // OpenFile(), OemToAnsi, AnsiToOem, and OF_* +#define NOSCROLL // SB_* and scrolling routines +#define NOSERVICE // All Service Controller routines, SERVICE_ equates, etc. +#define NOSOUND // Sound driver routines +#define NOTEXTMETRIC // typedef TEXTMETRIC and associated routines +#define NOWH // SetWindowsHook and WH_* +#define NOWINOFFSETS // GWL_*, GCL_*, associated routines +#define NOCOMM // COMM driver routines +#define NOKANJI // Kanji support stuff. +#define NOHELP // Help engine interface. +#define NOPROFILER // Profiler interface. +#define NODEFERWINDOWPOS // DeferWindowPos routines +#define NOMCX // Modem Configuration Extensions +#define MMNOSOUND + +//---------------------------------------------------------------------------------- +// Platform defines +//---------------------------------------------------------------------------------- + +#define PLATFORM_WINDOWS 1 +#define PLATFORM_LINUX 2 + +#if defined(__WIN32__) || defined(WIN32) +# define PLATFORM PLATFORM_WINDOWS +#elif defined(_LINUX) +# define PLATFORM PLATFORM_LINUX +#endif + +//---------------------------------------------------------------------------------- +// Platform type definitions +// From: https://github.com/DFHack/clsocket/blob/master/src/Host.h +//---------------------------------------------------------------------------------- + +#ifdef WIN32 +typedef int socklen_t; +#endif + +#ifndef RESULT_SUCCESS +# define RESULT_SUCCESS 0 +#endif // RESULT_SUCCESS + +#ifndef RESULT_FAILURE +# define RESULT_FAILURE 1 +#endif // RESULT_FAILURE + +#ifndef htonll +# ifdef _BIG_ENDIAN +# define htonll(x) (x) +# define ntohll(x) (x) +# else +# define htonll(x) ((((uint64) htonl(x)) << 32) + htonl(x >> 32)) +# define ntohll(x) ((((uint64) ntohl(x)) << 32) + ntohl(x >> 32)) +# endif // _BIG_ENDIAN +#endif // htonll + +//---------------------------------------------------------------------------------- +// Platform specific network includes +// From: https://github.com/SDL-mirror/SDL_net/blob/master/SDLnetsys.h +//---------------------------------------------------------------------------------- + +// Include system network headers + +#ifdef _WIN32 +# pragma comment(lib, "ws2_32.lib") +# define __USE_W32_SOCKETS +# define WIN32_LEAN_AND_MEAN +# include +# include +# include +# define IPTOS_LOWDELAY 0x10 +#else /* UNIX */ +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +#endif /* WIN32 */ + +#ifndef INVALID_SOCKET +# define INVALID_SOCKET ~(0) +#endif + +#ifndef __USE_W32_SOCKETS +# define closesocket close +# define SOCKET int +# define INVALID_SOCKET -1 +# define SOCKET_ERROR -1 +#endif + +#ifdef __USE_W32_SOCKETS +# ifndef EINTR +# define EINTR WSAEINTR +# endif +#endif + +//---------------------------------------------------------------------------------- +// Module defines +//---------------------------------------------------------------------------------- + +// Network connection related defines +#define SOCKET_MAX_SET_SIZE (32) // Maximum sockets in a set +#define SOCKET_MAX_QUEUE_SIZE (16) // Maximum socket queue size + +// Network address related defines +#define ADDRESS_IPV4_ADDRSTRLEN (22) // IPv4 string length +#define ADDRESS_IPV6_ADDRSTRLEN (65) // IPv6 string length +#define ADDRESS_TYPE_ANY (0) // AF_UNSPEC +#define ADDRESS_TYPE_IPV4 (2) // AF_INET +#define ADDRESS_TYPE_IPV6 (23) // AF_INET6 +#define ADDRESS_MAXHOST (1025) // Max size of a fully-qualified domain name +#define ADDRESS_MAXSERV (32) // Max size of a service name + +// Network address related defines +#define ADDRESS_ANY ((unsigned long) 0x00000000) +#define ADDRESS_LOOPBACK (0x7f000001) +#define ADDRESS_BROADCAST ((unsigned long) 0xffffffff) +#define ADDRESS_NONE (0xffffffff) + +// Address resolution related defines +#if defined(_WIN32) + #define ADDRESS_INFO_PASSIVE (0x00000001) // Socket address will be used in bind() call + #define ADDRESS_INFO_CANONNAME (0x00000002) // Return canonical name in first ai_canonname + #define ADDRESS_INFO_NUMERICHOST (0x00000004) // Nodename must be a numeric address string + #define ADDRESS_INFO_NUMERICSERV (0x00000008) // Servicename must be a numeric port number + #define ADDRESS_INFO_DNS_ONLY (0x00000010) // Restrict queries to unicast DNS only (no LLMNR, netbios, etc.) + #define ADDRESS_INFO_ALL (0x00000100) // Query both IP6 and IP4 with AI_V4MAPPED + #define ADDRESS_INFO_ADDRCONFIG (0x00000400) // Resolution only if global address configured + #define ADDRESS_INFO_V4MAPPED (0x00000800) // On v6 failure, query v4 and convert to V4MAPPED format + #define ADDRESS_INFO_NON_AUTHORITATIVE (0x00004000) // LUP_NON_AUTHORITATIVE + #define ADDRESS_INFO_SECURE (0x00008000) // LUP_SECURE + #define ADDRESS_INFO_RETURN_PREFERRED_NAMES (0x00010000) // LUP_RETURN_PREFERRED_NAMES + #define ADDRESS_INFO_FQDN (0x00020000) // Return the FQDN in ai_canonname + #define ADDRESS_INFO_FILESERVER (0x00040000) // Resolving fileserver name resolution + #define ADDRESS_INFO_DISABLE_IDN_ENCODING (0x00080000) // Disable Internationalized Domain Names handling + #define ADDRESS_INFO_EXTENDED (0x80000000) // Indicates this is extended ADDRINFOEX(2/..) struct + #define ADDRESS_INFO_RESOLUTION_HANDLE (0x40000000) // Request resolution handle +#endif + +// Network resolution related defines +#define NAME_INFO_DEFAULT (0x00) // No flags set +#define NAME_INFO_NOFQDN (0x01) // Only return nodename portion for local hosts +#define NAME_INFO_NUMERICHOST (0x02) // Return numeric form of the host's address +#define NAME_INFO_NAMEREQD (0x04) // Error if the host's name not in DNS +#define NAME_INFO_NUMERICSERV (0x08) // Return numeric form of the service (port #) +#define NAME_INFO_DGRAM (0x10) // Service is a datagram service \ No newline at end of file -- cgit v1.2.3 From f7d978e7261d3a8cb715108095f582d8908ac647 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 22 Apr 2019 20:27:54 +0200 Subject: Reviewed rnet inclusion Move to own header for a more deep review of the module --- src/raylib.h | 167 +------------------------------------------- src/rnet.c | 21 +++--- src/rnet.h | 223 ++++++++++++++++++++++++++++++++++++++++++++++++++++------- 3 files changed, 210 insertions(+), 201 deletions(-) (limited to 'src/raylib.h') diff --git a/src/raylib.h b/src/raylib.h index 710f69b6..29850efd 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -75,7 +75,6 @@ #define RAYLIB_H #include // Required for: va_list - Only used by TraceLogCallback -#include // Required for rnet #if defined(_WIN32) && defined(BUILD_LIBTYPE_SHARED) #define RLAPI __declspec(dllexport) // We are building raylib as a Win32 shared library (.dll) @@ -448,100 +447,6 @@ typedef struct VrDeviceInfo { float lensDistortionValues[4]; // HMD lens distortion constant parameters float chromaAbCorrection[4]; // HMD chromatic aberration correction parameters } VrDeviceInfo; - -// Network typedefs -typedef uint32_t SocketChannel; -typedef struct _AddressInformation * AddressInformation; -typedef struct _SocketAddress * SocketAddress; -typedef struct _SocketAddressIPv4 * SocketAddressIPv4; -typedef struct _SocketAddressIPv6 * SocketAddressIPv6; -typedef struct _SocketAddressStorage *SocketAddressStorage; - -// IPAddress definition (in network byte order) -typedef struct IPAddress -{ - unsigned long host; /* 32-bit IPv4 host address */ - unsigned short port; /* 16-bit protocol port */ -} IPAddress; - -// An option ID, value, sizeof(value) tuple for setsockopt(2). -typedef struct SocketOpt -{ - int id; - void *value; - int valueLen; -} SocketOpt; - -typedef enum -{ - SOCKET_TCP = 0, // SOCK_STREAM - SOCKET_UDP = 1 // SOCK_DGRAM -} SocketType; - -typedef struct UDPChannel -{ - int numbound; // The total number of addresses this channel is bound to - IPAddress address[SOCKET_MAX_UDPADDRESSES]; // The list of remote addresses this channel is bound to -} UDPChannel; - -typedef struct Socket -{ - int ready; // Is the socket ready? i.e. has information - int status; // The last status code to have occured using this socket - bool isServer; // Is this socket a server socket (i.e. TCP/UDP Listen Server) - SocketChannel channel; // The socket handle id - SocketType type; // Is this socket a TCP or UDP socket? - bool isIPv6; // Is this socket address an ipv6 address? - SocketAddressIPv4 addripv4; // The host/target IPv4 for this socket (in network byte order) - SocketAddressIPv6 addripv6; // The host/target IPv6 for this socket (in network byte order) - - struct UDPChannel binding[SOCKET_MAX_UDPCHANNELS]; // The amount of channels (if UDP) this socket is bound to -} Socket; - -typedef struct SocketSet -{ - int numsockets; - int maxsockets; - struct Socket **sockets; -} SocketSet; - -typedef struct SocketDataPacket -{ - int channel; // The src/dst channel of the packet - unsigned char *data; // The packet data - int len; // The length of the packet data - int maxlen; // The size of the data buffer - int status; // packet status after sending - IPAddress address; // The source/dest address of an incoming/outgoing packet -} SocketDataPacket; - -// Configuration for a socket. -typedef struct SocketConfig -{ - char * host; // The host address in xxx.xxx.xxx.xxx form - char * port; // The target port/service in the form "http" or "25565" - bool server; // Listen for incoming clients? - SocketType type; // The type of socket, TCP/UDP - bool nonblocking; // non-blocking operation? - int backlog_size; // set a custom backlog size - SocketOpt sockopts[SOCKET_MAX_SOCK_OPTS]; -} SocketConfig; - -// Result from calling open with a given config. -typedef struct SocketResult -{ - int status; - Socket *socket; -} SocketResult; - -// -typedef struct Packet -{ - uint32_t size; // The total size of bytes in data - uint32_t offs; // The offset to data access - uint32_t maxs; // The max size of data - uint8_t *data; // Data stored in network byte order -} Packet; //---------------------------------------------------------------------------------- // Enumerators Definition @@ -1512,77 +1417,7 @@ RLAPI void SetAudioStreamPitch(AudioStream stream, float pitch); // Set pit // Network (Module: network) //------------------------------------------------------------------------------------ -// Initialisation and cleanup -RLAPI bool InitNetwork(void); -RLAPI void CloseNetwork(void); - -// Address API -RLAPI void ResolveIP(const char *ip, const char *service, int flags, char *outhost, char *outserv); -RLAPI int ResolveHost(const char *address, const char *service, int addressType, int flags, AddressInformation *outAddr); -RLAPI int GetAddressFamily(AddressInformation address); -RLAPI int GetAddressSocketType(AddressInformation address); -RLAPI int GetAddressProtocol(AddressInformation address); -RLAPI char* GetAddressCanonName(AddressInformation address); -RLAPI char* GetAddressHostAndPort(AddressInformation address, char *outhost, int *outport); -RLAPI void PrintAddressInfo(AddressInformation address); - -// Address Memory API -RLAPI AddressInformation AllocAddress(); -RLAPI void FreeAddress(AddressInformation *addressInfo); -RLAPI AddressInformation *AllocAddressList(int size); - -// Socket API -RLAPI bool SocketCreate(SocketConfig *config, SocketResult *result); -RLAPI bool SocketBind(SocketConfig *config, SocketResult *result); -RLAPI bool SocketListen(SocketConfig *config, SocketResult *result); -RLAPI bool SocketConnect(SocketConfig *config, SocketResult *result); -RLAPI Socket *SocketAccept(Socket *server, SocketConfig *config); - -// UDP Socket API -RLAPI int SocketSetChannel(Socket *socket, int channel, const IPAddress *address); -RLAPI void SocketUnsetChannel(Socket *socket, int channel); - -// UDP DataPacket API -RLAPI SocketDataPacket *AllocPacket(int size); -RLAPI int ResizePacket(SocketDataPacket *packet, int newsize); -RLAPI void FreePacket(SocketDataPacket *packet); -RLAPI SocketDataPacket **AllocPacketList(int count, int size); -RLAPI void FreePacketList(SocketDataPacket **packets); - -// General Socket API -RLAPI int SocketSend(Socket *sock, const void *datap, int len); -RLAPI int SocketReceive(Socket *sock, void *data, int maxlen); -RLAPI void SocketClose(Socket *sock); -RLAPI SocketAddressStorage SocketGetPeerAddress(Socket *sock); -RLAPI char* GetSocketAddressHost(SocketAddressStorage storage); -RLAPI short GetSocketAddressPort(SocketAddressStorage storage); - -// Socket Memory API -RLAPI Socket *AllocSocket(); -RLAPI void FreeSocket(Socket **sock); -RLAPI SocketResult *AllocSocketResult(); -RLAPI void FreeSocketResult(SocketResult **result); -RLAPI SocketSet *AllocSocketSet(int max); -RLAPI void FreeSocketSet(SocketSet *sockset); - -// Socket I/O API -RLAPI bool IsSocketReady(Socket *sock); -RLAPI bool IsSocketConnected(Socket *sock); -RLAPI int AddSocket(SocketSet *set, Socket *sock); -RLAPI int RemoveSocket(SocketSet *set, Socket *sock); -RLAPI int CheckSockets(SocketSet *set, unsigned int timeout); - -// Packet API -void PacketSend(Packet *packet); -void PacketReceive(Packet *packet); -void PacketWrite8(Packet *packet, uint16_t value); -void PacketWrite16(Packet *packet, uint16_t value); -void PacketWrite32(Packet *packet, uint32_t value); -void PacketWrite64(Packet *packet, uint64_t value); -uint16_t PacketRead8(Packet *packet); -uint16_t PacketRead16(Packet *packet); -uint32_t PacketRead32(Packet *packet); -uint64_t PacketRead64(Packet *packet); +// IN PROGRESS: Check rnet.h for reference #if defined(__cplusplus) } diff --git a/src/rnet.c b/src/rnet.c index 0a47b44f..07bc5ecc 100644 --- a/src/rnet.c +++ b/src/rnet.c @@ -10,13 +10,12 @@ * rnet.h - platform-specific network includes * * CONTRIBUTORS: -* Jak Barnes (github: @syphonx) (Feb. 2019): -* - Initial version +* Jak Barnes (github: @syphonx) (Feb. 2019) - Initial version * * * LICENSE: zlib/libpng * -* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) +* Copyright (c) 2019 Jak Barnes (github: @syphonx) 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. @@ -245,7 +244,7 @@ static bool IsSocketValid(Socket *sock) // Sets the error code that can be retrieved through the WSAGetLastError function. static void SocketSetLastError(int err) { -#if PLATFORM == PLATFORM_WINDOWS +#if defined(_WIN32) WSASetLastError(err); #else errno = err; @@ -255,7 +254,7 @@ static void SocketSetLastError(int err) // Returns the error status for the last Sockets operation that failed static int SocketGetLastError() { -#if PLATFORM == PLATFORM_WINDOWS +#if defined(_WIN32) return WSAGetLastError(); #else return errno; @@ -271,7 +270,7 @@ static char *SocketGetLastErrorString() // Returns a human-readable string representing the error message (err) static char *SocketErrorCodeToString(int err) { -#if PLATFORM == PLATFORM_WINDOWS +#if defined(_WIN32) static char gaiStrErrorBuffer[GAI_STRERROR_BUFFER_SIZE]; sprintf(gaiStrErrorBuffer, "%ws", gai_strerror(err)); return gaiStrErrorBuffer; @@ -496,7 +495,7 @@ static bool CreateSocket(SocketConfig *config, SocketResult *outresult) static bool SocketSetBlocking(Socket *sock) { bool ret = true; -#if PLATFORM == PLATFORM_WINDOWS +#if defined(_WIN32) unsigned long mode = 0; ret = ioctlsocket(sock->channel, FIONBIO, &mode); #else @@ -516,7 +515,7 @@ static bool SocketSetBlocking(Socket *sock) static bool SocketSetNonBlocking(Socket *sock) { bool ret = true; -#if PLATFORM == PLATFORM_WINDOWS +#if defined(_WIN32) unsigned long mode = 1; ret = ioctlsocket(sock->channel, FIONBIO, &mode); #else @@ -602,7 +601,7 @@ static void SocketSetHints(SocketConfig *config, struct addrinfo *hints) // Initialise the network (requires for windows platforms only) bool InitNetwork() { -#if PLATFORM == PLATFORM_WINDOWS +#if defined(_WIN32) WORD wVersionRequested; WSADATA wsaData; int err; @@ -635,7 +634,7 @@ bool InitNetwork() // Cleanup, and close the network void CloseNetwork() { -#if PLATFORM == PLATFORM_WINDOWS +#if defined(_WIN32) WSACleanup(); #endif } @@ -1592,7 +1591,7 @@ bool IsSocketReady(Socket *sock) // Check if the socket is considered connected bool IsSocketConnected(Socket *sock) { -#if PLATFORM_WINDOWS +#if defined(_WIN32) FD_SET writefds; FD_ZERO(&writefds); FD_SET(sock->channel, &writefds); diff --git a/src/rnet.h b/src/rnet.h index c2a14015..a2233a3c 100644 --- a/src/rnet.h +++ b/src/rnet.h @@ -14,12 +14,12 @@ * * * CONTRIBUTORS: -* Jak Barnes (github: @syphonx) (Feb. 2019): -* - Initial version +* Jak Barnes (github: @syphonx) (Feb. 2019) - Initial version +* * * LICENSE: zlib/libpng * -* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) +* Copyright (c) 2019 Jak Barnes (github: @syphonx) 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. @@ -38,16 +38,14 @@ * **********************************************************************************************/ -//---------------------------------------------------------------------------------- -// Platform type sizes -//---------------------------------------------------------------------------------- - -#include +#include // Required for limits +#include // Required for platform type sizes //---------------------------------------------------------------------------------- -// Undefine any conflicting windows.h symbols +// Defines and Macros //---------------------------------------------------------------------------------- +// Undefine any conflicting windows.h symbols // If defined, the following flags inhibit definition of the indicated items. #define NOGDICAPMASKS // CC_*, LC_*, PC_*, CP_*, TC_*, RC_ #define NOVIRTUALKEYCODES // VK_* @@ -90,19 +88,6 @@ #define NOMCX // Modem Configuration Extensions #define MMNOSOUND -//---------------------------------------------------------------------------------- -// Platform defines -//---------------------------------------------------------------------------------- - -#define PLATFORM_WINDOWS 1 -#define PLATFORM_LINUX 2 - -#if defined(__WIN32__) || defined(WIN32) -# define PLATFORM PLATFORM_WINDOWS -#elif defined(_LINUX) -# define PLATFORM PLATFORM_LINUX -#endif - //---------------------------------------------------------------------------------- // Platform type definitions // From: https://github.com/DFHack/clsocket/blob/master/src/Host.h @@ -137,7 +122,7 @@ typedef int socklen_t; // Include system network headers -#ifdef _WIN32 +#if defined(_WIN32) # pragma comment(lib, "ws2_32.lib") # define __USE_W32_SOCKETS # define WIN32_LEAN_AND_MEAN @@ -225,4 +210,194 @@ typedef int socklen_t; #define NAME_INFO_NUMERICHOST (0x02) // Return numeric form of the host's address #define NAME_INFO_NAMEREQD (0x04) // Error if the host's name not in DNS #define NAME_INFO_NUMERICSERV (0x08) // Return numeric form of the service (port #) -#define NAME_INFO_DGRAM (0x10) // Service is a datagram service \ No newline at end of file +#define NAME_INFO_DGRAM (0x10) // Service is a datagram service + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- + +// Network typedefs +typedef uint32_t SocketChannel; +typedef struct _AddressInformation * AddressInformation; +typedef struct _SocketAddress * SocketAddress; +typedef struct _SocketAddressIPv4 * SocketAddressIPv4; +typedef struct _SocketAddressIPv6 * SocketAddressIPv6; +typedef struct _SocketAddressStorage *SocketAddressStorage; + +// IPAddress definition (in network byte order) +typedef struct IPAddress +{ + unsigned long host; /* 32-bit IPv4 host address */ + unsigned short port; /* 16-bit protocol port */ +} IPAddress; + +// An option ID, value, sizeof(value) tuple for setsockopt(2). +typedef struct SocketOpt +{ + int id; + void *value; + int valueLen; +} SocketOpt; + +typedef enum +{ + SOCKET_TCP = 0, // SOCK_STREAM + SOCKET_UDP = 1 // SOCK_DGRAM +} SocketType; + +typedef struct UDPChannel +{ + int numbound; // The total number of addresses this channel is bound to + IPAddress address[SOCKET_MAX_UDPADDRESSES]; // The list of remote addresses this channel is bound to +} UDPChannel; + +typedef struct Socket +{ + int ready; // Is the socket ready? i.e. has information + int status; // The last status code to have occured using this socket + bool isServer; // Is this socket a server socket (i.e. TCP/UDP Listen Server) + SocketChannel channel; // The socket handle id + SocketType type; // Is this socket a TCP or UDP socket? + bool isIPv6; // Is this socket address an ipv6 address? + SocketAddressIPv4 addripv4; // The host/target IPv4 for this socket (in network byte order) + SocketAddressIPv6 addripv6; // The host/target IPv6 for this socket (in network byte order) + + struct UDPChannel binding[SOCKET_MAX_UDPCHANNELS]; // The amount of channels (if UDP) this socket is bound to +} Socket; + +typedef struct SocketSet +{ + int numsockets; + int maxsockets; + struct Socket **sockets; +} SocketSet; + +typedef struct SocketDataPacket +{ + int channel; // The src/dst channel of the packet + unsigned char *data; // The packet data + int len; // The length of the packet data + int maxlen; // The size of the data buffer + int status; // packet status after sending + IPAddress address; // The source/dest address of an incoming/outgoing packet +} SocketDataPacket; + +// Configuration for a socket. +typedef struct SocketConfig +{ + char * host; // The host address in xxx.xxx.xxx.xxx form + char * port; // The target port/service in the form "http" or "25565" + bool server; // Listen for incoming clients? + SocketType type; // The type of socket, TCP/UDP + bool nonblocking; // non-blocking operation? + int backlog_size; // set a custom backlog size + SocketOpt sockopts[SOCKET_MAX_SOCK_OPTS]; +} SocketConfig; + +// Result from calling open with a given config. +typedef struct SocketResult +{ + int status; + Socket *socket; +} SocketResult; + +// +typedef struct Packet +{ + uint32_t size; // The total size of bytes in data + uint32_t offs; // The offset to data access + uint32_t maxs; // The max size of data + uint8_t *data; // Data stored in network byte order +} Packet; + + +#ifdef __cplusplus +extern "C" { // Prevents name mangling of functions +#endif + +//---------------------------------------------------------------------------------- +// Global Variables Definition +//---------------------------------------------------------------------------------- +//... + +//---------------------------------------------------------------------------------- +// Module Functions Declaration +//---------------------------------------------------------------------------------- + +// Initialisation and cleanup +RLAPI bool InitNetwork(void); +RLAPI void CloseNetwork(void); + +// Address API +RLAPI void ResolveIP(const char *ip, const char *service, int flags, char *outhost, char *outserv); +RLAPI int ResolveHost(const char *address, const char *service, int addressType, int flags, AddressInformation *outAddr); +RLAPI int GetAddressFamily(AddressInformation address); +RLAPI int GetAddressSocketType(AddressInformation address); +RLAPI int GetAddressProtocol(AddressInformation address); +RLAPI char* GetAddressCanonName(AddressInformation address); +RLAPI char* GetAddressHostAndPort(AddressInformation address, char *outhost, int *outport); +RLAPI void PrintAddressInfo(AddressInformation address); + +// Address Memory API +RLAPI AddressInformation AllocAddress(); +RLAPI void FreeAddress(AddressInformation *addressInfo); +RLAPI AddressInformation *AllocAddressList(int size); + +// Socket API +RLAPI bool SocketCreate(SocketConfig *config, SocketResult *result); +RLAPI bool SocketBind(SocketConfig *config, SocketResult *result); +RLAPI bool SocketListen(SocketConfig *config, SocketResult *result); +RLAPI bool SocketConnect(SocketConfig *config, SocketResult *result); +RLAPI Socket *SocketAccept(Socket *server, SocketConfig *config); + +// UDP Socket API +RLAPI int SocketSetChannel(Socket *socket, int channel, const IPAddress *address); +RLAPI void SocketUnsetChannel(Socket *socket, int channel); + +// UDP DataPacket API +RLAPI SocketDataPacket *AllocPacket(int size); +RLAPI int ResizePacket(SocketDataPacket *packet, int newsize); +RLAPI void FreePacket(SocketDataPacket *packet); +RLAPI SocketDataPacket **AllocPacketList(int count, int size); +RLAPI void FreePacketList(SocketDataPacket **packets); + +// General Socket API +RLAPI int SocketSend(Socket *sock, const void *datap, int len); +RLAPI int SocketReceive(Socket *sock, void *data, int maxlen); +RLAPI void SocketClose(Socket *sock); +RLAPI SocketAddressStorage SocketGetPeerAddress(Socket *sock); +RLAPI char* GetSocketAddressHost(SocketAddressStorage storage); +RLAPI short GetSocketAddressPort(SocketAddressStorage storage); + +// Socket Memory API +RLAPI Socket *AllocSocket(); +RLAPI void FreeSocket(Socket **sock); +RLAPI SocketResult *AllocSocketResult(); +RLAPI void FreeSocketResult(SocketResult **result); +RLAPI SocketSet *AllocSocketSet(int max); +RLAPI void FreeSocketSet(SocketSet *sockset); + +// Socket I/O API +RLAPI bool IsSocketReady(Socket *sock); +RLAPI bool IsSocketConnected(Socket *sock); +RLAPI int AddSocket(SocketSet *set, Socket *sock); +RLAPI int RemoveSocket(SocketSet *set, Socket *sock); +RLAPI int CheckSockets(SocketSet *set, unsigned int timeout); + +// Packet API +void PacketSend(Packet *packet); +void PacketReceive(Packet *packet); +void PacketWrite8(Packet *packet, uint16_t value); +void PacketWrite16(Packet *packet, uint16_t value); +void PacketWrite32(Packet *packet, uint32_t value); +void PacketWrite64(Packet *packet, uint64_t value); +uint16_t PacketRead8(Packet *packet); +uint16_t PacketRead16(Packet *packet); +uint32_t PacketRead32(Packet *packet); +uint64_t PacketRead64(Packet *packet); + +#ifdef __cplusplus +} +#endif + +#endif // RNET_H \ No newline at end of file -- cgit v1.2.3 From 2d4c2ff351f0a295d2827c28ca747e3465422094 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 22 Apr 2019 20:54:50 +0200 Subject: Review rnet errors --- src/raylib.h | 5 --- src/rnet.c | 20 ++++++------ src/rnet.h | 102 +++++++++++++++++++++++++++++++++-------------------------- 3 files changed, 68 insertions(+), 59 deletions(-) (limited to 'src/raylib.h') diff --git a/src/raylib.h b/src/raylib.h index 29850efd..424a9dd7 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -100,11 +100,6 @@ #define MAX_SHADER_LOCATIONS 32 // Maximum number of predefined locations stored in shader struct #define MAX_MATERIAL_MAPS 12 // Maximum number of texture maps stored in shader struct -// Network connection related defines -#define SOCKET_MAX_SOCK_OPTS (4) // Maximum socket options -#define SOCKET_MAX_UDPCHANNELS (32) // Maximum UDP channels -#define SOCKET_MAX_UDPADDRESSES (4) // Maximum bound UDP addresses - // NOTE: MSC C++ compiler does not support compound literals (C99 feature) // Plain structures in C++ (without constructors) can be initialized from { } initializers. #if defined(__cplusplus) diff --git a/src/rnet.c b/src/rnet.c index 07bc5ecc..79a6f07f 100644 --- a/src/rnet.c +++ b/src/rnet.c @@ -42,10 +42,10 @@ #include "raylib.h" -#include // Required for: assert() -#include // Required for: FILE, fopen(), fclose(), fread() -#include // Required for: malloc(), free() -#include // Required for: strcmp(), strncmp() +#include // Required for: assert() +#include // Required for: FILE, fopen(), fclose(), fread() +#include // Required for: malloc(), free() +#include // Required for: strcmp(), strncmp() //---------------------------------------------------------------------------------- // Module defines @@ -272,7 +272,7 @@ static char *SocketErrorCodeToString(int err) { #if defined(_WIN32) static char gaiStrErrorBuffer[GAI_STRERROR_BUFFER_SIZE]; - sprintf(gaiStrErrorBuffer, "%ws", gai_strerror(err)); + sprintf(gaiStrErrorBuffer, "%s", gai_strerror(err)); return gaiStrErrorBuffer; #else return gai_strerror(err); @@ -386,7 +386,7 @@ static bool CreateSocket(SocketConfig *config, SocketResult *outresult) char hoststr[NI_MAXHOST]; char portstr[NI_MAXSERV]; socklen_t client_len = sizeof(struct sockaddr_storage); - int rc = getnameinfo((struct sockaddr *) res->ai_addr, client_len, hoststr, sizeof(hoststr), portstr, sizeof(portstr), NI_NUMERICHOST | NI_NUMERICSERV); + int rc = getnameinfo((struct sockaddr *) res->ai_addr, client_len, hoststr, sizeof(hoststr), portstr, sizeof(portstr), NI_NUMERICHOST | NI_NUMERICSERV); TraceLog(LOG_INFO, "Successfully resolved host %s:%s", hoststr, portstr); } @@ -746,10 +746,10 @@ int ResolveHost(const char *address, const char *service, int addressType, int f hints.ai_family = addressType; // Either IPv4 or IPv6 (ADDRESS_TYPE_IPV4, ADDRESS_TYPE_IPV6) hints.ai_protocol = 0; // Automatically select correct protocol (IPPROTO_TCP), (IPPROTO_UDP) hints.ai_flags = flags; - assert(hints.ai_addrlen == NULL || hints.ai_addrlen == 0); - assert(hints.ai_canonname == NULL || hints.ai_canonname == 0); - assert(hints.ai_addr == NULL || hints.ai_addr == 0); - assert(hints.ai_next == NULL || hints.ai_next == 0); + assert((hints.ai_addrlen == NULL) || (hints.ai_addrlen == 0)); + assert((hints.ai_canonname == NULL) || (hints.ai_canonname == 0)); + assert((hints.ai_addr == NULL) || (hints.ai_addr == 0)); + assert((hints.ai_next == NULL) || (hints.ai_next == 0)); // When the address is NULL, populate the IP for me if (address == NULL) diff --git a/src/rnet.h b/src/rnet.h index a2233a3c..f5dde220 100644 --- a/src/rnet.h +++ b/src/rnet.h @@ -38,6 +38,9 @@ * **********************************************************************************************/ +#ifndef RNET_H +#define RNET_H + #include // Required for limits #include // Required for platform type sizes @@ -168,6 +171,10 @@ typedef int socklen_t; // Network connection related defines #define SOCKET_MAX_SET_SIZE (32) // Maximum sockets in a set #define SOCKET_MAX_QUEUE_SIZE (16) // Maximum socket queue size +#define SOCKET_MAX_SOCK_OPTS (4) // Maximum socket options +#define SOCKET_MAX_UDPCHANNELS (32) // Maximum UDP channels +#define SOCKET_MAX_UDPADDRESSES (4) // Maximum bound UDP addresses + // Network address related defines #define ADDRESS_IPV4_ADDRSTRLEN (22) // IPv4 string length @@ -216,6 +223,13 @@ typedef int socklen_t; // Types and Structures Definition //---------------------------------------------------------------------------------- +// Boolean type +#if defined(__STDC__) && __STDC_VERSION__ >= 199901L + #include +#elif !defined(__cplusplus) && !defined(bool) + typedef enum { false, true } bool; +#endif + // Network typedefs typedef uint32_t SocketChannel; typedef struct _AddressInformation * AddressInformation; @@ -301,7 +315,7 @@ typedef struct SocketResult Socket *socket; } SocketResult; -// +// Packet type typedef struct Packet { uint32_t size; // The total size of bytes in data @@ -325,64 +339,64 @@ extern "C" { // Prevents name mangling of functions //---------------------------------------------------------------------------------- // Initialisation and cleanup -RLAPI bool InitNetwork(void); -RLAPI void CloseNetwork(void); +bool InitNetwork(void); +void CloseNetwork(void); // Address API -RLAPI void ResolveIP(const char *ip, const char *service, int flags, char *outhost, char *outserv); -RLAPI int ResolveHost(const char *address, const char *service, int addressType, int flags, AddressInformation *outAddr); -RLAPI int GetAddressFamily(AddressInformation address); -RLAPI int GetAddressSocketType(AddressInformation address); -RLAPI int GetAddressProtocol(AddressInformation address); -RLAPI char* GetAddressCanonName(AddressInformation address); -RLAPI char* GetAddressHostAndPort(AddressInformation address, char *outhost, int *outport); -RLAPI void PrintAddressInfo(AddressInformation address); +void ResolveIP(const char *ip, const char *service, int flags, char *outhost, char *outserv); +int ResolveHost(const char *address, const char *service, int addressType, int flags, AddressInformation *outAddr); +int GetAddressFamily(AddressInformation address); +int GetAddressSocketType(AddressInformation address); +int GetAddressProtocol(AddressInformation address); +char* GetAddressCanonName(AddressInformation address); +char* GetAddressHostAndPort(AddressInformation address, char *outhost, int *outport); +void PrintAddressInfo(AddressInformation address); // Address Memory API -RLAPI AddressInformation AllocAddress(); -RLAPI void FreeAddress(AddressInformation *addressInfo); -RLAPI AddressInformation *AllocAddressList(int size); +AddressInformation AllocAddress(); +void FreeAddress(AddressInformation *addressInfo); +AddressInformation *AllocAddressList(int size); // Socket API -RLAPI bool SocketCreate(SocketConfig *config, SocketResult *result); -RLAPI bool SocketBind(SocketConfig *config, SocketResult *result); -RLAPI bool SocketListen(SocketConfig *config, SocketResult *result); -RLAPI bool SocketConnect(SocketConfig *config, SocketResult *result); -RLAPI Socket *SocketAccept(Socket *server, SocketConfig *config); +bool SocketCreate(SocketConfig *config, SocketResult *result); +bool SocketBind(SocketConfig *config, SocketResult *result); +bool SocketListen(SocketConfig *config, SocketResult *result); +bool SocketConnect(SocketConfig *config, SocketResult *result); +Socket *SocketAccept(Socket *server, SocketConfig *config); // UDP Socket API -RLAPI int SocketSetChannel(Socket *socket, int channel, const IPAddress *address); -RLAPI void SocketUnsetChannel(Socket *socket, int channel); +int SocketSetChannel(Socket *socket, int channel, const IPAddress *address); +void SocketUnsetChannel(Socket *socket, int channel); // UDP DataPacket API -RLAPI SocketDataPacket *AllocPacket(int size); -RLAPI int ResizePacket(SocketDataPacket *packet, int newsize); -RLAPI void FreePacket(SocketDataPacket *packet); -RLAPI SocketDataPacket **AllocPacketList(int count, int size); -RLAPI void FreePacketList(SocketDataPacket **packets); +SocketDataPacket *AllocPacket(int size); +int ResizePacket(SocketDataPacket *packet, int newsize); +void FreePacket(SocketDataPacket *packet); +SocketDataPacket **AllocPacketList(int count, int size); +void FreePacketList(SocketDataPacket **packets); // General Socket API -RLAPI int SocketSend(Socket *sock, const void *datap, int len); -RLAPI int SocketReceive(Socket *sock, void *data, int maxlen); -RLAPI void SocketClose(Socket *sock); -RLAPI SocketAddressStorage SocketGetPeerAddress(Socket *sock); -RLAPI char* GetSocketAddressHost(SocketAddressStorage storage); -RLAPI short GetSocketAddressPort(SocketAddressStorage storage); +int SocketSend(Socket *sock, const void *datap, int len); +int SocketReceive(Socket *sock, void *data, int maxlen); +void SocketClose(Socket *sock); +SocketAddressStorage SocketGetPeerAddress(Socket *sock); +char* GetSocketAddressHost(SocketAddressStorage storage); +short GetSocketAddressPort(SocketAddressStorage storage); // Socket Memory API -RLAPI Socket *AllocSocket(); -RLAPI void FreeSocket(Socket **sock); -RLAPI SocketResult *AllocSocketResult(); -RLAPI void FreeSocketResult(SocketResult **result); -RLAPI SocketSet *AllocSocketSet(int max); -RLAPI void FreeSocketSet(SocketSet *sockset); +Socket *AllocSocket(); +void FreeSocket(Socket **sock); +SocketResult *AllocSocketResult(); +void FreeSocketResult(SocketResult **result); +SocketSet *AllocSocketSet(int max); +void FreeSocketSet(SocketSet *sockset); // Socket I/O API -RLAPI bool IsSocketReady(Socket *sock); -RLAPI bool IsSocketConnected(Socket *sock); -RLAPI int AddSocket(SocketSet *set, Socket *sock); -RLAPI int RemoveSocket(SocketSet *set, Socket *sock); -RLAPI int CheckSockets(SocketSet *set, unsigned int timeout); +bool IsSocketReady(Socket *sock); +bool IsSocketConnected(Socket *sock); +int AddSocket(SocketSet *set, Socket *sock); +int RemoveSocket(SocketSet *set, Socket *sock); +int CheckSockets(SocketSet *set, unsigned int timeout); // Packet API void PacketSend(Packet *packet); @@ -400,4 +414,4 @@ uint64_t PacketRead64(Packet *packet); } #endif -#endif // RNET_H \ No newline at end of file +#endif // RNET_H \ No newline at end of file -- cgit v1.2.3 From e67ebabb02c1068d6e7f5107dcff5388ede3faa5 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 23 Apr 2019 14:55:35 +0200 Subject: Support custom memory management macros Users can define their custom memory management macros. NOTE: Most external libraries support custom macros in the same way, raylib should redefine those macros to raylib ones, to unify custom memory loading. That redefinition is only implemented as example for stb_image.h in [textures] module. --- src/core.c | 24 ++--- src/models.c | 291 +++++++++++++++++++++++++++++---------------------------- src/raudio.c | 38 ++++---- src/raudio.h | 11 ++- src/raylib.h | 11 +++ src/rlgl.h | 88 ++++++++--------- src/text.c | 54 +++++------ src/textures.c | 181 ++++++++++++++++++----------------- src/utils.c | 6 +- 9 files changed, 365 insertions(+), 339 deletions(-) (limited to 'src/raylib.h') diff --git a/src/core.c b/src/core.c index 6bb54c51..844994d0 100644 --- a/src/core.c +++ b/src/core.c @@ -1113,7 +1113,7 @@ void EndDrawing(void) unsigned char *screenData = rlReadScreenPixels(screenWidth, screenHeight); GifWriteFrame(screenData, screenWidth, screenHeight, 10, 8, false); - free(screenData); // Free image data + RL_FREE(screenData); // Free image data } if (((gifFramesCounter/15)%2) == 1) @@ -1595,7 +1595,7 @@ void TakeScreenshot(const char *fileName) #endif ExportImage(image, path); - free(imgData); + RL_FREE(imgData); #if defined(PLATFORM_WEB) // Download file from MEMFS (emscripten memory filesystem) @@ -1742,8 +1742,8 @@ char **GetDirectoryFiles(const char *dirPath, int *fileCount) ClearDirectoryFiles(); // Memory allocation for MAX_DIRECTORY_FILES - dirFilesPath = (char **)malloc(sizeof(char *)*MAX_DIRECTORY_FILES); - for (int i = 0; i < MAX_DIRECTORY_FILES; i++) dirFilesPath[i] = (char *)malloc(sizeof(char)*MAX_FILEPATH_LENGTH); + dirFilesPath = (char **)RL_MALLOC(sizeof(char *)*MAX_DIRECTORY_FILES); + for (int i = 0; i < MAX_DIRECTORY_FILES; i++) dirFilesPath[i] = (char *)RL_MALLOC(sizeof(char)*MAX_FILEPATH_LENGTH); int counter = 0; struct dirent *ent; @@ -1776,9 +1776,9 @@ void ClearDirectoryFiles(void) { if (dirFilesCount > 0) { - for (int i = 0; i < dirFilesCount; i++) free(dirFilesPath[i]); + for (int i = 0; i < dirFilesCount; i++) RL_FREE(dirFilesPath[i]); - free(dirFilesPath); + RL_FREE(dirFilesPath); dirFilesCount = 0; } } @@ -1808,9 +1808,9 @@ void ClearDroppedFiles(void) { if (dropFilesCount > 0) { - for (int i = 0; i < dropFilesCount; i++) free(dropFilesPath[i]); + for (int i = 0; i < dropFilesCount; i++) RL_FREE(dropFilesPath[i]); - free(dropFilesPath); + RL_FREE(dropFilesPath); dropFilesCount = 0; } @@ -1925,7 +1925,7 @@ void OpenURL(const char *url) } else { - char *cmd = (char *)calloc(strlen(url) + 10, sizeof(char)); + char *cmd = (char *)RL_CALLOC(strlen(url) + 10, sizeof(char)); #if defined(_WIN32) sprintf(cmd, "explorer %s", url); @@ -1935,7 +1935,7 @@ void OpenURL(const char *url) sprintf(cmd, "open '%s'", url); #endif system(cmd); - free(cmd); + RL_FREE(cmd); } } @@ -3455,11 +3455,11 @@ static void WindowDropCallback(GLFWwindow *window, int count, const char **paths { ClearDroppedFiles(); - dropFilesPath = (char **)malloc(sizeof(char *)*count); + dropFilesPath = (char **)RL_MALLOC(sizeof(char *)*count); for (int i = 0; i < count; i++) { - dropFilesPath[i] = (char *)malloc(sizeof(char)*MAX_FILEPATH_LENGTH); + dropFilesPath[i] = (char *)RL_MALLOC(sizeof(char)*MAX_FILEPATH_LENGTH); strcpy(dropFilesPath[i], paths[i]); } diff --git a/src/models.c b/src/models.c index 631c4942..c81dfe19 100644 --- a/src/models.c +++ b/src/models.c @@ -651,7 +651,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 *)calloc(model.meshCount, sizeof(Mesh)); + model.meshes = (Mesh *)RL_CALLOC(model.meshCount, sizeof(Mesh)); model.meshes[0] = GenMeshCube(1.0f, 1.0f, 1.0f); } else @@ -665,10 +665,10 @@ 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 *)calloc(model.materialCount, sizeof(Material)); + model.materials = (Material *)RL_CALLOC(model.materialCount, sizeof(Material)); model.materials[0] = LoadMaterialDefault(); - model.meshMaterial = (int *)calloc(model.meshCount, sizeof(int)); + model.meshMaterial = (int *)RL_CALLOC(model.meshCount, sizeof(int)); } return model; @@ -685,14 +685,14 @@ Model LoadModelFromMesh(Mesh mesh) model.transform = MatrixIdentity(); model.meshCount = 1; - model.meshes = (Mesh *)malloc(model.meshCount*sizeof(Mesh)); + model.meshes = (Mesh *)RL_MALLOC(model.meshCount*sizeof(Mesh)); model.meshes[0] = mesh; model.materialCount = 1; - model.materials = (Material *)malloc(model.materialCount*sizeof(Material)); + model.materials = (Material *)RL_MALLOC(model.materialCount*sizeof(Material)); model.materials[0] = LoadMaterialDefault(); - model.meshMaterial = (int *)malloc(model.meshCount*sizeof(int)); + model.meshMaterial = (int *)RL_MALLOC(model.meshCount*sizeof(int)); model.meshMaterial[0] = 0; // First material index return model; @@ -704,13 +704,13 @@ 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); + RL_FREE(model.meshes); + RL_FREE(model.materials); + RL_FREE(model.meshMaterial); // Unload animation data - free(model.bones); - free(model.bindPose); + RL_FREE(model.bones); + RL_FREE(model.bindPose); TraceLog(LOG_INFO, "Unloaded model data from RAM and VRAM"); } @@ -866,7 +866,7 @@ void SetModelMeshMaterial(Model *model, int meshId, int materialId) // Load model animations from file ModelAnimation *LoadModelAnimations(const char *filename, int *animCount) { - ModelAnimation *animations = (ModelAnimation *)malloc(1*sizeof(ModelAnimation)); + ModelAnimation *animations = (ModelAnimation *)RL_MALLOC(1*sizeof(ModelAnimation)); int count = 1; #define IQM_MAGIC "INTERQUAKEMODEL" // IQM file magic number @@ -935,12 +935,12 @@ ModelAnimation *LoadModelAnimations(const char *filename, int *animCount) // bones IQMPose *poses; - poses = malloc(sizeof(IQMPose)*iqm.num_poses); + poses = RL_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); + animation.bones = RL_MALLOC(sizeof(BoneInfo)*iqm.num_poses); for (int j = 0; j < iqm.num_poses; j++) { @@ -957,12 +957,12 @@ ModelAnimation *LoadModelAnimations(const char *filename, int *animCount) //animation.framerate = anim.framerate; // frameposes - unsigned short *framedata = malloc(sizeof(unsigned short)*iqm.num_frames*iqm.num_framechannels); + unsigned short *framedata = RL_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); + animation.framePoses = RL_MALLOC(sizeof(Transform*)*anim.num_frames); + for (int j = 0; j < anim.num_frames; j++) animation.framePoses[j] = RL_MALLOC(sizeof(Transform)*iqm.num_poses); int dcounter = anim.first_frame*iqm.num_framechannels; @@ -1069,8 +1069,8 @@ ModelAnimation *LoadModelAnimations(const char *filename, int *animCount) } } - free(framedata); - free(poses); + RL_FREE(framedata); + RL_FREE(poses); fclose(iqmFile); @@ -1145,10 +1145,10 @@ void UpdateModelAnimation(Model model, ModelAnimation anim, int frame) // Unload animation data void UnloadModelAnimation(ModelAnimation anim) { - for (int i = 0; i < anim.frameCount; i++) free(anim.framePoses[i]); + for (int i = 0; i < anim.frameCount; i++) RL_FREE(anim.framePoses[i]); - free(anim.bones); - free(anim.framePoses); + RL_FREE(anim.bones); + RL_FREE(anim.framePoses); } // Check model animation skeleton match @@ -1177,7 +1177,7 @@ Mesh GenMeshPoly(int sides, float radius) int vertexCount = sides*3; // Vertices definition - Vector3 *vertices = (Vector3 *)malloc(vertexCount*sizeof(Vector3)); + Vector3 *vertices = (Vector3 *)RL_MALLOC(vertexCount*sizeof(Vector3)); for (int i = 0, v = 0; i < 360; i += 360/sides, v += 3) { vertices[v] = (Vector3){ 0.0f, 0.0f, 0.0f }; @@ -1186,18 +1186,18 @@ Mesh GenMeshPoly(int sides, float radius) } // Normals definition - Vector3 *normals = (Vector3 *)malloc(vertexCount*sizeof(Vector3)); + Vector3 *normals = (Vector3 *)RL_MALLOC(vertexCount*sizeof(Vector3)); for (int n = 0; n < vertexCount; n++) normals[n] = (Vector3){ 0.0f, 1.0f, 0.0f }; // Vector3.up; // TexCoords definition - Vector2 *texcoords = (Vector2 *)malloc(vertexCount*sizeof(Vector2)); + Vector2 *texcoords = (Vector2 *)RL_MALLOC(vertexCount*sizeof(Vector2)); for (int n = 0; n < vertexCount; n++) texcoords[n] = (Vector2){ 0.0f, 0.0f }; mesh.vertexCount = vertexCount; mesh.triangleCount = sides; - 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.vertices = (float *)RL_MALLOC(mesh.vertexCount*3*sizeof(float)); + mesh.texcoords = (float *)RL_MALLOC(mesh.vertexCount*2*sizeof(float)); + mesh.normals = (float *)RL_MALLOC(mesh.vertexCount*3*sizeof(float)); // Mesh vertices position array for (int i = 0; i < mesh.vertexCount; i++) @@ -1222,9 +1222,9 @@ Mesh GenMeshPoly(int sides, float radius) mesh.normals[3*i + 2] = normals[i].z; } - free(vertices); - free(normals); - free(texcoords); + RL_FREE(vertices); + RL_FREE(normals); + RL_FREE(texcoords); // Upload vertex data to GPU (static mesh) rlLoadMesh(&mesh, false); @@ -1245,7 +1245,7 @@ Mesh GenMeshPlane(float width, float length, int resX, int resZ) // Vertices definition int vertexCount = resX*resZ; // vertices get reused for the faces - Vector3 *vertices = (Vector3 *)malloc(vertexCount*sizeof(Vector3)); + Vector3 *vertices = (Vector3 *)RL_MALLOC(vertexCount*sizeof(Vector3)); for (int z = 0; z < resZ; z++) { // [-length/2, length/2] @@ -1259,11 +1259,11 @@ Mesh GenMeshPlane(float width, float length, int resX, int resZ) } // Normals definition - Vector3 *normals = (Vector3 *)malloc(vertexCount*sizeof(Vector3)); + Vector3 *normals = (Vector3 *)RL_MALLOC(vertexCount*sizeof(Vector3)); for (int n = 0; n < vertexCount; n++) normals[n] = (Vector3){ 0.0f, 1.0f, 0.0f }; // Vector3.up; // TexCoords definition - Vector2 *texcoords = (Vector2 *)malloc(vertexCount*sizeof(Vector2)); + Vector2 *texcoords = (Vector2 *)RL_MALLOC(vertexCount*sizeof(Vector2)); for (int v = 0; v < resZ; v++) { for (int u = 0; u < resX; u++) @@ -1274,7 +1274,7 @@ Mesh GenMeshPlane(float width, float length, int resX, int resZ) // Triangles definition (indices) int numFaces = (resX - 1)*(resZ - 1); - int *triangles = (int *)malloc(numFaces*6*sizeof(int)); + int *triangles = (int *)RL_MALLOC(numFaces*6*sizeof(int)); int t = 0; for (int face = 0; face < numFaces; face++) { @@ -1292,10 +1292,10 @@ Mesh GenMeshPlane(float width, float length, int resX, int resZ) mesh.vertexCount = vertexCount; mesh.triangleCount = numFaces*2; - 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.indices = (unsigned short *)malloc(mesh.triangleCount*3*sizeof(unsigned short)); + mesh.vertices = (float *)RL_MALLOC(mesh.vertexCount*3*sizeof(float)); + mesh.texcoords = (float *)RL_MALLOC(mesh.vertexCount*2*sizeof(float)); + mesh.normals = (float *)RL_MALLOC(mesh.vertexCount*3*sizeof(float)); + mesh.indices = (unsigned short *)RL_MALLOC(mesh.triangleCount*3*sizeof(unsigned short)); // Mesh vertices position array for (int i = 0; i < mesh.vertexCount; i++) @@ -1323,10 +1323,10 @@ Mesh GenMeshPlane(float width, float length, int resX, int resZ) // Mesh indices array initialization for (int i = 0; i < mesh.triangleCount*3; i++) mesh.indices[i] = triangles[i]; - free(vertices); - free(normals); - free(texcoords); - free(triangles); + RL_FREE(vertices); + RL_FREE(normals); + RL_FREE(texcoords); + RL_FREE(triangles); #else // Use par_shapes library to generate plane mesh @@ -1335,9 +1335,9 @@ Mesh GenMeshPlane(float width, float length, int resX, int resZ) par_shapes_rotate(plane, -PI/2.0f, (float[]){ 1, 0, 0 }); par_shapes_translate(plane, -width/2, 0.0f, length/2); - mesh.vertices = (float *)malloc(plane->ntriangles*3*3*sizeof(float)); - mesh.texcoords = (float *)malloc(plane->ntriangles*3*2*sizeof(float)); - mesh.normals = (float *)malloc(plane->ntriangles*3*3*sizeof(float)); + mesh.vertices = (float *)RL_MALLOC(plane->ntriangles*3*3*sizeof(float)); + mesh.texcoords = (float *)RL_MALLOC(plane->ntriangles*3*2*sizeof(float)); + mesh.normals = (float *)RL_MALLOC(plane->ntriangles*3*3*sizeof(float)); mesh.vertexCount = plane->ntriangles*3; mesh.triangleCount = plane->ntriangles; @@ -1453,16 +1453,16 @@ Mesh GenMeshCube(float width, float height, float length) -1.0f, 0.0f, 0.0f }; - mesh.vertices = (float *)malloc(24*3*sizeof(float)); + mesh.vertices = (float *)RL_MALLOC(24*3*sizeof(float)); memcpy(mesh.vertices, vertices, 24*3*sizeof(float)); - mesh.texcoords = (float *)malloc(24*2*sizeof(float)); + mesh.texcoords = (float *)RL_MALLOC(24*2*sizeof(float)); memcpy(mesh.texcoords, texcoords, 24*2*sizeof(float)); - mesh.normals = (float *)malloc(24*3*sizeof(float)); + mesh.normals = (float *)RL_MALLOC(24*3*sizeof(float)); memcpy(mesh.normals, normals, 24*3*sizeof(float)); - mesh.indices = (unsigned short *)malloc(36*sizeof(unsigned short)); + mesh.indices = (unsigned short *)RL_MALLOC(36*sizeof(unsigned short)); int k = 0; @@ -1500,9 +1500,9 @@ par_shapes_mesh* par_shapes_create_icosahedron(); // 20 sides polyhedron par_shapes_translate(cube, -width/2, 0.0f, -length/2); par_shapes_compute_normals(cube); - mesh.vertices = (float *)malloc(cube->ntriangles*3*3*sizeof(float)); - mesh.texcoords = (float *)malloc(cube->ntriangles*3*2*sizeof(float)); - mesh.normals = (float *)malloc(cube->ntriangles*3*3*sizeof(float)); + mesh.vertices = (float *)RL_MALLOC(cube->ntriangles*3*3*sizeof(float)); + mesh.texcoords = (float *)RL_MALLOC(cube->ntriangles*3*2*sizeof(float)); + mesh.normals = (float *)RL_MALLOC(cube->ntriangles*3*3*sizeof(float)); mesh.vertexCount = cube->ntriangles*3; mesh.triangleCount = cube->ntriangles; @@ -1539,9 +1539,9 @@ RLAPI Mesh GenMeshSphere(float radius, int rings, int slices) par_shapes_scale(sphere, radius, radius, radius); // NOTE: Soft normals are computed internally - mesh.vertices = (float *)malloc(sphere->ntriangles*3*3*sizeof(float)); - mesh.texcoords = (float *)malloc(sphere->ntriangles*3*2*sizeof(float)); - mesh.normals = (float *)malloc(sphere->ntriangles*3*3*sizeof(float)); + mesh.vertices = (float *)RL_MALLOC(sphere->ntriangles*3*3*sizeof(float)); + mesh.texcoords = (float *)RL_MALLOC(sphere->ntriangles*3*2*sizeof(float)); + mesh.normals = (float *)RL_MALLOC(sphere->ntriangles*3*3*sizeof(float)); mesh.vertexCount = sphere->ntriangles*3; mesh.triangleCount = sphere->ntriangles; @@ -1577,9 +1577,9 @@ RLAPI Mesh GenMeshHemiSphere(float radius, int rings, int slices) par_shapes_scale(sphere, radius, radius, radius); // NOTE: Soft normals are computed internally - mesh.vertices = (float *)malloc(sphere->ntriangles*3*3*sizeof(float)); - mesh.texcoords = (float *)malloc(sphere->ntriangles*3*2*sizeof(float)); - mesh.normals = (float *)malloc(sphere->ntriangles*3*3*sizeof(float)); + mesh.vertices = (float *)RL_MALLOC(sphere->ntriangles*3*3*sizeof(float)); + mesh.texcoords = (float *)RL_MALLOC(sphere->ntriangles*3*2*sizeof(float)); + mesh.normals = (float *)RL_MALLOC(sphere->ntriangles*3*3*sizeof(float)); mesh.vertexCount = sphere->ntriangles*3; mesh.triangleCount = sphere->ntriangles; @@ -1635,9 +1635,9 @@ Mesh GenMeshCylinder(float radius, float height, int slices) par_shapes_merge_and_free(cylinder, capTop); par_shapes_merge_and_free(cylinder, capBottom); - mesh.vertices = (float *)malloc(cylinder->ntriangles*3*3*sizeof(float)); - mesh.texcoords = (float *)malloc(cylinder->ntriangles*3*2*sizeof(float)); - mesh.normals = (float *)malloc(cylinder->ntriangles*3*3*sizeof(float)); + mesh.vertices = (float *)RL_MALLOC(cylinder->ntriangles*3*3*sizeof(float)); + mesh.texcoords = (float *)RL_MALLOC(cylinder->ntriangles*3*2*sizeof(float)); + mesh.normals = (float *)RL_MALLOC(cylinder->ntriangles*3*3*sizeof(float)); mesh.vertexCount = cylinder->ntriangles*3; mesh.triangleCount = cylinder->ntriangles; @@ -1677,9 +1677,9 @@ Mesh GenMeshTorus(float radius, float size, int radSeg, int sides) par_shapes_mesh *torus = par_shapes_create_torus(radSeg, sides, radius); par_shapes_scale(torus, size/2, size/2, size/2); - mesh.vertices = (float *)malloc(torus->ntriangles*3*3*sizeof(float)); - mesh.texcoords = (float *)malloc(torus->ntriangles*3*2*sizeof(float)); - mesh.normals = (float *)malloc(torus->ntriangles*3*3*sizeof(float)); + mesh.vertices = (float *)RL_MALLOC(torus->ntriangles*3*3*sizeof(float)); + mesh.texcoords = (float *)RL_MALLOC(torus->ntriangles*3*2*sizeof(float)); + mesh.normals = (float *)RL_MALLOC(torus->ntriangles*3*3*sizeof(float)); mesh.vertexCount = torus->ntriangles*3; mesh.triangleCount = torus->ntriangles; @@ -1717,9 +1717,9 @@ Mesh GenMeshKnot(float radius, float size, int radSeg, int sides) par_shapes_mesh *knot = par_shapes_create_trefoil_knot(radSeg, sides, radius); par_shapes_scale(knot, size, size, size); - mesh.vertices = (float *)malloc(knot->ntriangles*3*3*sizeof(float)); - mesh.texcoords = (float *)malloc(knot->ntriangles*3*2*sizeof(float)); - mesh.normals = (float *)malloc(knot->ntriangles*3*3*sizeof(float)); + mesh.vertices = (float *)RL_MALLOC(knot->ntriangles*3*3*sizeof(float)); + mesh.texcoords = (float *)RL_MALLOC(knot->ntriangles*3*2*sizeof(float)); + mesh.normals = (float *)RL_MALLOC(knot->ntriangles*3*3*sizeof(float)); mesh.vertexCount = knot->ntriangles*3; mesh.triangleCount = knot->ntriangles; @@ -1764,9 +1764,9 @@ Mesh GenMeshHeightmap(Image heightmap, Vector3 size) mesh.vertexCount = mesh.triangleCount*3; - mesh.vertices = (float *)malloc(mesh.vertexCount*3*sizeof(float)); - mesh.normals = (float *)malloc(mesh.vertexCount*3*sizeof(float)); - mesh.texcoords = (float *)malloc(mesh.vertexCount*2*sizeof(float)); + mesh.vertices = (float *)RL_MALLOC(mesh.vertexCount*3*sizeof(float)); + mesh.normals = (float *)RL_MALLOC(mesh.vertexCount*3*sizeof(float)); + mesh.texcoords = (float *)RL_MALLOC(mesh.vertexCount*2*sizeof(float)); mesh.colors = NULL; int vCounter = 0; // Used to count vertices float by float @@ -1848,7 +1848,7 @@ Mesh GenMeshHeightmap(Image heightmap, Vector3 size) } } - free(pixels); + RL_FREE(pixels); // Upload vertex data to GPU (static mesh) rlLoadMesh(&mesh, false); @@ -1878,9 +1878,9 @@ Mesh GenMeshCubicmap(Image cubicmap, Vector3 cubeSize) float h = cubeSize.z; float h2 = cubeSize.y; - Vector3 *mapVertices = (Vector3 *)malloc(maxTriangles*3*sizeof(Vector3)); - Vector2 *mapTexcoords = (Vector2 *)malloc(maxTriangles*3*sizeof(Vector2)); - Vector3 *mapNormals = (Vector3 *)malloc(maxTriangles*3*sizeof(Vector3)); + Vector3 *mapVertices = (Vector3 *)RL_MALLOC(maxTriangles*3*sizeof(Vector3)); + Vector2 *mapTexcoords = (Vector2 *)RL_MALLOC(maxTriangles*3*sizeof(Vector2)); + Vector3 *mapNormals = (Vector3 *)RL_MALLOC(maxTriangles*3*sizeof(Vector3)); // Define the 6 normals of the cube, we will combine them accordingly later... Vector3 n1 = { 1.0f, 0.0f, 0.0f }; @@ -2167,9 +2167,9 @@ Mesh GenMeshCubicmap(Image cubicmap, Vector3 cubeSize) mesh.vertexCount = vCounter; mesh.triangleCount = vCounter/3; - mesh.vertices = (float *)malloc(mesh.vertexCount*3*sizeof(float)); - mesh.normals = (float *)malloc(mesh.vertexCount*3*sizeof(float)); - mesh.texcoords = (float *)malloc(mesh.vertexCount*2*sizeof(float)); + mesh.vertices = (float *)RL_MALLOC(mesh.vertexCount*3*sizeof(float)); + mesh.normals = (float *)RL_MALLOC(mesh.vertexCount*3*sizeof(float)); + mesh.texcoords = (float *)RL_MALLOC(mesh.vertexCount*2*sizeof(float)); mesh.colors = NULL; int fCounter = 0; @@ -2204,11 +2204,11 @@ Mesh GenMeshCubicmap(Image cubicmap, Vector3 cubeSize) fCounter += 2; } - free(mapVertices); - free(mapNormals); - free(mapTexcoords); + RL_FREE(mapVertices); + RL_FREE(mapNormals); + RL_FREE(mapTexcoords); - free(cubicmapPixels); // Free image pixel data + RL_FREE(cubicmapPixels); // Free image pixel data // Upload vertex data to GPU (static mesh) rlLoadMesh(&mesh, false); @@ -2250,11 +2250,11 @@ BoundingBox MeshBoundingBox(Mesh mesh) // 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)); + if (mesh->tangents == NULL) mesh->tangents = (float *)RL_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)); + Vector3 *tan1 = (Vector3 *)RL_MALLOC(mesh->vertexCount*sizeof(Vector3)); + Vector3 *tan2 = (Vector3 *)RL_MALLOC(mesh->vertexCount*sizeof(Vector3)); for (int i = 0; i < mesh->vertexCount; i += 3) { @@ -2318,8 +2318,8 @@ void MeshTangents(Mesh *mesh) #endif } - free(tan1); - free(tan2); + RL_FREE(tan1); + RL_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); @@ -2746,7 +2746,7 @@ static Model LoadOBJ(const char *fileName) long length = ftell(objFile); // Get file size fseek(objFile, 0, SEEK_SET); // Reset file pointer - data = (char *)malloc(length); + data = (char *)RL_MALLOC(length); fread(data, length, 1, objFile); dataLength = length; @@ -2763,12 +2763,12 @@ static Model LoadOBJ(const char *fileName) // Init model meshes array model.meshCount = meshCount; - model.meshes = (Mesh *)malloc(model.meshCount*sizeof(Mesh)); + model.meshes = (Mesh *)RL_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)); + model.materials = (Material *)RL_MALLOC(model.materialCount*sizeof(Material)); + model.meshMaterial = (int *)RL_CALLOC(model.meshCount, sizeof(int)); /* // Multiple meshes data reference @@ -2787,9 +2787,9 @@ static Model LoadOBJ(const char *fileName) 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)); + mesh.vertices = (float *)RL_MALLOC(mesh.vertexCount*3*sizeof(float)); + mesh.texcoords = (float *)RL_MALLOC(mesh.vertexCount*2*sizeof(float)); + mesh.normals = (float *)RL_MALLOC(mesh.vertexCount*3*sizeof(float)); int vCount = 0; int vtCount = 0; @@ -2934,17 +2934,26 @@ static Model LoadIQM(const char *fileName) 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 IQMVertexArray { + unsigned int type; + unsigned int flags; + unsigned int format; + unsigned int size; + unsigned int offset; + } IQMVertexArray; + + // NOTE: Below IQM structures are not used but listed for reference + /* + typedef struct IQMAdjacency { + unsigned int triangle[3]; + } IQMAdjacency; typedef struct IQMPose { int parent; @@ -2960,19 +2969,11 @@ static Model LoadIQM(const char *fileName) 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 @@ -3028,12 +3029,12 @@ static Model LoadIQM(const char *fileName) } // Meshes data processing - imesh = malloc(sizeof(IQMMesh)*iqm.num_meshes); + imesh = RL_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(model.meshCount*sizeof(Mesh)); + model.meshes = RL_MALLOC(model.meshCount*sizeof(Mesh)); char name[MESH_NAME_LENGTH]; @@ -3043,24 +3044,24 @@ static Model LoadIQM(const char *fileName) 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)*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].vertices = RL_MALLOC(sizeof(float)*model.meshes[i].vertexCount*3); // Default vertex positions + model.meshes[i].normals = RL_MALLOC(sizeof(float)*model.meshes[i].vertexCount*3); // Default vertex normals + model.meshes[i].texcoords = RL_MALLOC(sizeof(float)*model.meshes[i].vertexCount*2); // Default vertex texcoords - 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].boneIds = RL_MALLOC(sizeof(int)*model.meshes[i].vertexCount*4); // Up-to 4 bones supported! + model.meshes[i].boneWeights = RL_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)*model.meshes[i].triangleCount*3); + model.meshes[i].indices = RL_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)*model.meshes[i].vertexCount*3); - model.meshes[i].animNormals = malloc(sizeof(float)*model.meshes[i].vertexCount*3); + model.meshes[i].animVertices = RL_MALLOC(sizeof(float)*model.meshes[i].vertexCount*3); + model.meshes[i].animNormals = RL_MALLOC(sizeof(float)*model.meshes[i].vertexCount*3); } // Triangles data processing - tri = malloc(sizeof(IQMTriangle)*iqm.num_triangles); + tri = RL_MALLOC(sizeof(IQMTriangle)*iqm.num_triangles); fseek(iqmFile, iqm.ofs_triangles, SEEK_SET); fread(tri, sizeof(IQMTriangle)*iqm.num_triangles, 1, iqmFile); @@ -3079,7 +3080,7 @@ static Model LoadIQM(const char *fileName) } // Vertex arrays data processing - va = malloc(sizeof(IQMVertexArray)*iqm.num_vertexarrays); + va = RL_MALLOC(sizeof(IQMVertexArray)*iqm.num_vertexarrays); fseek(iqmFile, iqm.ofs_vertexarrays, SEEK_SET); fread(va, sizeof(IQMVertexArray)*iqm.num_vertexarrays, 1, iqmFile); @@ -3089,7 +3090,7 @@ static Model LoadIQM(const char *fileName) { case IQM_POSITION: { - vertex = malloc(sizeof(float)*iqm.num_vertexes*3); + vertex = RL_MALLOC(sizeof(float)*iqm.num_vertexes*3); fseek(iqmFile, va[i].offset, SEEK_SET); fread(vertex, sizeof(float)*iqm.num_vertexes*3, 1, iqmFile); @@ -3106,7 +3107,7 @@ static Model LoadIQM(const char *fileName) } break; case IQM_NORMAL: { - normal = malloc(sizeof(float)*iqm.num_vertexes*3); + normal = RL_MALLOC(sizeof(float)*iqm.num_vertexes*3); fseek(iqmFile, va[i].offset, SEEK_SET); fread(normal, sizeof(float)*iqm.num_vertexes*3, 1, iqmFile); @@ -3123,7 +3124,7 @@ static Model LoadIQM(const char *fileName) } break; case IQM_TEXCOORD: { - text = malloc(sizeof(float)*iqm.num_vertexes*2); + text = RL_MALLOC(sizeof(float)*iqm.num_vertexes*2); fseek(iqmFile, va[i].offset, SEEK_SET); fread(text, sizeof(float)*iqm.num_vertexes*2, 1, iqmFile); @@ -3139,7 +3140,7 @@ static Model LoadIQM(const char *fileName) } break; case IQM_BLENDINDEXES: { - blendi = malloc(sizeof(char)*iqm.num_vertexes*4); + blendi = RL_MALLOC(sizeof(char)*iqm.num_vertexes*4); fseek(iqmFile, va[i].offset, SEEK_SET); fread(blendi, sizeof(char)*iqm.num_vertexes*4, 1, iqmFile); @@ -3155,7 +3156,7 @@ static Model LoadIQM(const char *fileName) } break; case IQM_BLENDWEIGHTS: { - blendw = malloc(sizeof(unsigned char)*iqm.num_vertexes*4); + blendw = RL_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); @@ -3173,13 +3174,13 @@ static Model LoadIQM(const char *fileName) } // Bones (joints) data processing - ijoint = malloc(sizeof(IQMJoint)*iqm.num_joints); + ijoint = RL_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); + model.bones = RL_MALLOC(sizeof(BoneInfo)*iqm.num_joints); + model.bindPose = RL_MALLOC(sizeof(Transform)*iqm.num_joints); for (int i = 0; i < iqm.num_joints; i++) { @@ -3216,15 +3217,15 @@ static Model LoadIQM(const char *fileName) } fclose(iqmFile); - free(imesh); - free(tri); - free(va); - free(vertex); - free(normal); - free(text); - free(blendi); - free(blendw); - free(ijoint); + RL_FREE(imesh); + RL_FREE(tri); + RL_FREE(va); + RL_FREE(vertex); + RL_FREE(normal); + RL_FREE(text); + RL_FREE(blendi); + RL_FREE(blendw); + RL_FREE(ijoint); return model; } @@ -3249,7 +3250,7 @@ static Model LoadGLTF(const char *fileName) int size = ftell(gltfFile); fseek(gltfFile, 0, SEEK_SET); - void *buffer = malloc(size); + void *buffer = RL_MALLOC(size); fread(buffer, size, 1, gltfFile); fclose(gltfFile); @@ -3259,7 +3260,7 @@ static Model LoadGLTF(const char *fileName) cgltf_data *data; cgltf_result result = cgltf_parse(&options, buffer, size, &data); - free(buffer); + RL_FREE(buffer); if (result == cgltf_result_success) { @@ -3270,7 +3271,7 @@ static Model LoadGLTF(const char *fileName) // Process glTF data and map to model model.meshCount = data->meshes_count; - model.meshes = malloc(model.meshCount*sizeof(Mesh)); + model.meshes = RL_MALLOC(model.meshCount*sizeof(Mesh)); for (int i = 0; i < model.meshCount; i++) { @@ -3282,11 +3283,11 @@ static Model LoadGLTF(const char *fileName) 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].vertices = RL_MALLOC(sizeof(float)*model.meshes[i].vertexCount*3); // Default vertex positions + model.meshes[i].normals = RL_MALLOC(sizeof(float)*model.meshes[i].vertexCount*3); // Default vertex normals + model.meshes[i].texcoords = RL_MALLOC(sizeof(float)*model.meshes[i].vertexCount*2); // Default vertex texcoords - model.meshes[i].indices = malloc(sizeof(unsigned short)*model.meshes[i].triangleCount*3); + model.meshes[i].indices = RL_MALLOC(sizeof(unsigned short)*model.meshes[i].triangleCount*3); } } diff --git a/src/raudio.c b/src/raudio.c index 636d15b8..9108a903 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -567,7 +567,7 @@ void SetMasterVolume(float volume) // Create a new audio buffer. Initially filled with silence AudioBuffer *CreateAudioBuffer(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 bufferSizeInFrames, AudioBufferUsage usage) { - AudioBuffer *audioBuffer = (AudioBuffer *)calloc(sizeof(*audioBuffer) + (bufferSizeInFrames*channels*ma_get_bytes_per_sample(format)), 1); + AudioBuffer *audioBuffer = (AudioBuffer *)RL_CALLOC(sizeof(*audioBuffer) + (bufferSizeInFrames*channels*ma_get_bytes_per_sample(format)), 1); if (audioBuffer == NULL) { TraceLog(LOG_ERROR, "CreateAudioBuffer() : Failed to allocate memory for audio buffer"); @@ -591,7 +591,7 @@ AudioBuffer *CreateAudioBuffer(ma_format format, ma_uint32 channels, ma_uint32 s if (result != MA_SUCCESS) { TraceLog(LOG_ERROR, "CreateAudioBuffer() : Failed to create data conversion pipeline"); - free(audioBuffer); + RL_FREE(audioBuffer); return NULL; } @@ -623,7 +623,7 @@ void DeleteAudioBuffer(AudioBuffer *audioBuffer) } UntrackAudioBuffer(audioBuffer); - free(audioBuffer); + RL_FREE(audioBuffer); } // Check if an audio buffer is playing @@ -863,7 +863,7 @@ Sound LoadSoundFromWave(Wave wave) // Unload wave data void UnloadWave(Wave wave) { - if (wave.data != NULL) free(wave.data); + if (wave.data != NULL) RL_FREE(wave.data); TraceLog(LOG_INFO, "Unloaded wave data from RAM"); } @@ -1017,7 +1017,7 @@ void WaveFormat(Wave *wave, int sampleRate, int sampleSize, int channels) return; } - void *data = malloc(frameCount*channels*(sampleSize/8)); + void *data = RL_MALLOC(frameCount*channels*(sampleSize/8)); frameCount = (ma_uint32)ma_convert_frames(data, formatOut, channels, sampleRate, wave->data, formatIn, wave->channels, wave->sampleRate, frameCountIn); if (frameCount == 0) @@ -1030,7 +1030,7 @@ void WaveFormat(Wave *wave, int sampleRate, int sampleSize, int channels) wave->sampleSize = sampleSize; wave->sampleRate = sampleRate; wave->channels = channels; - free(wave->data); + RL_FREE(wave->data); wave->data = data; } @@ -1039,7 +1039,7 @@ Wave WaveCopy(Wave wave) { Wave newWave = { 0 }; - newWave.data = malloc(wave.sampleCount*wave.sampleSize/8*wave.channels); + newWave.data = RL_MALLOC(wave.sampleCount*wave.sampleSize/8*wave.channels); if (newWave.data != NULL) { @@ -1064,11 +1064,11 @@ void WaveCrop(Wave *wave, int initSample, int finalSample) { int sampleCount = finalSample - initSample; - void *data = malloc(sampleCount*wave->sampleSize/8*wave->channels); + void *data = RL_MALLOC(sampleCount*wave->sampleSize/8*wave->channels); memcpy(data, (unsigned char *)wave->data + (initSample*wave->channels*wave->sampleSize/8), sampleCount*wave->channels*wave->sampleSize/8); - free(wave->data); + RL_FREE(wave->data); wave->data = data; } else TraceLog(LOG_WARNING, "Wave crop range out of bounds"); @@ -1078,7 +1078,7 @@ void WaveCrop(Wave *wave, int initSample, int finalSample) // NOTE: Returned sample values are normalized to range [-1..1] float *GetWaveData(Wave wave) { - float *samples = (float *)malloc(wave.sampleCount*wave.channels*sizeof(float)); + float *samples = (float *)RL_MALLOC(wave.sampleCount*wave.channels*sizeof(float)); for (unsigned int i = 0; i < wave.sampleCount; i++) { @@ -1100,7 +1100,7 @@ float *GetWaveData(Wave wave) // Load music stream from file Music LoadMusicStream(const char *fileName) { - Music music = (MusicData *)malloc(sizeof(MusicData)); + Music music = (MusicData *)RL_MALLOC(sizeof(MusicData)); bool musicLoaded = true; #if defined(SUPPORT_FILEFORMAT_OGG) @@ -1228,7 +1228,7 @@ Music LoadMusicStream(const char *fileName) if (false) {} #endif #if defined(SUPPORT_FILEFORMAT_FLAC) - else if (music->ctxType == MUSIC_AUDIO_FLAC) drflac_free(music->ctxFlac); + else if (music->ctxType == MUSIC_AUDIO_FLAC) drflac_RL_FREE(music->ctxFlac); #endif #if defined(SUPPORT_FILEFORMAT_MP3) else if (music->ctxType == MUSIC_AUDIO_MP3) drmp3_uninit(&music->ctxMp3); @@ -1240,7 +1240,7 @@ Music LoadMusicStream(const char *fileName) else if (music->ctxType == MUSIC_MODULE_MOD) jar_mod_unload(&music->ctxMod); #endif - free(music); + RL_FREE(music); music = NULL; TraceLog(LOG_WARNING, "[%s] Music file could not be opened", fileName); @@ -1262,7 +1262,7 @@ void UnloadMusicStream(Music music) if (false) {} #endif #if defined(SUPPORT_FILEFORMAT_FLAC) - else if (music->ctxType == MUSIC_AUDIO_FLAC) drflac_free(music->ctxFlac); + else if (music->ctxType == MUSIC_AUDIO_FLAC) drflac_RL_FREE(music->ctxFlac); #endif #if defined(SUPPORT_FILEFORMAT_MP3) else if (music->ctxType == MUSIC_AUDIO_MP3) drmp3_uninit(&music->ctxMp3); @@ -1274,7 +1274,7 @@ void UnloadMusicStream(Music music) else if (music->ctxType == MUSIC_MODULE_MOD) jar_mod_unload(&music->ctxMod); #endif - free(music); + RL_FREE(music); } // Start music playing (open stream) @@ -1357,7 +1357,7 @@ void UpdateMusicStream(Music music) unsigned int subBufferSizeInFrames = ((AudioBuffer *)music->stream.audioBuffer)->bufferSizeInFrames/2; // NOTE: Using dynamic allocation because it could require more than 16KB - void *pcm = calloc(subBufferSizeInFrames*music->stream.channels*music->stream.sampleSize/8, 1); + void *pcm = RL_CALLOC(subBufferSizeInFrames*music->stream.channels*music->stream.sampleSize/8, 1); int samplesCount = 0; // Total size of data steamed in L+R samples for xm floats, individual L or R for ogg shorts @@ -1427,7 +1427,7 @@ void UpdateMusicStream(Music music) } // Free allocated pcm data - free(pcm); + RL_FREE(pcm); // Reset audio stream for looping if (streamEnding) @@ -1750,7 +1750,7 @@ static Wave LoadWAV(const char *fileName) else { // Allocate memory for data - wave.data = malloc(wavData.subChunkSize); + wave.data = RL_MALLOC(wavData.subChunkSize); // Read in the sound data into the soundData variable fread(wave.data, wavData.subChunkSize, 1, wavFile); @@ -1891,7 +1891,7 @@ static Wave LoadOGG(const char *fileName) float totalSeconds = stb_vorbis_stream_length_in_seconds(oggFile); if (totalSeconds > 10) TraceLog(LOG_WARNING, "[%s] Ogg audio length is larger than 10 seconds (%f), that's a big file in memory, consider music streaming", fileName, totalSeconds); - wave.data = (short *)malloc(wave.sampleCount*wave.channels*sizeof(short)); + wave.data = (short *)RL_MALLOC(wave.sampleCount*wave.channels*sizeof(short)); // NOTE: Returns the number of samples to process (be careful! we ask for number of shorts!) int numSamplesOgg = stb_vorbis_get_samples_short_interleaved(oggFile, info.channels, (short *)wave.data, wave.sampleCount*wave.channels); diff --git a/src/raudio.h b/src/raudio.h index e8701814..f71a3083 100644 --- a/src/raudio.h +++ b/src/raudio.h @@ -56,7 +56,16 @@ //---------------------------------------------------------------------------------- // Defines and Macros //---------------------------------------------------------------------------------- -//... +// Allow custom memory allocators +#ifndef RL_MALLOC + #define RL_MALLOC(sz) malloc(sz) +#endif +#ifndef RL_CALLOC + #define RL_CALLOC(n,sz) calloc(n,sz) +#endif +#ifndef RL_FREE + #define RL_FREE(p) free(p) +#endif //---------------------------------------------------------------------------------- // Types and Structures Definition diff --git a/src/raylib.h b/src/raylib.h index 424a9dd7..43260e06 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -99,6 +99,17 @@ // Shader and material limits #define MAX_SHADER_LOCATIONS 32 // Maximum number of predefined locations stored in shader struct #define MAX_MATERIAL_MAPS 12 // Maximum number of texture maps stored in shader struct + +// Allow custom memory allocators +#ifndef RL_MALLOC + #define RL_MALLOC(sz) malloc(sz) +#endif +#ifndef RL_CALLOC + #define RL_CALLOC(n,sz) calloc(n,sz) +#endif +#ifndef RL_FREE + #define RL_FREE(p) free(p) +#endif // NOTE: MSC C++ compiler does not support compound literals (C99 feature) // Plain structures in C++ (without constructors) can be initialized from { } initializers. diff --git a/src/rlgl.h b/src/rlgl.h index 0356858a..c2ddd028 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -1492,7 +1492,7 @@ void rlglInit(int width, int height) glGetIntegerv(GL_NUM_EXTENSIONS, &numExt); #if defined(_MSC_VER) - const char **extList = malloc(sizeof(const char *)*numExt); + const char **extList = RL_MALLOC(sizeof(const char *)*numExt); #else const char *extList[numExt]; #endif @@ -1504,7 +1504,7 @@ void rlglInit(int width, int height) // NOTE: We have to duplicate string because glGetString() returns a const string int len = strlen(extensions) + 1; - char *extensionsDup = (char *)malloc(len); + char *extensionsDup = (char *)RL_MALLOC(len); strcpy(extensionsDup, extensions); // NOTE: String could be splitted using strtok() function (string.h) @@ -1520,7 +1520,7 @@ void rlglInit(int width, int height) extList[numExt] = strtok(NULL, " "); } - free(extensionsDup); // Duplicated string must be deallocated + RL_FREE(extensionsDup); // Duplicated string must be deallocated numExt -= 1; #endif @@ -1595,7 +1595,7 @@ void rlglInit(int width, int height) } #if defined(_WIN32) && defined(_MSC_VER) - free(extList); + RL_FREE(extList); #endif #if defined(GRAPHICS_API_OPENGL_ES2) @@ -1640,7 +1640,7 @@ void rlglInit(int width, int height) transformMatrix = MatrixIdentity(); // Init draw calls tracking system - draws = (DrawCall *)malloc(sizeof(DrawCall)*MAX_DRAWCALL_REGISTERED); + draws = (DrawCall *)RL_MALLOC(sizeof(DrawCall)*MAX_DRAWCALL_REGISTERED); for (int i = 0; i < MAX_DRAWCALL_REGISTERED; i++) { @@ -1710,7 +1710,7 @@ void rlglClose(void) TraceLog(LOG_INFO, "[TEX ID %i] Unloaded texture data (base white texture) from VRAM", defaultTextureId); - free(draws); + RL_FREE(draws); #endif } @@ -2300,7 +2300,7 @@ void rlGenerateMipmaps(Texture2D *texture) } texture->mipmaps = mipmapCount + 1; - free(data); // Once mipmaps have been generated and data has been uploaded to GPU VRAM, we can discard RAM data + RL_FREE(data); // Once mipmaps have been generated and data has been uploaded to GPU VRAM, we can discard RAM data TraceLog(LOG_WARNING, "[TEX ID %i] Mipmaps [%i] generated manually on CPU side", texture->id, texture->mipmaps); } @@ -2735,18 +2735,18 @@ void rlDrawMesh(Mesh mesh, Material material, Matrix transform) // Unload mesh data from CPU and GPU void rlUnloadMesh(Mesh *mesh) { - free(mesh->vertices); - free(mesh->texcoords); - free(mesh->normals); - free(mesh->colors); - free(mesh->tangents); - free(mesh->texcoords2); - free(mesh->indices); - - free(mesh->animVertices); - free(mesh->animNormals); - free(mesh->boneWeights); - free(mesh->boneIds); + RL_FREE(mesh->vertices); + RL_FREE(mesh->texcoords); + RL_FREE(mesh->normals); + RL_FREE(mesh->colors); + RL_FREE(mesh->tangents); + RL_FREE(mesh->texcoords2); + RL_FREE(mesh->indices); + + RL_FREE(mesh->animVertices); + RL_FREE(mesh->animNormals); + RL_FREE(mesh->boneWeights); + RL_FREE(mesh->boneIds); rlDeleteBuffers(mesh->vboId[0]); // vertex rlDeleteBuffers(mesh->vboId[1]); // texcoords @@ -2762,14 +2762,14 @@ void rlUnloadMesh(Mesh *mesh) // Read screen pixel data (color buffer) unsigned char *rlReadScreenPixels(int width, int height) { - unsigned char *screenData = (unsigned char *)calloc(width*height*4, sizeof(unsigned char)); + unsigned char *screenData = (unsigned char *)RL_CALLOC(width*height*4, sizeof(unsigned char)); // NOTE 1: glReadPixels returns image flipped vertically -> (0,0) is the bottom left corner of the framebuffer // NOTE 2: We are getting alpha channel! Be careful, it can be transparent if not cleared properly! glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, screenData); // Flip image vertically! - unsigned char *imgData = (unsigned char *)malloc(width*height*sizeof(unsigned char)*4); + unsigned char *imgData = (unsigned char *)RL_MALLOC(width*height*sizeof(unsigned char)*4); for (int y = height - 1; y >= 0; y--) { @@ -2783,7 +2783,7 @@ unsigned char *rlReadScreenPixels(int width, int height) } } - free(screenData); + RL_FREE(screenData); return imgData; // NOTE: image data should be freed } @@ -2815,7 +2815,7 @@ void *rlReadTexturePixels(Texture2D texture) if ((glInternalFormat != -1) && (texture.format < COMPRESSED_DXT1_RGB)) { - pixels = (unsigned char *)malloc(size); + pixels = (unsigned char *)RL_MALLOC(size); glGetTexImage(GL_TEXTURE_2D, 0, glFormat, glType, pixels); } else TraceLog(LOG_WARNING, "Texture data retrieval not suported for pixel format"); @@ -2841,7 +2841,7 @@ void *rlReadTexturePixels(Texture2D texture) glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture.id, 0); // Allocate enough memory to read back our texture data - pixels = (unsigned char *)malloc(GetPixelDataSize(texture.width, texture.height, texture.format)); + pixels = (unsigned char *)RL_MALLOC(GetPixelDataSize(texture.width, texture.height, texture.format)); // Get OpenGL internal formats and data type from our texture format unsigned int glInternalFormat, glFormat, glType; @@ -2911,7 +2911,7 @@ char *LoadText(const char *fileName) if (size > 0) { - text = (char *)malloc(sizeof(char)*(size + 1)); + text = (char *)RL_MALLOC(sizeof(char)*(size + 1)); int count = fread(text, sizeof(char), size, textFile); text[count] = '\0'; } @@ -2938,8 +2938,8 @@ Shader LoadShader(const char *vsFileName, const char *fsFileName) shader = LoadShaderCode(vShaderStr, fShaderStr); - if (vShaderStr != NULL) free(vShaderStr); - if (fShaderStr != NULL) free(fShaderStr); + if (vShaderStr != NULL) RL_FREE(vShaderStr); + if (fShaderStr != NULL) RL_FREE(fShaderStr); return shader; } @@ -3758,7 +3758,7 @@ static unsigned int CompileShader(const char *shaderStr, int type) glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &maxLength); #if defined(_MSC_VER) - char *log = malloc(maxLength); + char *log = RL_MALLOC(maxLength); #else char log[maxLength]; #endif @@ -3767,7 +3767,7 @@ static unsigned int CompileShader(const char *shaderStr, int type) TraceLog(LOG_INFO, "%s", log); #if defined(_MSC_VER) - free(log); + RL_FREE(log); #endif } else TraceLog(LOG_INFO, "[SHDR ID %i] Shader compiled successfully", shader); @@ -3814,7 +3814,7 @@ static unsigned int LoadShaderProgram(unsigned int vShaderId, unsigned int fShad glGetProgramiv(program, GL_INFO_LOG_LENGTH, &maxLength); #if defined(_MSC_VER) - char *log = malloc(maxLength); + char *log = RL_MALLOC(maxLength); #else char log[maxLength]; #endif @@ -3823,7 +3823,7 @@ static unsigned int LoadShaderProgram(unsigned int vShaderId, unsigned int fShad TraceLog(LOG_INFO, "%s", log); #if defined(_MSC_VER) - free(log); + RL_FREE(log); #endif glDeleteProgram(program); @@ -3984,13 +3984,13 @@ static void LoadBuffersDefault(void) //-------------------------------------------------------------------------------------------- for (int i = 0; i < MAX_BATCH_BUFFERING; i++) { - vertexData[i].vertices = (float *)malloc(sizeof(float)*3*4*MAX_BATCH_ELEMENTS); // 3 float by vertex, 4 vertex by quad - vertexData[i].texcoords = (float *)malloc(sizeof(float)*2*4*MAX_BATCH_ELEMENTS); // 2 float by texcoord, 4 texcoord by quad - vertexData[i].colors = (unsigned char *)malloc(sizeof(unsigned char)*4*4*MAX_BATCH_ELEMENTS); // 4 float by color, 4 colors by quad + vertexData[i].vertices = (float *)RL_MALLOC(sizeof(float)*3*4*MAX_BATCH_ELEMENTS); // 3 float by vertex, 4 vertex by quad + vertexData[i].texcoords = (float *)RL_MALLOC(sizeof(float)*2*4*MAX_BATCH_ELEMENTS); // 2 float by texcoord, 4 texcoord by quad + vertexData[i].colors = (unsigned char *)RL_MALLOC(sizeof(unsigned char)*4*4*MAX_BATCH_ELEMENTS); // 4 float by color, 4 colors by quad #if defined(GRAPHICS_API_OPENGL_33) - vertexData[i].indices = (unsigned int *)malloc(sizeof(unsigned int)*6*MAX_BATCH_ELEMENTS); // 6 int by quad (indices) + vertexData[i].indices = (unsigned int *)RL_MALLOC(sizeof(unsigned int)*6*MAX_BATCH_ELEMENTS); // 6 int by quad (indices) #elif defined(GRAPHICS_API_OPENGL_ES2) - vertexData[i].indices = (unsigned short *)malloc(sizeof(unsigned short)*6*MAX_BATCH_ELEMENTS); // 6 int by quad (indices) + vertexData[i].indices = (unsigned short *)RL_MALLOC(sizeof(unsigned short)*6*MAX_BATCH_ELEMENTS); // 6 int by quad (indices) #endif for (int j = 0; j < (3*4*MAX_BATCH_ELEMENTS); j++) vertexData[i].vertices[j] = 0.0f; @@ -4266,10 +4266,10 @@ static void UnloadBuffersDefault(void) if (vaoSupported) glDeleteVertexArrays(1, &vertexData[i].vaoId); // Free vertex arrays memory from CPU (RAM) - free(vertexData[i].vertices); - free(vertexData[i].texcoords); - free(vertexData[i].colors); - free(vertexData[i].indices); + RL_FREE(vertexData[i].vertices); + RL_FREE(vertexData[i].texcoords); + RL_FREE(vertexData[i].colors); + RL_FREE(vertexData[i].indices); } } @@ -4444,7 +4444,7 @@ static int GenerateMipmaps(unsigned char *data, int baseWidth, int baseHeight) // Generate mipmaps // NOTE: Every mipmap data is stored after data - Color *image = (Color *)malloc(width*height*sizeof(Color)); + Color *image = (Color *)RL_MALLOC(width*height*sizeof(Color)); Color *mipmap = NULL; int offset = 0; int j = 0; @@ -4481,13 +4481,13 @@ static int GenerateMipmaps(unsigned char *data, int baseWidth, int baseHeight) j++; } - free(image); + RL_FREE(image); image = mipmap; mipmap = NULL; } - free(mipmap); // free mipmap data + RL_FREE(mipmap); // free mipmap data return mipmapCount; } @@ -4501,7 +4501,7 @@ static Color *GenNextMipmap(Color *srcData, int srcWidth, int srcHeight) int width = srcWidth/2; int height = srcHeight/2; - Color *mipmap = (Color *)malloc(width*height*sizeof(Color)); + Color *mipmap = (Color *)RL_MALLOC(width*height*sizeof(Color)); // Scaling algorithm works perfectly (box-filter) for (int y = 0; y < height; y++) diff --git a/src/text.c b/src/text.c index bd9e09f0..22bd7659 100644 --- a/src/text.c +++ b/src/text.c @@ -174,7 +174,7 @@ extern void LoadDefaultFont(void) int imWidth = 128; int imHeight = 128; - Color *imagePixels = (Color *)malloc(imWidth*imHeight*sizeof(Color)); + Color *imagePixels = (Color *)RL_MALLOC(imWidth*imHeight*sizeof(Color)); for (int i = 0; i < imWidth*imHeight; i++) imagePixels[i] = BLANK; // Initialize array @@ -196,7 +196,7 @@ extern void LoadDefaultFont(void) Image image = LoadImageEx(imagePixels, imWidth, imHeight); ImageFormat(&image, UNCOMPRESSED_GRAY_ALPHA); - free(imagePixels); + RL_FREE(imagePixels); defaultFont.texture = LoadTextureFromImage(image); UnloadImage(image); @@ -206,7 +206,7 @@ extern void LoadDefaultFont(void) // Allocate space for our characters info data // NOTE: This memory should be freed at end! --> CloseWindow() - defaultFont.chars = (CharInfo *)malloc(defaultFont.charsCount*sizeof(CharInfo)); + defaultFont.chars = (CharInfo *)RL_MALLOC(defaultFont.charsCount*sizeof(CharInfo)); int currentLine = 0; int currentPosX = charsDivisor; @@ -249,7 +249,7 @@ extern void LoadDefaultFont(void) extern void UnloadDefaultFont(void) { UnloadTexture(defaultFont.texture); - free(defaultFont.chars); + RL_FREE(defaultFont.chars); } #endif // SUPPORT_DEFAULT_FONT @@ -407,7 +407,7 @@ Font LoadFontFromImage(Image image, Color key, int firstChar) // Create a new image with the processed color data (key color replaced by BLANK) Image fontClear = LoadImageEx(pixels, image.width, image.height); - free(pixels); // Free pixels array memory + RL_FREE(pixels); // Free pixels array memory // Create spritefont with all data parsed from image Font spriteFont = { 0 }; @@ -419,7 +419,7 @@ Font LoadFontFromImage(Image image, Color key, int firstChar) // We got tempCharValues and tempCharsRecs populated with chars data // Now we move temp data to sized charValues and charRecs arrays - spriteFont.chars = (CharInfo *)malloc(spriteFont.charsCount*sizeof(CharInfo)); + spriteFont.chars = (CharInfo *)RL_MALLOC(spriteFont.charsCount*sizeof(CharInfo)); for (int i = 0; i < spriteFont.charsCount; i++) { @@ -466,7 +466,7 @@ CharInfo *LoadFontData(const char *fileName, int fontSize, int *fontChars, int c long size = ftell(fontFile); // Get file size fseek(fontFile, 0, SEEK_SET); // Reset file pointer - unsigned char *fontBuffer = (unsigned char *)malloc(size); + unsigned char *fontBuffer = (unsigned char *)RL_MALLOC(size); fread(fontBuffer, size, 1, fontFile); fclose(fontFile); @@ -491,12 +491,12 @@ CharInfo *LoadFontData(const char *fileName, int fontSize, int *fontChars, int c int genFontChars = false; if (fontChars == NULL) { - fontChars = (int *)malloc(charsCount*sizeof(int)); + fontChars = (int *)RL_MALLOC(charsCount*sizeof(int)); for (int i = 0; i < charsCount; i++) fontChars[i] = i + 32; genFontChars = true; } - chars = (CharInfo *)malloc(charsCount*sizeof(CharInfo)); + chars = (CharInfo *)RL_MALLOC(charsCount*sizeof(CharInfo)); // NOTE: Using simple packaging, one char after another for (int i = 0; i < charsCount; i++) @@ -540,8 +540,8 @@ CharInfo *LoadFontData(const char *fileName, int fontSize, int *fontChars, int c chars[i].advanceX *= scaleFactor; } - free(fontBuffer); - if (genFontChars) free(fontChars); + RL_FREE(fontBuffer); + if (genFontChars) RL_FREE(fontChars); } else TraceLog(LOG_WARNING, "[%s] TTF file could not be opened", fileName); #else @@ -572,7 +572,7 @@ Image GenImageFontAtlas(CharInfo *chars, int charsCount, int fontSize, int paddi atlas.width = imageSize; // Atlas bitmap width atlas.height = imageSize; // Atlas bitmap height - atlas.data = (unsigned char *)calloc(1, atlas.width*atlas.height); // Create a bitmap to store characters (8 bpp) + atlas.data = (unsigned char *)RL_CALLOC(1, atlas.width*atlas.height); // Create a bitmap to store characters (8 bpp) atlas.format = UNCOMPRESSED_GRAYSCALE; atlas.mipmaps = 1; @@ -619,11 +619,11 @@ Image GenImageFontAtlas(CharInfo *chars, int charsCount, int fontSize, int paddi { TraceLog(LOG_DEBUG, "Using Skyline packing algorythm!"); - stbrp_context *context = (stbrp_context *)malloc(sizeof(*context)); - stbrp_node *nodes = (stbrp_node *)malloc(charsCount*sizeof(*nodes)); + stbrp_context *context = (stbrp_context *)RL_MALLOC(sizeof(*context)); + stbrp_node *nodes = (stbrp_node *)RL_MALLOC(charsCount*sizeof(*nodes)); stbrp_init_target(context, atlas.width, atlas.height, nodes, charsCount); - stbrp_rect *rects = (stbrp_rect *)malloc(charsCount*sizeof(stbrp_rect)); + stbrp_rect *rects = (stbrp_rect *)RL_MALLOC(charsCount*sizeof(stbrp_rect)); // Fill rectangles for packaging for (int i = 0; i < charsCount; i++) @@ -655,16 +655,16 @@ Image GenImageFontAtlas(CharInfo *chars, int charsCount, int fontSize, int paddi else TraceLog(LOG_WARNING, "Character could not be packed: %i", i); } - free(rects); - free(nodes); - free(context); + RL_FREE(rects); + RL_FREE(nodes); + RL_FREE(context); } // TODO: Crop image if required for smaller size // Convert image data from GRAYSCALE to GRAY_ALPHA // WARNING: ImageAlphaMask(&atlas, atlas) does not work in this case, requires manual operation - unsigned char *dataGrayAlpha = (unsigned char *)malloc(imageSize*imageSize*sizeof(unsigned char)*2); // Two channels + unsigned char *dataGrayAlpha = (unsigned char *)RL_MALLOC(imageSize*imageSize*sizeof(unsigned char)*2); // Two channels for (int i = 0, k = 0; i < atlas.width*atlas.height; i++, k += 2) { @@ -672,7 +672,7 @@ Image GenImageFontAtlas(CharInfo *chars, int charsCount, int fontSize, int paddi dataGrayAlpha[k + 1] = ((unsigned char *)atlas.data)[i]; } - free(atlas.data); + RL_FREE(atlas.data); atlas.data = dataGrayAlpha; atlas.format = UNCOMPRESSED_GRAY_ALPHA; @@ -688,10 +688,10 @@ void UnloadFont(Font font) { for (int i = 0; i < font.charsCount; i++) { - free(font.chars[i].data); + RL_FREE(font.chars[i].data); } UnloadTexture(font.texture); - free(font.chars); + RL_FREE(font.chars); TraceLog(LOG_DEBUG, "Unloaded sprite font data"); } @@ -1214,7 +1214,7 @@ const char *TextReplace(char *text, const char *replace, const char *by) for (count = 0; (temp = strstr(insertPoint, replace)); count++) insertPoint = temp + replaceLen; // Allocate returning string and point temp to it - temp = result = malloc(strlen(text) + (byLen - replaceLen)*count + 1); + temp = result = RL_MALLOC(strlen(text) + (byLen - replaceLen)*count + 1); if (!result) return NULL; // Memory could not be allocated @@ -1245,7 +1245,7 @@ const char *TextInsert(const char *text, const char *insert, int position) int textLen = strlen(text); int insertLen = strlen(insert); - char *result = (char *)malloc(textLen + insertLen + 1); + char *result = (char *)RL_MALLOC(textLen + insertLen + 1); for (int i = 0; i < position; i++) result[i] = text[i]; for (int i = position; i < insertLen + position; i++) result[i] = insert[i]; @@ -1477,7 +1477,7 @@ static Font LoadBMFont(const char *fileName) } // NOTE: We need some extra space to avoid memory corruption on next allocations! - texPath = malloc(strlen(fileName) - strlen(lastSlash) + strlen(texFileName) + 4); + texPath = RL_MALLOC(strlen(fileName) - strlen(lastSlash) + strlen(texFileName) + 4); // NOTE: strcat() and strncat() required a '\0' terminated string to work! *texPath = '\0'; @@ -1501,13 +1501,13 @@ static Font LoadBMFont(const char *fileName) else font.texture = LoadTextureFromImage(imFont); UnloadImage(imFont); - free(texPath); + RL_FREE(texPath); // Fill font characters info data font.baseSize = fontSize; font.charsCount = charsCount; - font.chars = (CharInfo *)malloc(charsCount*sizeof(CharInfo)); + font.chars = (CharInfo *)RL_MALLOC(charsCount*sizeof(CharInfo)); int charId, charX, charY, charWidth, charHeight, charOffsetX, charOffsetY, charAdvanceX; diff --git a/src/textures.c b/src/textures.c index 7059fabb..eb743026 100644 --- a/src/textures.c +++ b/src/textures.c @@ -112,9 +112,14 @@ defined(SUPPORT_FILEFORMAT_GIF) || \ defined(SUPPORT_FILEFORMAT_PIC) || \ defined(SUPPORT_FILEFORMAT_HDR)) + + #define STBI_MALLOC RL_MALLOC + #define STBI_FREE RL_FREE + #define STBI_REALLOC(p,newsz) realloc(p,newsz) + #define STB_IMAGE_IMPLEMENTATION - #include "external/stb_image.h" // Required for: stbi_load_from_file() - // NOTE: Used to read image data (multiple formats support) + #include "external/stb_image.h" // Required for: stbi_load_from_file() + // NOTE: Used to read image data (multiple formats support) #endif #if defined(SUPPORT_IMAGE_EXPORT) @@ -305,7 +310,7 @@ Image LoadImageEx(Color *pixels, int width, int height) int k = 0; - image.data = (unsigned char *)malloc(image.width*image.height*4*sizeof(unsigned char)); + image.data = (unsigned char *)RL_MALLOC(image.width*image.height*4*sizeof(unsigned char)); for (int i = 0; i < image.width*image.height*4; i += 4) { @@ -353,7 +358,7 @@ Image LoadImageRaw(const char *fileName, int width, int height, int format, int unsigned int size = GetPixelDataSize(width, height, format); - image.data = malloc(size); // Allocate required memory in bytes + image.data = RL_MALLOC(size); // Allocate required memory in bytes // NOTE: fread() returns num read elements instead of bytes, // to get bytes we need to read (1 byte size, elements) instead of (x byte size, 1 element) @@ -364,7 +369,7 @@ Image LoadImageRaw(const char *fileName, int width, int height, int format, int { TraceLog(LOG_WARNING, "[%s] RAW image data can not be read, wrong requested format or size", fileName); - free(image.data); + RL_FREE(image.data); } else { @@ -425,7 +430,7 @@ RenderTexture2D LoadRenderTexture(int width, int height) // Unload image from CPU memory (RAM) void UnloadImage(Image image) { - free(image.data); + RL_FREE(image.data); } // Unload texture from GPU memory (VRAM) @@ -448,7 +453,7 @@ void UnloadRenderTexture(RenderTexture2D target) // Get pixel data from image in the form of Color struct array Color *GetImageData(Image image) { - Color *pixels = (Color *)malloc(image.width*image.height*sizeof(Color)); + Color *pixels = (Color *)RL_MALLOC(image.width*image.height*sizeof(Color)); if (image.format >= COMPRESSED_DXT1_RGB) TraceLog(LOG_WARNING, "Pixel data retrieval not supported for compressed image formats"); else @@ -563,7 +568,7 @@ Color *GetImageData(Image image) // Get pixel data from image as Vector4 array (float normalized) Vector4 *GetImageDataNormalized(Image image) { - Vector4 *pixels = (Vector4 *)malloc(image.width*image.height*sizeof(Vector4)); + Vector4 *pixels = (Vector4 *)RL_MALLOC(image.width*image.height*sizeof(Vector4)); if (image.format >= COMPRESSED_DXT1_RGB) TraceLog(LOG_WARNING, "Pixel data retrieval not supported for compressed image formats"); else @@ -797,7 +802,7 @@ void ExportImage(Image image, const char *fileName) fclose(rawFile); } - free(imgData); + RL_FREE(imgData); #endif if (success != 0) TraceLog(LOG_INFO, "Image exported successfully: %s", fileName); @@ -863,7 +868,7 @@ Image ImageCopy(Image image) if (height < 1) height = 1; } - newImage.data = malloc(size); + newImage.data = RL_MALLOC(size); if (newImage.data != NULL) { @@ -896,7 +901,7 @@ void ImageToPOT(Image *image, Color fillColor) Color *pixelsPOT = NULL; // Generate POT array from NPOT data - pixelsPOT = (Color *)malloc(potWidth*potHeight*sizeof(Color)); + pixelsPOT = (Color *)RL_MALLOC(potWidth*potHeight*sizeof(Color)); for (int j = 0; j < potHeight; j++) { @@ -909,15 +914,15 @@ void ImageToPOT(Image *image, Color fillColor) TraceLog(LOG_WARNING, "Image converted to POT: (%ix%i) -> (%ix%i)", image->width, image->height, potWidth, potHeight); - free(pixels); // Free pixels data - free(image->data); // Free old image data + RL_FREE(pixels); // Free pixels data + RL_FREE(image->data); // Free old image data int format = image->format; // Store image data format to reconvert later // NOTE: Image size changes, new width and height *image = LoadImageEx(pixelsPOT, potWidth, potHeight); - free(pixelsPOT); // Free POT pixels data + RL_FREE(pixelsPOT); // Free POT pixels data ImageFormat(image, format); // Reconvert image to previous format } @@ -932,7 +937,7 @@ void ImageFormat(Image *image, int newFormat) { Vector4 *pixels = GetImageDataNormalized(*image); // Supports 8 to 32 bit per channel - free(image->data); // WARNING! We loose mipmaps data --> Regenerated at the end... + RL_FREE(image->data); // WARNING! We loose mipmaps data --> Regenerated at the end... image->data = NULL; image->format = newFormat; @@ -942,7 +947,7 @@ void ImageFormat(Image *image, int newFormat) { case UNCOMPRESSED_GRAYSCALE: { - image->data = (unsigned char *)malloc(image->width*image->height*sizeof(unsigned char)); + image->data = (unsigned char *)RL_MALLOC(image->width*image->height*sizeof(unsigned char)); for (int i = 0; i < image->width*image->height; i++) { @@ -952,7 +957,7 @@ void ImageFormat(Image *image, int newFormat) } break; case UNCOMPRESSED_GRAY_ALPHA: { - image->data = (unsigned char *)malloc(image->width*image->height*2*sizeof(unsigned char)); + image->data = (unsigned char *)RL_MALLOC(image->width*image->height*2*sizeof(unsigned char)); for (int i = 0; i < image->width*image->height*2; i += 2, k++) { @@ -963,7 +968,7 @@ void ImageFormat(Image *image, int newFormat) } break; case UNCOMPRESSED_R5G6B5: { - image->data = (unsigned short *)malloc(image->width*image->height*sizeof(unsigned short)); + image->data = (unsigned short *)RL_MALLOC(image->width*image->height*sizeof(unsigned short)); unsigned char r = 0; unsigned char g = 0; @@ -981,7 +986,7 @@ void ImageFormat(Image *image, int newFormat) } break; case UNCOMPRESSED_R8G8B8: { - image->data = (unsigned char *)malloc(image->width*image->height*3*sizeof(unsigned char)); + image->data = (unsigned char *)RL_MALLOC(image->width*image->height*3*sizeof(unsigned char)); for (int i = 0, k = 0; i < image->width*image->height*3; i += 3, k++) { @@ -994,7 +999,7 @@ void ImageFormat(Image *image, int newFormat) { #define ALPHA_THRESHOLD 50 - image->data = (unsigned short *)malloc(image->width*image->height*sizeof(unsigned short)); + image->data = (unsigned short *)RL_MALLOC(image->width*image->height*sizeof(unsigned short)); unsigned char r = 0; unsigned char g = 0; @@ -1014,7 +1019,7 @@ void ImageFormat(Image *image, int newFormat) } break; case UNCOMPRESSED_R4G4B4A4: { - image->data = (unsigned short *)malloc(image->width*image->height*sizeof(unsigned short)); + image->data = (unsigned short *)RL_MALLOC(image->width*image->height*sizeof(unsigned short)); unsigned char r = 0; unsigned char g = 0; @@ -1034,7 +1039,7 @@ void ImageFormat(Image *image, int newFormat) } break; case UNCOMPRESSED_R8G8B8A8: { - image->data = (unsigned char *)malloc(image->width*image->height*4*sizeof(unsigned char)); + image->data = (unsigned char *)RL_MALLOC(image->width*image->height*4*sizeof(unsigned char)); for (int i = 0, k = 0; i < image->width*image->height*4; i += 4, k++) { @@ -1048,7 +1053,7 @@ void ImageFormat(Image *image, int newFormat) { // WARNING: Image is converted to GRAYSCALE eqeuivalent 32bit - image->data = (float *)malloc(image->width*image->height*sizeof(float)); + image->data = (float *)RL_MALLOC(image->width*image->height*sizeof(float)); for (int i = 0; i < image->width*image->height; i++) { @@ -1057,7 +1062,7 @@ void ImageFormat(Image *image, int newFormat) } break; case UNCOMPRESSED_R32G32B32: { - image->data = (float *)malloc(image->width*image->height*3*sizeof(float)); + image->data = (float *)RL_MALLOC(image->width*image->height*3*sizeof(float)); for (int i = 0, k = 0; i < image->width*image->height*3; i += 3, k++) { @@ -1068,7 +1073,7 @@ void ImageFormat(Image *image, int newFormat) } break; case UNCOMPRESSED_R32G32B32A32: { - image->data = (float *)malloc(image->width*image->height*4*sizeof(float)); + image->data = (float *)RL_MALLOC(image->width*image->height*4*sizeof(float)); for (int i = 0, k = 0; i < image->width*image->height*4; i += 4, k++) { @@ -1081,7 +1086,7 @@ void ImageFormat(Image *image, int newFormat) default: break; } - free(pixels); + RL_FREE(pixels); pixels = NULL; // In case original image had mipmaps, generate mipmaps for formated image @@ -1286,7 +1291,7 @@ void ImageCrop(Image *image, Rectangle crop) { // Start the cropping process Color *pixels = GetImageData(*image); // Get data as Color pixels array - Color *cropPixels = (Color *)malloc((int)crop.width*(int)crop.height*sizeof(Color)); + Color *cropPixels = (Color *)RL_MALLOC((int)crop.width*(int)crop.height*sizeof(Color)); for (int j = (int)crop.y; j < (int)(crop.y + crop.height); j++) { @@ -1296,7 +1301,7 @@ void ImageCrop(Image *image, Rectangle crop) } } - free(pixels); + RL_FREE(pixels); int format = image->format; @@ -1304,7 +1309,7 @@ void ImageCrop(Image *image, Rectangle crop) *image = LoadImageEx(cropPixels, (int)crop.width, (int)crop.height); - free(cropPixels); + RL_FREE(cropPixels); // Reformat 32bit RGBA image to original format ImageFormat(image, format); @@ -1341,7 +1346,7 @@ void ImageAlphaCrop(Image *image, float threshold) Rectangle crop = { xMin, yMin, (xMax + 1) - xMin, (yMax + 1) - yMin }; - free(pixels); + RL_FREE(pixels); // Check for not empty image brefore cropping if (!((xMax < xMin) || (yMax < yMin))) ImageCrop(image, crop); @@ -1355,7 +1360,7 @@ void ImageResize(Image *image, int newWidth, int newHeight) { // Get data as Color pixels array to work with it Color *pixels = GetImageData(*image); - Color *output = (Color *)malloc(newWidth*newHeight*sizeof(Color)); + Color *output = (Color *)RL_MALLOC(newWidth*newHeight*sizeof(Color)); // NOTE: Color data is casted to (unsigned char *), there shouldn't been any problem... stbir_resize_uint8((unsigned char *)pixels, image->width, image->height, 0, (unsigned char *)output, newWidth, newHeight, 0, 4); @@ -1367,15 +1372,15 @@ void ImageResize(Image *image, int newWidth, int newHeight) *image = LoadImageEx(output, newWidth, newHeight); ImageFormat(image, format); // Reformat 32bit RGBA image to original format - free(output); - free(pixels); + RL_FREE(output); + RL_FREE(pixels); } // Resize and image to new size using Nearest-Neighbor scaling algorithm void ImageResizeNN(Image *image,int newWidth,int newHeight) { Color *pixels = GetImageData(*image); - Color *output = (Color *)malloc(newWidth*newHeight*sizeof(Color)); + Color *output = (Color *)RL_MALLOC(newWidth*newHeight*sizeof(Color)); // EDIT: added +1 to account for an early rounding problem int xRatio = (int)((image->width << 16)/newWidth) + 1; @@ -1400,8 +1405,8 @@ void ImageResizeNN(Image *image,int newWidth,int newHeight) *image = LoadImageEx(output, newWidth, newHeight); ImageFormat(image, format); // Reformat 32bit RGBA image to original format - free(output); - free(pixels); + RL_FREE(output); + RL_FREE(pixels); } // Resize canvas and fill with color @@ -1555,7 +1560,7 @@ void ImageDither(Image *image, int rBpp, int gBpp, int bBpp, int aBpp) { Color *pixels = GetImageData(*image); - free(image->data); // free old image data + RL_FREE(image->data); // free old image data if ((image->format != UNCOMPRESSED_R8G8B8) && (image->format != UNCOMPRESSED_R8G8B8A8)) { @@ -1573,7 +1578,7 @@ void ImageDither(Image *image, int rBpp, int gBpp, int bBpp, int aBpp) } // NOTE: We will store the dithered data as unsigned short (16bpp) - image->data = (unsigned short *)malloc(image->width*image->height*sizeof(unsigned short)); + image->data = (unsigned short *)RL_MALLOC(image->width*image->height*sizeof(unsigned short)); Color oldPixel = WHITE; Color newPixel = WHITE; @@ -1641,7 +1646,7 @@ void ImageDither(Image *image, int rBpp, int gBpp, int bBpp, int aBpp) } } - free(pixels); + RL_FREE(pixels); } } @@ -1652,7 +1657,7 @@ Color *ImageExtractPalette(Image image, int maxPaletteSize, int *extractCount) #define COLOR_EQUAL(col1, col2) ((col1.r == col2.r)&&(col1.g == col2.g)&&(col1.b == col2.b)&&(col1.a == col2.a)) Color *pixels = GetImageData(image); - Color *palette = (Color *)malloc(maxPaletteSize*sizeof(Color)); + Color *palette = (Color *)RL_MALLOC(maxPaletteSize*sizeof(Color)); int palCount = 0; for (int i = 0; i < maxPaletteSize; i++) palette[i] = BLANK; // Set all colors to BLANK @@ -1689,7 +1694,7 @@ Color *ImageExtractPalette(Image image, int maxPaletteSize, int *extractCount) } } - free(pixels); + RL_FREE(pixels); *extractCount = palCount; @@ -1799,8 +1804,8 @@ void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec) *dst = LoadImageEx(dstPixels, (int)dst->width, (int)dst->height); ImageFormat(dst, dst->format); - free(srcPixels); - free(dstPixels); + RL_FREE(srcPixels); + RL_FREE(dstPixels); } // Create an image from text (default font) @@ -1933,7 +1938,7 @@ void ImageDrawTextEx(Image *dst, Vector2 position, Font font, const char *text, void ImageFlipVertical(Image *image) { Color *srcPixels = GetImageData(*image); - Color *dstPixels = (Color *)malloc(image->width*image->height*sizeof(Color)); + Color *dstPixels = (Color *)RL_MALLOC(image->width*image->height*sizeof(Color)); for (int y = 0; y < image->height; y++) { @@ -1947,8 +1952,8 @@ void ImageFlipVertical(Image *image) ImageFormat(&processed, image->format); UnloadImage(*image); - free(srcPixels); - free(dstPixels); + RL_FREE(srcPixels); + RL_FREE(dstPixels); image->data = processed.data; } @@ -1957,7 +1962,7 @@ void ImageFlipVertical(Image *image) void ImageFlipHorizontal(Image *image) { Color *srcPixels = GetImageData(*image); - Color *dstPixels = (Color *)malloc(image->width*image->height*sizeof(Color)); + Color *dstPixels = (Color *)RL_MALLOC(image->width*image->height*sizeof(Color)); for (int y = 0; y < image->height; y++) { @@ -1971,8 +1976,8 @@ void ImageFlipHorizontal(Image *image) ImageFormat(&processed, image->format); UnloadImage(*image); - free(srcPixels); - free(dstPixels); + RL_FREE(srcPixels); + RL_FREE(dstPixels); image->data = processed.data; } @@ -1981,7 +1986,7 @@ void ImageFlipHorizontal(Image *image) void ImageRotateCW(Image *image) { Color *srcPixels = GetImageData(*image); - Color *rotPixels = (Color *)malloc(image->width*image->height*sizeof(Color)); + Color *rotPixels = (Color *)RL_MALLOC(image->width*image->height*sizeof(Color)); for (int y = 0; y < image->height; y++) { @@ -1995,8 +2000,8 @@ void ImageRotateCW(Image *image) ImageFormat(&processed, image->format); UnloadImage(*image); - free(srcPixels); - free(rotPixels); + RL_FREE(srcPixels); + RL_FREE(rotPixels); image->data = processed.data; image->width = processed.width; @@ -2007,7 +2012,7 @@ void ImageRotateCW(Image *image) void ImageRotateCCW(Image *image) { Color *srcPixels = GetImageData(*image); - Color *rotPixels = (Color *)malloc(image->width*image->height*sizeof(Color)); + Color *rotPixels = (Color *)RL_MALLOC(image->width*image->height*sizeof(Color)); for (int y = 0; y < image->height; y++) { @@ -2021,8 +2026,8 @@ void ImageRotateCCW(Image *image) ImageFormat(&processed, image->format); UnloadImage(*image); - free(srcPixels); - free(rotPixels); + RL_FREE(srcPixels); + RL_FREE(rotPixels); image->data = processed.data; image->width = processed.width; @@ -2059,7 +2064,7 @@ void ImageColorTint(Image *image, Color color) Image processed = LoadImageEx(pixels, image->width, image->height); ImageFormat(&processed, image->format); UnloadImage(*image); - free(pixels); + RL_FREE(pixels); image->data = processed.data; } @@ -2082,7 +2087,7 @@ void ImageColorInvert(Image *image) Image processed = LoadImageEx(pixels, image->width, image->height); ImageFormat(&processed, image->format); UnloadImage(*image); - free(pixels); + RL_FREE(pixels); image->data = processed.data; } @@ -2142,7 +2147,7 @@ void ImageColorContrast(Image *image, float contrast) Image processed = LoadImageEx(pixels, image->width, image->height); ImageFormat(&processed, image->format); UnloadImage(*image); - free(pixels); + RL_FREE(pixels); image->data = processed.data; } @@ -2182,7 +2187,7 @@ void ImageColorBrightness(Image *image, int brightness) Image processed = LoadImageEx(pixels, image->width, image->height); ImageFormat(&processed, image->format); UnloadImage(*image); - free(pixels); + RL_FREE(pixels); image->data = processed.data; } @@ -2212,7 +2217,7 @@ void ImageColorReplace(Image *image, Color color, Color replace) Image processed = LoadImageEx(pixels, image->width, image->height); ImageFormat(&processed, image->format); UnloadImage(*image); - free(pixels); + RL_FREE(pixels); image->data = processed.data; } @@ -2221,13 +2226,13 @@ void ImageColorReplace(Image *image, Color color, Color replace) // Generate image: plain color Image GenImageColor(int width, int height, Color color) { - Color *pixels = (Color *)calloc(width*height, sizeof(Color)); + Color *pixels = (Color *)RL_CALLOC(width*height, sizeof(Color)); for (int i = 0; i < width*height; i++) pixels[i] = color; Image image = LoadImageEx(pixels, width, height); - free(pixels); + RL_FREE(pixels); return image; } @@ -2236,7 +2241,7 @@ Image GenImageColor(int width, int height, Color color) // Generate image: vertical gradient Image GenImageGradientV(int width, int height, Color top, Color bottom) { - Color *pixels = (Color *)malloc(width*height*sizeof(Color)); + Color *pixels = (Color *)RL_MALLOC(width*height*sizeof(Color)); for (int j = 0; j < height; j++) { @@ -2251,7 +2256,7 @@ Image GenImageGradientV(int width, int height, Color top, Color bottom) } Image image = LoadImageEx(pixels, width, height); - free(pixels); + RL_FREE(pixels); return image; } @@ -2259,7 +2264,7 @@ Image GenImageGradientV(int width, int height, Color top, Color bottom) // Generate image: horizontal gradient Image GenImageGradientH(int width, int height, Color left, Color right) { - Color *pixels = (Color *)malloc(width*height*sizeof(Color)); + Color *pixels = (Color *)RL_MALLOC(width*height*sizeof(Color)); for (int i = 0; i < width; i++) { @@ -2274,7 +2279,7 @@ Image GenImageGradientH(int width, int height, Color left, Color right) } Image image = LoadImageEx(pixels, width, height); - free(pixels); + RL_FREE(pixels); return image; } @@ -2282,7 +2287,7 @@ Image GenImageGradientH(int width, int height, Color left, Color right) // Generate image: radial gradient Image GenImageGradientRadial(int width, int height, float density, Color inner, Color outer) { - Color *pixels = (Color *)malloc(width*height*sizeof(Color)); + Color *pixels = (Color *)RL_MALLOC(width*height*sizeof(Color)); float radius = (width < height)? (float)width/2.0f : (float)height/2.0f; float centerX = (float)width/2.0f; @@ -2306,7 +2311,7 @@ Image GenImageGradientRadial(int width, int height, float density, Color inner, } Image image = LoadImageEx(pixels, width, height); - free(pixels); + RL_FREE(pixels); return image; } @@ -2314,7 +2319,7 @@ Image GenImageGradientRadial(int width, int height, float density, Color inner, // Generate image: checked Image GenImageChecked(int width, int height, int checksX, int checksY, Color col1, Color col2) { - Color *pixels = (Color *)malloc(width*height*sizeof(Color)); + Color *pixels = (Color *)RL_MALLOC(width*height*sizeof(Color)); for (int y = 0; y < height; y++) { @@ -2326,7 +2331,7 @@ Image GenImageChecked(int width, int height, int checksX, int checksY, Color col } Image image = LoadImageEx(pixels, width, height); - free(pixels); + RL_FREE(pixels); return image; } @@ -2334,7 +2339,7 @@ Image GenImageChecked(int width, int height, int checksX, int checksY, Color col // Generate image: white noise Image GenImageWhiteNoise(int width, int height, float factor) { - Color *pixels = (Color *)malloc(width*height*sizeof(Color)); + Color *pixels = (Color *)RL_MALLOC(width*height*sizeof(Color)); for (int i = 0; i < width*height; i++) { @@ -2343,7 +2348,7 @@ Image GenImageWhiteNoise(int width, int height, float factor) } Image image = LoadImageEx(pixels, width, height); - free(pixels); + RL_FREE(pixels); return image; } @@ -2351,7 +2356,7 @@ Image GenImageWhiteNoise(int width, int height, float factor) // Generate image: perlin noise Image GenImagePerlinNoise(int width, int height, int offsetX, int offsetY, float scale) { - Color *pixels = (Color *)malloc(width*height*sizeof(Color)); + Color *pixels = (Color *)RL_MALLOC(width*height*sizeof(Color)); for (int y = 0; y < height; y++) { @@ -2374,7 +2379,7 @@ Image GenImagePerlinNoise(int width, int height, int offsetX, int offsetY, float } Image image = LoadImageEx(pixels, width, height); - free(pixels); + RL_FREE(pixels); return image; } @@ -2382,13 +2387,13 @@ Image GenImagePerlinNoise(int width, int height, int offsetX, int offsetY, float // Generate image: cellular algorithm. Bigger tileSize means bigger cells Image GenImageCellular(int width, int height, int tileSize) { - Color *pixels = (Color *)malloc(width*height*sizeof(Color)); + Color *pixels = (Color *)RL_MALLOC(width*height*sizeof(Color)); int seedsPerRow = width/tileSize; int seedsPerCol = height/tileSize; int seedsCount = seedsPerRow * seedsPerCol; - Vector2 *seeds = (Vector2 *)malloc(seedsCount*sizeof(Vector2)); + Vector2 *seeds = (Vector2 *)RL_MALLOC(seedsCount*sizeof(Vector2)); for (int i = 0; i < seedsCount; i++) { @@ -2431,10 +2436,10 @@ Image GenImageCellular(int width, int height, int tileSize) } } - free(seeds); + RL_FREE(seeds); Image image = LoadImageEx(pixels, width, height); - free(pixels); + RL_FREE(pixels); return image; } @@ -2921,7 +2926,7 @@ static Image LoadDDS(const char *fileName) { if (ddsHeader.ddspf.flags == 0x40) // no alpha channel { - image.data = (unsigned short *)malloc(image.width*image.height*sizeof(unsigned short)); + image.data = (unsigned short *)RL_MALLOC(image.width*image.height*sizeof(unsigned short)); fread(image.data, image.width*image.height*sizeof(unsigned short), 1, ddsFile); image.format = UNCOMPRESSED_R5G6B5; @@ -2930,7 +2935,7 @@ static Image LoadDDS(const char *fileName) { if (ddsHeader.ddspf.aBitMask == 0x8000) // 1bit alpha { - image.data = (unsigned short *)malloc(image.width*image.height*sizeof(unsigned short)); + image.data = (unsigned short *)RL_MALLOC(image.width*image.height*sizeof(unsigned short)); fread(image.data, image.width*image.height*sizeof(unsigned short), 1, ddsFile); unsigned char alpha = 0; @@ -2947,7 +2952,7 @@ static Image LoadDDS(const char *fileName) } else if (ddsHeader.ddspf.aBitMask == 0xf000) // 4bit alpha { - image.data = (unsigned short *)malloc(image.width*image.height*sizeof(unsigned short)); + image.data = (unsigned short *)RL_MALLOC(image.width*image.height*sizeof(unsigned short)); fread(image.data, image.width*image.height*sizeof(unsigned short), 1, ddsFile); unsigned char alpha = 0; @@ -2967,14 +2972,14 @@ static Image LoadDDS(const char *fileName) if (ddsHeader.ddspf.flags == 0x40 && ddsHeader.ddspf.rgbBitCount == 24) // DDS_RGB, no compressed { // NOTE: not sure if this case exists... - image.data = (unsigned char *)malloc(image.width*image.height*3*sizeof(unsigned char)); + image.data = (unsigned char *)RL_MALLOC(image.width*image.height*3*sizeof(unsigned char)); fread(image.data, image.width*image.height*3, 1, ddsFile); image.format = UNCOMPRESSED_R8G8B8; } else if (ddsHeader.ddspf.flags == 0x41 && ddsHeader.ddspf.rgbBitCount == 32) // DDS_RGBA, no compressed { - image.data = (unsigned char *)malloc(image.width*image.height*4*sizeof(unsigned char)); + image.data = (unsigned char *)RL_MALLOC(image.width*image.height*4*sizeof(unsigned char)); fread(image.data, image.width*image.height*4, 1, ddsFile); unsigned char blue = 0; @@ -3001,7 +3006,7 @@ static Image LoadDDS(const char *fileName) TraceLog(LOG_DEBUG, "Pitch or linear size: %i", ddsHeader.pitchOrLinearSize); - image.data = (unsigned char *)malloc(size*sizeof(unsigned char)); + image.data = (unsigned char *)RL_MALLOC(size*sizeof(unsigned char)); fread(image.data, size, 1, ddsFile); @@ -3098,7 +3103,7 @@ static Image LoadPKM(const char *fileName) int size = image.width*image.height*bpp/8; // Total data size in bytes - image.data = (unsigned char *)malloc(size*sizeof(unsigned char)); + image.data = (unsigned char *)RL_MALLOC(size*sizeof(unsigned char)); fread(image.data, size, 1, pkmFile); @@ -3192,7 +3197,7 @@ static Image LoadKTX(const char *fileName) int dataSize; fread(&dataSize, sizeof(unsigned int), 1, ktxFile); - image.data = (unsigned char *)malloc(dataSize*sizeof(unsigned char)); + image.data = (unsigned char *)RL_MALLOC(dataSize*sizeof(unsigned char)); fread(image.data, dataSize, 1, ktxFile); @@ -3441,7 +3446,7 @@ static Image LoadPVR(const char *fileName) } int dataSize = image.width*image.height*bpp/8; // Total data size in bytes - image.data = (unsigned char *)malloc(dataSize*sizeof(unsigned char)); + image.data = (unsigned char *)RL_MALLOC(dataSize*sizeof(unsigned char)); // Read data from file fread(image.data, dataSize, 1, pvrFile); @@ -3518,7 +3523,7 @@ static Image LoadASTC(const char *fileName) { int dataSize = image.width*image.height*bpp/8; // Data size in bytes - image.data = (unsigned char *)malloc(dataSize*sizeof(unsigned char)); + image.data = (unsigned char *)RL_MALLOC(dataSize*sizeof(unsigned char)); fread(image.data, dataSize, 1, astcFile); if (bpp == 8) image.format = COMPRESSED_ASTC_4x4_RGBA; diff --git a/src/utils.c b/src/utils.c index 10cce9b9..3cff472b 100644 --- a/src/utils.c +++ b/src/utils.c @@ -45,10 +45,10 @@ #include // Required for: Android assets manager: AAsset, AAssetManager_open(), ... #endif -#include // Required for: malloc(), free() -#include // Required for: fopen(), fclose(), fputc(), fwrite(), printf(), fprintf(), funopen() +#include // Required for: exit() +#include // Required for: printf(), sprintf() #include // Required for: va_list, va_start(), vfprintf(), va_end() -#include // Required for: strlen(), strrchr(), strcmp() +#include // Required for: strcpy(), strcat() #define MAX_TRACELOG_BUFFER_SIZE 128 // Max length of one trace-log message -- cgit v1.2.3 From b911cefab3f39506e22d517088cc9869f363e897 Mon Sep 17 00:00:00 2001 From: Reece Mackie <20544390+Rover656@users.noreply.github.com> Date: Sat, 27 Apr 2019 20:49:33 +0100 Subject: First gamepad stuff --- .../VS2017/examples/core_basic_window_cpp.vcxproj | 7 +- src/core.c | 126 +++++++++++++++++---- src/raylib.h | 51 +++++++++ 3 files changed, 161 insertions(+), 23 deletions(-) (limited to 'src/raylib.h') diff --git a/projects/VS2017/examples/core_basic_window_cpp.vcxproj b/projects/VS2017/examples/core_basic_window_cpp.vcxproj index cfe08524..bc8221a5 100644 --- a/projects/VS2017/examples/core_basic_window_cpp.vcxproj +++ b/projects/VS2017/examples/core_basic_window_cpp.vcxproj @@ -166,14 +166,15 @@ kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + {e89d61ac-55de-4482-afd4-df7242ebc859} + true - - - diff --git a/src/core.c b/src/core.c index 0d17fdad..448ad668 100644 --- a/src/core.c +++ b/src/core.c @@ -3050,6 +3050,86 @@ static bool GetMouseButtonStatus(int button) #endif } +static GamepadButton GetGamepadButton(int button) +{ + GamepadButton b = GAMEPAD_BUTTON_UNKNOWN; +#if defined(PLATFORM_DESKTOP) + switch (button) + { + case GLFW_GAMEPAD_BUTTON_Y: b = GAMEPAD_BUTTON_RIGHT_FACE_UP; break; + case GLFW_GAMEPAD_BUTTON_B: b = GAMEPAD_BUTTON_RIGHT_FACE_RIGHT; break; + case GLFW_GAMEPAD_BUTTON_A: b = GAMEPAD_BUTTON_RIGHT_FACE_DOWN; break; + case GLFW_GAMEPAD_BUTTON_X: b = GAMEPAD_BUTTON_RIGHT_FACE_LEFT; break; + case GLFW_GAMEPAD_BUTTON_LEFT_BUMPER: b = GAMEPAD_BUTTON_LEFT_TRIGGER_1; break; + case GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER: b = GAMEPAD_BUTTON_RIGHT_TRIGGER_1; break; + case GLFW_GAMEPAD_BUTTON_BACK: b = GAMEPAD_BUTTON_MIDDLE_LEFT; break; + case GLFW_GAMEPAD_BUTTON_GUIDE: b = GAMEPAD_BUTTON_MIDDLE; break; + case GLFW_GAMEPAD_BUTTON_START: b = GAMEPAD_BUTTON_MIDDLE_RIGHT; break; + case GLFW_GAMEPAD_BUTTON_DPAD_UP: b = GAMEPAD_BUTTON_LEFT_FACE_UP; break; + case GLFW_GAMEPAD_BUTTON_DPAD_RIGHT: b = GAMEPAD_BUTTON_LEFT_FACE_RIGHT; break; + case GLFW_GAMEPAD_BUTTON_DPAD_DOWN: b = GAMEPAD_BUTTON_LEFT_FACE_DOWN; break; + case GLFW_GAMEPAD_BUTTON_DPAD_LEFT: b = GAMEPAD_BUTTON_LEFT_FACE_LEFT; break; + case GLFW_GAMEPAD_BUTTON_LEFT_THUMB: b = GAMEPAD_BUTTON_LEFT_THUMB; break; + case GLFW_GAMEPAD_BUTTON_RIGHT_THUMB: b = GAMEPAD_BUTTON_RIGHT_THUMB; break; + } +#endif + +#if defined(PLATFORM_WEB) + //TODO: TEST + //https://www.w3.org/TR/gamepad/#gamepad-interface + switch (button) + { + case 0: b = GAMEPAD_BUTTON_RIGHT_FACE_DOWN; break; + case 1: b = GAMEPAD_BUTTON_RIGHT_FACE_RIGHT; break; + case 2: b = GAMEPAD_BUTTON_RIGHT_FACE_LEFT; break; + case 3: b = GAMEPAD_BUTTON_RIGHT_FACE_UP; break; + case 4: b = GAMEPAD_BUTTON_LEFT_TRIGGER_1; break; + case 5: b = GAMEPAD_BUTTON_RIGHT_TRIGGER_1; break; + case 6: b = GAMEPAD_BUTTON_LEFT_TRIGGER_2; break; + case 7: b = GAMEPAD_BUTTON_RIGHT_TRIGGER_2; break; + case 8: b = GAMEPAD_BUTTON_MIDDLE_LEFT; break; + case 9: b = GAMEPAD_BUTTON_MIDDLE_RIGHT; break; + case 10: b = GAMEPAD_BUTTON_LEFT_THUMB; break; + case 11: b = GAMEPAD_BUTTON_RIGHT_THUMB; break; + case 12: b = GAMEPAD_BUTTON_LEFT_FACE_UP; break; + case 13: b = GAMEPAD_BUTTON_LEFT_FACE_DOWN; break; + case 14: b = GAMEPAD_BUTTON_LEFT_FACE_LEFT; break; + case 15: b = GAMEPAD_BUTTON_LEFT_FACE_RIGHT; break; + } +#endif + + return b; +} + +static GamepadAxis GetGamepadAxis(int axis) +{ + GamepadAxis a = GAMEPAD_AXIS_UNKNOWN; +#if defined(PLATFORM_DESKTOP) + switch(axis) + { + case GLFW_GAMEPAD_AXIS_LEFT_X: a = GAMEPAD_AXIS_LEFT_X; break; + case GLFW_GAMEPAD_AXIS_LEFT_Y: a = GAMEPAD_AXIS_LEFT_Y; break; + case GLFW_GAMEPAD_AXIS_RIGHT_X: a = GAMEPAD_AXIS_RIGHT_X; break; + case GLFW_GAMEPAD_AXIS_RIGHT_Y: a = GAMEPAD_AXIS_RIGHT_Y; break; + case GLFW_GAMEPAD_AXIS_LEFT_TRIGGER: a = GAMEPAD_AXIS_LEFT_TRIGGER; break; + case GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER: a = GAMEPAD_AXIS_RIGHT_TRIGGER; break; + } +#endif + +#if defined(PLATFORM_WEB) + //TODO: TEST + switch(axis) + { + case 0: a = GAMEPAD_AXIS_LEFT_X; + case 1: a = GAMEPAD_AXIS_LEFT_Y; + case 2: a = GAMEPAD_AXIS_RIGHT_X; + case 3: a = GAMEPAD_AXIS_RIGHT_X; + } +#endif + + return a; +} + // Poll (store) all input events static void PollInputEvents(void) { @@ -3114,7 +3194,7 @@ static void PollInputEvents(void) #if defined(PLATFORM_DESKTOP) // Check if gamepads are ready - // NOTE: We do it here in case of disconection + // NOTE: We do it here in case of disconnection for (int i = 0; i < MAX_GAMEPADS; i++) { if (glfwJoystickPresent(i)) gamepadReady[i] = true; @@ -3131,33 +3211,37 @@ static void PollInputEvents(void) // Get current gamepad state // NOTE: There is no callback available, so we get it manually - const unsigned char *buttons; - int buttonsCount; - - buttons = glfwGetJoystickButtons(i, &buttonsCount); + //Get remapped buttons + GLFWgamepadstate state; + glfwGetGamepadState(i, &state); //This remapps all gamepads so they work the same + const unsigned char *buttons = state.buttons; - for (int k = 0; (buttons != NULL) && (k < buttonsCount) && (buttonsCount < MAX_GAMEPAD_BUTTONS); k++) + for (int k = 0; (buttons != NULL) && (k < GLFW_GAMEPAD_BUTTON_DPAD_LEFT + 1) && (k < MAX_GAMEPAD_BUTTONS); k++) { + const GamepadButton button = GetGamepadButton(k); + if (buttons[k] == GLFW_PRESS) { - currentGamepadState[i][k] = 1; - lastGamepadButtonPressed = k; + currentGamepadState[i][button] = 1; + lastGamepadButtonPressed = button; } - else currentGamepadState[i][k] = 0; + else currentGamepadState[i][button] = 0; } // Get current axis state - const float *axes; - int axisCount = 0; - - axes = glfwGetJoystickAxes(i, &axisCount); + const float *axes = state.axes; - for (int k = 0; (axes != NULL) && (k < axisCount) && (k < MAX_GAMEPAD_AXIS); k++) + for (int k = 0; (axes != NULL) && (k < GLFW_GAMEPAD_AXIS_LAST + 1) && (k < MAX_GAMEPAD_AXIS); k++) { - gamepadAxisState[i][k] = axes[k]; + const GamepadAxis axis = GetGamepadAxis(k); + gamepadAxisState[i][axis] = axes[k]; } - gamepadAxisCount = axisCount; + //Register buttons for 2nd triggers + currentGamepadState[i][GAMEPAD_BUTTON_LEFT_TRIGGER_2] = (char)(gamepadAxisState[i][GAMEPAD_AXIS_LEFT_TRIGGER] > 0.1); + currentGamepadState[i][GAMEPAD_BUTTON_RIGHT_TRIGGER_2] = (char)(gamepadAxisState[i][GAMEPAD_AXIS_RIGHT_TRIGGER] > 0.1); + + gamepadAxisCount = GLFW_GAMEPAD_AXIS_LAST; } } @@ -3191,12 +3275,13 @@ static void PollInputEvents(void) // Register buttons data for every connected gamepad for (int j = 0; (j < gamepadState.numButtons) && (j < MAX_GAMEPAD_BUTTONS); j++) { + const GamepadButton button = GetGamepadButton(j); if (gamepadState.digitalButton[j] == 1) { - currentGamepadState[i][j] = 1; - lastGamepadButtonPressed = j; + currentGamepadState[i][button] = 1; + lastGamepadButtonPressed = button; } - else currentGamepadState[i][j] = 0; + else currentGamepadState[i][button] = 0; //TraceLog(LOG_DEBUG, "Gamepad %d, button %d: Digital: %d, Analog: %g", gamepadState.index, j, gamepadState.digitalButton[j], gamepadState.analogButton[j]); } @@ -3204,7 +3289,8 @@ static void PollInputEvents(void) // Register axis data for every connected gamepad for (int j = 0; (j < gamepadState.numAxes) && (j < MAX_GAMEPAD_AXIS); j++) { - gamepadAxisState[i][j] = gamepadState.axis[j]; + const GamepadAxis axis = GetGamepadAxis(k); + gamepadAxisState[i][axis] = gamepadState.axis[j]; } gamepadAxisCount = gamepadState.numAxes; diff --git a/src/raylib.h b/src/raylib.h index 43260e06..9759000f 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -619,6 +619,57 @@ typedef enum { GAMEPAD_PLAYER4 = 3 } GamepadNumber; +// Gamepad Buttons +typedef enum +{ + //This is here just for error checking + GAMEPAD_BUTTON_UNKNOWN = 0, + + //This is normally ABXY/Circle, Triangle, Square, Cross. No support for 6 button controllers though.. + GAMEPAD_BUTTON_LEFT_FACE_UP, + GAMEPAD_BUTTON_LEFT_FACE_RIGHT, + GAMEPAD_BUTTON_LEFT_FACE_DOWN, + GAMEPAD_BUTTON_LEFT_FACE_LEFT, + + + //This is normally a DPAD + GAMEPAD_BUTTON_RIGHT_FACE_UP, + GAMEPAD_BUTTON_RIGHT_FACE_RIGHT, + GAMEPAD_BUTTON_RIGHT_FACE_DOWN, + GAMEPAD_BUTTON_RIGHT_FACE_LEFT, + + //Triggers + GAMEPAD_BUTTON_LEFT_TRIGGER_1, + GAMEPAD_BUTTON_LEFT_TRIGGER_2, + GAMEPAD_BUTTON_RIGHT_TRIGGER_1, + GAMEPAD_BUTTON_RIGHT_TRIGGER_2, + + //These are buttons in the center of the gamepad + GAMEPAD_BUTTON_MIDDLE_LEFT, //PS3 Select + GAMEPAD_BUTTON_MIDDLE, //PS Button/XBOX Button + GAMEPAD_BUTTON_MIDDLE_RIGHT, //PS3 Start + + //These are the joystick press in buttons + GAMEPAD_BUTTON_LEFT_THUMB, + GAMEPAD_BUTTON_RIGHT_THUMB +} GamepadButton; + +typedef enum +{ + GAMEPAD_AXIS_UNKNOWN = 0, + //Left stick + GAMEPAD_AXIS_LEFT_X, + GAMEPAD_AXIS_LEFT_Y, + + //Right stick + GAMEPAD_AXIS_RIGHT_X, + GAMEPAD_AXIS_RIGHT_Y, + + //Pressure levels + GAMEPAD_AXIS_LEFT_TRIGGER, // [1..-1] (pressure-level) + GAMEPAD_AXIS_RIGHT_TRIGGER // [1..-1] (pressure-level) +} GamepadAxis; + // PS3 USB Controller Buttons // TODO: Provide a generic way to list gamepad controls schemes, // defining specific controls schemes is not a good option -- cgit v1.2.3 From 56ded3259ded9c5fc8d5c7dff1a50044326f5ab5 Mon Sep 17 00:00:00 2001 From: Reece Mackie <20544390+Rover656@users.noreply.github.com> Date: Sat, 27 Apr 2019 21:36:57 +0100 Subject: More work, UWP now supports it and deleted old gamepads --- projects/VS2017.UWP/raylib.App.UWP/App.cpp | 6 +- projects/VS2017.UWP/raylib.App.UWP/BaseApp.h | 41 ++++++------ .../VS2017/examples/core_basic_window_cpp.vcxproj | 7 +- src/core.c | 38 +++++++++++ src/raylib.h | 78 ---------------------- 5 files changed, 66 insertions(+), 104 deletions(-) (limited to 'src/raylib.h') diff --git a/projects/VS2017.UWP/raylib.App.UWP/App.cpp b/projects/VS2017.UWP/raylib.App.UWP/App.cpp index ae137ed2..fb07636c 100644 --- a/projects/VS2017.UWP/raylib.App.UWP/App.cpp +++ b/projects/VS2017.UWP/raylib.App.UWP/App.cpp @@ -36,8 +36,8 @@ void App::Update() ClearBackground(RAYWHITE); - posX += GetGamepadAxisMovement(GAMEPAD_PLAYER1, GAMEPAD_XBOX_AXIS_LEFT_X) * 5; - posY += GetGamepadAxisMovement(GAMEPAD_PLAYER1, GAMEPAD_XBOX_AXIS_LEFT_Y) * -5; + posX += GetGamepadAxisMovement(GAMEPAD_PLAYER1, GAMEPAD_AXIS_LEFT_X) * 5; + posY += GetGamepadAxisMovement(GAMEPAD_PLAYER1, GAMEPAD_AXIS_LEFT_Y) * -5; DrawRectangle(posX, posY, 400, 100, RED); @@ -63,7 +63,7 @@ void App::Update() if (IsKeyDown(KEY_LEFT_ALT)) DrawRectangle(250, 250, 20, 20, BLACK); - if (IsKeyDown(KEY_BACKSPACE)) + if (IsKeyDown(KEY_BACKSPACE) || IsGamepadButtonDown(GAMEPAD_PLAYER1, GAMEPAD_BUTTON_RIGHT_FACE_DOWN)) DrawRectangle(280, 250, 20, 20, BLACK); if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) DrawRectangle(280, 250, 20, 20, BLACK); diff --git a/projects/VS2017.UWP/raylib.App.UWP/BaseApp.h b/projects/VS2017.UWP/raylib.App.UWP/BaseApp.h index e27909e2..65483754 100644 --- a/projects/VS2017.UWP/raylib.App.UWP/BaseApp.h +++ b/projects/VS2017.UWP/raylib.App.UWP/BaseApp.h @@ -371,27 +371,30 @@ protected: GamepadReading reading = gamepad->GetCurrentReading(); // NOTE: Maybe it would be wiser to redefine the gamepad button mappings in "raylib.h" for the UWP platform instead of remapping them manually - RegisterGamepadButton(i, GAMEPAD_XBOX_BUTTON_A, ((reading.Buttons & GamepadButtons::A) == GamepadButtons::A)); - RegisterGamepadButton(i, GAMEPAD_XBOX_BUTTON_B, ((reading.Buttons & GamepadButtons::B) == GamepadButtons::B)); - RegisterGamepadButton(i, GAMEPAD_XBOX_BUTTON_X, ((reading.Buttons & GamepadButtons::X) == GamepadButtons::X)); - RegisterGamepadButton(i, GAMEPAD_XBOX_BUTTON_Y, ((reading.Buttons & GamepadButtons::Y) == GamepadButtons::Y)); - RegisterGamepadButton(i, GAMEPAD_XBOX_BUTTON_LB, ((reading.Buttons & GamepadButtons::LeftShoulder) == GamepadButtons::LeftShoulder)); - RegisterGamepadButton(i, GAMEPAD_XBOX_BUTTON_RB, ((reading.Buttons & GamepadButtons::RightShoulder) == GamepadButtons::RightShoulder)); - RegisterGamepadButton(i, GAMEPAD_XBOX_BUTTON_SELECT, ((reading.Buttons & GamepadButtons::View) == GamepadButtons::View)); // Changed for XB1 Controller - RegisterGamepadButton(i, GAMEPAD_XBOX_BUTTON_START, ((reading.Buttons & GamepadButtons::Menu) == GamepadButtons::Menu)); // Changed for XB1 Controller - RegisterGamepadButton(i, GAMEPAD_XBOX_BUTTON_UP, ((reading.Buttons & GamepadButtons::DPadUp) == GamepadButtons::DPadUp)); - RegisterGamepadButton(i, GAMEPAD_XBOX_BUTTON_RIGHT, ((reading.Buttons & GamepadButtons::DPadRight) == GamepadButtons::DPadRight)); - RegisterGamepadButton(i, GAMEPAD_XBOX_BUTTON_DOWN, ((reading.Buttons & GamepadButtons::DPadDown) == GamepadButtons::DPadDown)); - RegisterGamepadButton(i, GAMEPAD_XBOX_BUTTON_LEFT, ((reading.Buttons & GamepadButtons::DPadLeft) == GamepadButtons::DPadLeft)); - RegisterGamepadButton(i, GAMEPAD_XBOX_BUTTON_HOME, false); // Home button not supported by UWP + RegisterGamepadButton(i, GAMEPAD_BUTTON_RIGHT_FACE_DOWN, ((reading.Buttons & GamepadButtons::A) == GamepadButtons::A)); + RegisterGamepadButton(i, GAMEPAD_BUTTON_RIGHT_FACE_RIGHT, ((reading.Buttons & GamepadButtons::B) == GamepadButtons::B)); + RegisterGamepadButton(i, GAMEPAD_BUTTON_RIGHT_FACE_LEFT, ((reading.Buttons & GamepadButtons::X) == GamepadButtons::X)); + RegisterGamepadButton(i, GAMEPAD_BUTTON_RIGHT_FACE_UP, ((reading.Buttons & GamepadButtons::Y) == GamepadButtons::Y)); + + RegisterGamepadButton(i, GAMEPAD_BUTTON_LEFT_TRIGGER_1, ((reading.Buttons & GamepadButtons::LeftShoulder) == GamepadButtons::LeftShoulder)); + RegisterGamepadButton(i, GAMEPAD_BUTTON_RIGHT_TRIGGER_1, ((reading.Buttons & GamepadButtons::RightShoulder) == GamepadButtons::RightShoulder)); + + RegisterGamepadButton(i, GAMEPAD_BUTTON_MIDDLE_LEFT, ((reading.Buttons & GamepadButtons::View) == GamepadButtons::View)); // Changed for XB1 Controller + RegisterGamepadButton(i, GAMEPAD_BUTTON_MIDDLE_RIGHT, ((reading.Buttons & GamepadButtons::Menu) == GamepadButtons::Menu)); // Changed for XB1 Controller + + RegisterGamepadButton(i, GAMEPAD_BUTTON_LEFT_FACE_UP, ((reading.Buttons & GamepadButtons::DPadUp) == GamepadButtons::DPadUp)); + RegisterGamepadButton(i, GAMEPAD_BUTTON_LEFT_FACE_RIGHT, ((reading.Buttons & GamepadButtons::DPadRight) == GamepadButtons::DPadRight)); + RegisterGamepadButton(i, GAMEPAD_BUTTON_LEFT_FACE_DOWN, ((reading.Buttons & GamepadButtons::DPadDown) == GamepadButtons::DPadDown)); + RegisterGamepadButton(i, GAMEPAD_BUTTON_LEFT_FACE_LEFT, ((reading.Buttons & GamepadButtons::DPadLeft) == GamepadButtons::DPadLeft)); + RegisterGamepadButton(i, GAMEPAD_BUTTON_MIDDLE, false); // Home button not supported by UWP // Get current axis state - RegisterGamepadAxis(i, GAMEPAD_XBOX_AXIS_LEFT_X, (float)reading.LeftThumbstickX); - RegisterGamepadAxis(i, GAMEPAD_XBOX_AXIS_LEFT_Y, (float)reading.LeftThumbstickY); - RegisterGamepadAxis(i, GAMEPAD_XBOX_AXIS_RIGHT_X, (float)reading.RightThumbstickX); - RegisterGamepadAxis(i, GAMEPAD_XBOX_AXIS_RIGHT_Y, (float)reading.RightThumbstickY); - RegisterGamepadAxis(i, GAMEPAD_XBOX_AXIS_LT, (float)reading.LeftTrigger); - RegisterGamepadAxis(i, GAMEPAD_XBOX_AXIS_RT, (float)reading.RightTrigger); + RegisterGamepadAxis(i, GAMEPAD_AXIS_LEFT_X, (float)reading.LeftThumbstickX); + RegisterGamepadAxis(i, GAMEPAD_AXIS_LEFT_Y, (float)reading.LeftThumbstickY); + RegisterGamepadAxis(i, GAMEPAD_AXIS_RIGHT_X, (float)reading.RightThumbstickX); + RegisterGamepadAxis(i, GAMEPAD_AXIS_RIGHT_Y, (float)reading.RightThumbstickY); + RegisterGamepadAxis(i, GAMEPAD_AXIS_LEFT_TRIGGER, (float)reading.LeftTrigger); + RegisterGamepadAxis(i, GAMEPAD_AXIS_RIGHT_TRIGGER, (float)reading.RightTrigger); } } } diff --git a/projects/VS2017/examples/core_basic_window_cpp.vcxproj b/projects/VS2017/examples/core_basic_window_cpp.vcxproj index bc8221a5..cfe08524 100644 --- a/projects/VS2017/examples/core_basic_window_cpp.vcxproj +++ b/projects/VS2017/examples/core_basic_window_cpp.vcxproj @@ -166,15 +166,14 @@ kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) - - - {e89d61ac-55de-4482-afd4-df7242ebc859} - true + + + diff --git a/src/core.c b/src/core.c index 7977d0ad..2aa22fa6 100644 --- a/src/core.c +++ b/src/core.c @@ -3095,20 +3095,50 @@ static GamepadButton GetGamepadButton(int button) case GLFW_GAMEPAD_BUTTON_B: b = GAMEPAD_BUTTON_RIGHT_FACE_RIGHT; break; case GLFW_GAMEPAD_BUTTON_A: b = GAMEPAD_BUTTON_RIGHT_FACE_DOWN; break; case GLFW_GAMEPAD_BUTTON_X: b = GAMEPAD_BUTTON_RIGHT_FACE_LEFT; break; + case GLFW_GAMEPAD_BUTTON_LEFT_BUMPER: b = GAMEPAD_BUTTON_LEFT_TRIGGER_1; break; case GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER: b = GAMEPAD_BUTTON_RIGHT_TRIGGER_1; break; + case GLFW_GAMEPAD_BUTTON_BACK: b = GAMEPAD_BUTTON_MIDDLE_LEFT; break; case GLFW_GAMEPAD_BUTTON_GUIDE: b = GAMEPAD_BUTTON_MIDDLE; break; case GLFW_GAMEPAD_BUTTON_START: b = GAMEPAD_BUTTON_MIDDLE_RIGHT; break; + case GLFW_GAMEPAD_BUTTON_DPAD_UP: b = GAMEPAD_BUTTON_LEFT_FACE_UP; break; case GLFW_GAMEPAD_BUTTON_DPAD_RIGHT: b = GAMEPAD_BUTTON_LEFT_FACE_RIGHT; break; case GLFW_GAMEPAD_BUTTON_DPAD_DOWN: b = GAMEPAD_BUTTON_LEFT_FACE_DOWN; break; case GLFW_GAMEPAD_BUTTON_DPAD_LEFT: b = GAMEPAD_BUTTON_LEFT_FACE_LEFT; break; + case GLFW_GAMEPAD_BUTTON_LEFT_THUMB: b = GAMEPAD_BUTTON_LEFT_THUMB; break; case GLFW_GAMEPAD_BUTTON_RIGHT_THUMB: b = GAMEPAD_BUTTON_RIGHT_THUMB; break; } #endif +#if defined(PLATFORM_UWP) + /*switch(button) + { + case 4: b = GAMEPAD_BUTTON_RIGHT_FACE_DOWN; break; + case 8: b = GAMEPAD_BUTTON_RIGHT_FACE_RIGHT; break; + case 16: b = GAMEPAD_BUTTON_RIGHT_FACE_LEFT; break; + case 32: b = GAMEPAD_BUTTON_RIGHT_FACE_RIGHT; break; + + case 128: b = GAMEPAD_BUTTON_LEFT_FACE_DOWN; break; + case 256: b = GAMEPAD_BUTTON_LEFT_FACE_LEFT; break; + case 512: b = GAMEPAD_BUTTON_LEFT_FACE_RIGHT; break; + case 64: b = GAMEPAD_BUTTON_LEFT_FACE_UP; break; + + case 1024: b = GAMEPAD_BUTTON_LEFT_TRIGGER_1; break; + case 2048: b = GAMEPAD_BUTTON_RIGHT_TRIGGER_1; break; + + case 4096: b = GAMEPAD_BUTTON_LEFT_THUMB; break; + case 8192: b = GAMEPAD_BUTTON_RIGHT_THUMB; break; + + case 2: b = GAMEPAD_BUTTON_MIDDLE_LEFT; + case 1: b = GAMEPAD_BUTTON_MIDDLE_RIGHT; + }*/ + //Above might not be most efficient, so not doing it for now + b = button; +#endif + #if defined(PLATFORM_WEB) //TODO: TEST //https://www.w3.org/TR/gamepad/#gamepad-interface @@ -3151,6 +3181,10 @@ static GamepadAxis GetGamepadAxis(int axis) } #endif +#if defined(PLATFORM_UWP) + a = axis; //UWP will provide the correct axis +#endif + #if defined(PLATFORM_WEB) //TODO: TEST switch(axis) @@ -3347,6 +3381,10 @@ static void PollInputEvents(void) { if (msg->Int0 < MAX_GAMEPADS && msg->Int1 < MAX_GAMEPAD_AXIS) gamepadAxisState[msg->Int0][msg->Int1] = msg->Float0; + + //Register buttons for 2nd triggers + currentGamepadState[msg->Int0][GAMEPAD_BUTTON_LEFT_TRIGGER_2] = (char)(gamepadAxisState[msg->Int0][GAMEPAD_AXIS_LEFT_TRIGGER] > 0.1); + currentGamepadState[msg->Int0][GAMEPAD_BUTTON_RIGHT_TRIGGER_2] = (char)(gamepadAxisState[msg->Int0][GAMEPAD_AXIS_RIGHT_TRIGGER] > 0.1); break; } diff --git a/src/raylib.h b/src/raylib.h index 9759000f..1b4a5434 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -670,84 +670,6 @@ typedef enum GAMEPAD_AXIS_RIGHT_TRIGGER // [1..-1] (pressure-level) } GamepadAxis; -// PS3 USB Controller Buttons -// TODO: Provide a generic way to list gamepad controls schemes, -// defining specific controls schemes is not a good option -typedef enum { - GAMEPAD_PS3_BUTTON_TRIANGLE = 0, - GAMEPAD_PS3_BUTTON_CIRCLE = 1, - GAMEPAD_PS3_BUTTON_CROSS = 2, - GAMEPAD_PS3_BUTTON_SQUARE = 3, - GAMEPAD_PS3_BUTTON_L1 = 6, - GAMEPAD_PS3_BUTTON_R1 = 7, - GAMEPAD_PS3_BUTTON_L2 = 4, - GAMEPAD_PS3_BUTTON_R2 = 5, - GAMEPAD_PS3_BUTTON_START = 8, - GAMEPAD_PS3_BUTTON_SELECT = 9, - GAMEPAD_PS3_BUTTON_PS = 12, - GAMEPAD_PS3_BUTTON_UP = 24, - GAMEPAD_PS3_BUTTON_RIGHT = 25, - GAMEPAD_PS3_BUTTON_DOWN = 26, - GAMEPAD_PS3_BUTTON_LEFT = 27 -} GamepadPS3Button; - -// PS3 USB Controller Axis -typedef enum { - GAMEPAD_PS3_AXIS_LEFT_X = 0, - GAMEPAD_PS3_AXIS_LEFT_Y = 1, - GAMEPAD_PS3_AXIS_RIGHT_X = 2, - GAMEPAD_PS3_AXIS_RIGHT_Y = 5, - GAMEPAD_PS3_AXIS_L2 = 3, // [1..-1] (pressure-level) - GAMEPAD_PS3_AXIS_R2 = 4 // [1..-1] (pressure-level) -} GamepadPS3Axis; - -// Xbox360 USB Controller Buttons -typedef enum { - GAMEPAD_XBOX_BUTTON_A = 0, - GAMEPAD_XBOX_BUTTON_B = 1, - GAMEPAD_XBOX_BUTTON_X = 2, - GAMEPAD_XBOX_BUTTON_Y = 3, - GAMEPAD_XBOX_BUTTON_LB = 4, - GAMEPAD_XBOX_BUTTON_RB = 5, - GAMEPAD_XBOX_BUTTON_SELECT = 6, - GAMEPAD_XBOX_BUTTON_START = 7, - GAMEPAD_XBOX_BUTTON_HOME = 8, - GAMEPAD_XBOX_BUTTON_UP = 10, - GAMEPAD_XBOX_BUTTON_RIGHT = 11, - GAMEPAD_XBOX_BUTTON_DOWN = 12, - GAMEPAD_XBOX_BUTTON_LEFT = 13 -} GamepadXbox360Button; - -// Xbox360 USB Controller Axis, -// NOTE: For Raspberry Pi, axis must be reconfigured -typedef enum { - GAMEPAD_XBOX_AXIS_LEFT_X = 0, // [-1..1] (left->right) - GAMEPAD_XBOX_AXIS_LEFT_Y = 1, // [1..-1] (up->down) - GAMEPAD_XBOX_AXIS_RIGHT_X = 2, // [-1..1] (left->right) - GAMEPAD_XBOX_AXIS_RIGHT_Y = 3, // [1..-1] (up->down) - GAMEPAD_XBOX_AXIS_LT = 4, // [-1..1] (pressure-level) - GAMEPAD_XBOX_AXIS_RT = 5 // [-1..1] (pressure-level) -} GamepadXbox360Axis; - -// Android Gamepad Controller (SNES CLASSIC) -typedef enum { - GAMEPAD_ANDROID_DPAD_UP = 19, - GAMEPAD_ANDROID_DPAD_DOWN = 20, - GAMEPAD_ANDROID_DPAD_LEFT = 21, - GAMEPAD_ANDROID_DPAD_RIGHT = 22, - GAMEPAD_ANDROID_DPAD_CENTER = 23, - GAMEPAD_ANDROID_BUTTON_A = 96, - GAMEPAD_ANDROID_BUTTON_B = 97, - GAMEPAD_ANDROID_BUTTON_C = 98, - GAMEPAD_ANDROID_BUTTON_X = 99, - GAMEPAD_ANDROID_BUTTON_Y = 100, - GAMEPAD_ANDROID_BUTTON_Z = 101, - GAMEPAD_ANDROID_BUTTON_L1 = 102, - GAMEPAD_ANDROID_BUTTON_R1 = 103, - GAMEPAD_ANDROID_BUTTON_L2 = 104, - GAMEPAD_ANDROID_BUTTON_R2 = 105 -} GamepadAndroid; - // Shader location point type typedef enum { LOC_VERTEX_POSITION = 0, -- cgit v1.2.3 From c1f33eb817765ad801b2419d86457a6102b64032 Mon Sep 17 00:00:00 2001 From: Reece Mackie <20544390+Rover656@users.noreply.github.com> Date: Sat, 27 Apr 2019 21:43:32 +0100 Subject: Line cleaning --- src/raylib.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/raylib.h') diff --git a/src/raylib.h b/src/raylib.h index 1b4a5434..b8347ef4 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -631,7 +631,6 @@ typedef enum GAMEPAD_BUTTON_LEFT_FACE_DOWN, GAMEPAD_BUTTON_LEFT_FACE_LEFT, - //This is normally a DPAD GAMEPAD_BUTTON_RIGHT_FACE_UP, GAMEPAD_BUTTON_RIGHT_FACE_RIGHT, @@ -657,6 +656,7 @@ typedef enum typedef enum { GAMEPAD_AXIS_UNKNOWN = 0, + //Left stick GAMEPAD_AXIS_LEFT_X, GAMEPAD_AXIS_LEFT_Y, -- cgit v1.2.3 From 604a8c0b78b6f51abcce61aa0c3db25412acafa7 Mon Sep 17 00:00:00 2001 From: raysan5 Date: Sun, 28 Apr 2019 14:45:46 +0200 Subject: WARNING: Functions renamed Two functions have been renamed for coherence; previous naming was confusing for several users: - DrawPolyEx() ---> DrawTriangleFan() - DrawPolyExLines() ---> DrawLineStrip() --- src/raylib.h | 4 +-- src/shapes.c | 99 ++++++++++++++++++++++++++++++------------------------------ 2 files changed, 52 insertions(+), 51 deletions(-) (limited to 'src/raylib.h') diff --git a/src/raylib.h b/src/raylib.h index 43260e06..c1cebdc8 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1058,6 +1058,7 @@ RLAPI void DrawLine(int startPosX, int startPosY, int endPosX, int endPosY, Colo RLAPI void DrawLineV(Vector2 startPos, Vector2 endPos, Color color); // Draw a line (Vector version) 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 DrawLineStrip(Vector2 *points, int numPoints, Color color); // Draw lines sequence 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 @@ -1079,9 +1080,8 @@ RLAPI void DrawRectangleRounded(Rectangle rec, float roundness, int segments, Co 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 DrawTriangleFan(Vector2 *points, int numPoints, Color color); // Draw a triangle fan defined by points RLAPI void DrawPoly(Vector2 center, int sides, float radius, float rotation, Color color); // Draw a regular polygon (Vector version) -RLAPI void DrawPolyEx(Vector2 *points, int numPoints, Color color); // Draw a closed polygon defined by points -RLAPI void DrawPolyExLines(Vector2 *points, int numPoints, Color color); // Draw polygon lines RLAPI void SetShapesTexture(Texture2D texture, Rectangle source); // Define default texture used to draw shapes diff --git a/src/shapes.c b/src/shapes.c index ecef287c..93b332f5 100644 --- a/src/shapes.c +++ b/src/shapes.c @@ -176,6 +176,25 @@ void DrawLineBezier(Vector2 startPos, Vector2 endPos, float thick, Color color) } } +// Draw lines sequence +void DrawLineStrip(Vector2 *points, int pointsCount, Color color) +{ + if (pointsCount >= 2) + { + if (rlCheckBufferLimit(pointsCount)) rlglDraw(); + + rlBegin(RL_LINES); + rlColor4ub(color.r, color.g, color.b, color.a); + + for (int i = 0; i < pointsCount - 1; i++) + { + rlVertex2f(points[i].x, points[i].y); + rlVertex2f(points[i + 1].x, points[i + 1].y); + } + rlEnd(); + } +} + // Draw a color-filled circle void DrawCircle(int centerX, int centerY, float radius, Color color) { @@ -1208,6 +1227,37 @@ void DrawTriangleLines(Vector2 v1, Vector2 v2, Vector2 v3, Color color) rlEnd(); } +// Draw a triangle fan defined by points +// NOTE: First point provided is shared by all triangles +void DrawTriangleFan(Vector2 *points, int pointsCount, Color color) +{ + if (pointsCount >= 3) + { + if (rlCheckBufferLimit((pointsCount - 2)*4)) rlglDraw(); + + rlEnableTexture(GetShapesTexture().id); + rlBegin(RL_QUADS); + rlColor4ub(color.r, color.g, color.b, color.a); + + for (int i = 1; i < pointsCount - 1; i++) + { + rlTexCoord2f(recTexShapes.x/texShapes.width, recTexShapes.y/texShapes.height); + rlVertex2f(points[0].x, points[0].y); + + rlTexCoord2f(recTexShapes.x/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height); + rlVertex2f(points[i].x, points[i].y); + + rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height); + rlVertex2f(points[i + 1].x, points[i + 1].y); + + rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, recTexShapes.y/texShapes.height); + rlVertex2f(points[i + 1].x, points[i + 1].y); + } + rlEnd(); + rlDisableTexture(); + } +} + // Draw a regular polygon of n sides (Vector version) void DrawPoly(Vector2 center, int sides, float radius, float rotation, Color color) { @@ -1256,55 +1306,6 @@ void DrawPoly(Vector2 center, int sides, float radius, float rotation, Color col rlPopMatrix(); } -// Draw a closed polygon defined by points -void DrawPolyEx(Vector2 *points, int pointsCount, Color color) -{ - if (pointsCount >= 3) - { - if (rlCheckBufferLimit((pointsCount - 2)*4)) rlglDraw(); - - rlEnableTexture(GetShapesTexture().id); - rlBegin(RL_QUADS); - rlColor4ub(color.r, color.g, color.b, color.a); - - for (int i = 1; i < pointsCount - 1; i++) - { - rlTexCoord2f(recTexShapes.x/texShapes.width, recTexShapes.y/texShapes.height); - rlVertex2f(points[0].x, points[0].y); - - rlTexCoord2f(recTexShapes.x/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height); - rlVertex2f(points[i].x, points[i].y); - - rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height); - rlVertex2f(points[i + 1].x, points[i + 1].y); - - rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, recTexShapes.y/texShapes.height); - rlVertex2f(points[i + 1].x, points[i + 1].y); - } - rlEnd(); - rlDisableTexture(); - } -} - -// Draw polygon using lines -void DrawPolyExLines(Vector2 *points, int pointsCount, Color color) -{ - if (pointsCount >= 2) - { - if (rlCheckBufferLimit(pointsCount)) rlglDraw(); - - rlBegin(RL_LINES); - rlColor4ub(color.r, color.g, color.b, color.a); - - for (int i = 0; i < pointsCount - 1; i++) - { - rlVertex2f(points[i].x, points[i].y); - rlVertex2f(points[i + 1].x, points[i + 1].y); - } - rlEnd(); - } -} - // Define default texture used to draw shapes void SetShapesTexture(Texture2D texture, Rectangle source) { -- cgit v1.2.3 From 7ca856f9b76e7e9eac49120d145f89c2fc639c38 Mon Sep 17 00:00:00 2001 From: Reece Mackie <20544390+Rover656@users.noreply.github.com> Date: Sun, 28 Apr 2019 15:59:39 +0100 Subject: Formatting changes --- src/core.c | 35 ++++++----------------------------- src/raylib.h | 19 ++++++++++--------- 2 files changed, 16 insertions(+), 38 deletions(-) (limited to 'src/raylib.h') diff --git a/src/core.c b/src/core.c index ec7586df..4bb1e76f 100644 --- a/src/core.c +++ b/src/core.c @@ -3110,34 +3110,11 @@ static GamepadButton GetGamepadButton(int button) #endif #if defined(PLATFORM_UWP) - /*switch(button) - { - case 4: b = GAMEPAD_BUTTON_RIGHT_FACE_DOWN; break; - case 8: b = GAMEPAD_BUTTON_RIGHT_FACE_RIGHT; break; - case 16: b = GAMEPAD_BUTTON_RIGHT_FACE_LEFT; break; - case 32: b = GAMEPAD_BUTTON_RIGHT_FACE_RIGHT; break; - - case 128: b = GAMEPAD_BUTTON_LEFT_FACE_DOWN; break; - case 256: b = GAMEPAD_BUTTON_LEFT_FACE_LEFT; break; - case 512: b = GAMEPAD_BUTTON_LEFT_FACE_RIGHT; break; - case 64: b = GAMEPAD_BUTTON_LEFT_FACE_UP; break; - - case 1024: b = GAMEPAD_BUTTON_LEFT_TRIGGER_1; break; - case 2048: b = GAMEPAD_BUTTON_RIGHT_TRIGGER_1; break; - - case 4096: b = GAMEPAD_BUTTON_LEFT_THUMB; break; - case 8192: b = GAMEPAD_BUTTON_RIGHT_THUMB; break; - - case 2: b = GAMEPAD_BUTTON_MIDDLE_LEFT; - case 1: b = GAMEPAD_BUTTON_MIDDLE_RIGHT; - }*/ - //Above might not be most efficient, so not doing it for now b = button; #endif #if defined(PLATFORM_WEB) - //TODO: TEST - //https://www.w3.org/TR/gamepad/#gamepad-interface + // https://www.w3.org/TR/gamepad/#gamepad-interface switch (button) { case 0: b = GAMEPAD_BUTTON_RIGHT_FACE_DOWN; break; @@ -3178,11 +3155,11 @@ static GamepadAxis GetGamepadAxis(int axis) #endif #if defined(PLATFORM_UWP) - a = axis; //UWP will provide the correct axis + a = axis; // UWP will provide the correct axis #endif #if defined(PLATFORM_WEB) - //TODO: TEST + // https://www.w3.org/TR/gamepad/#gamepad-interface switch(axis) { case 0: a = GAMEPAD_AXIS_LEFT_X; @@ -3430,9 +3407,9 @@ static void PollInputEvents(void) // Get current gamepad state // NOTE: There is no callback available, so we get it manually - //Get remapped buttons + // Get remapped buttons GLFWgamepadstate state; - glfwGetGamepadState(i, &state); //This remapps all gamepads so they work the same + glfwGetGamepadState(i, &state); // This remapps all gamepads so they have their buttons mapped like an xbox controller const unsigned char *buttons = state.buttons; for (int k = 0; (buttons != NULL) && (k < GLFW_GAMEPAD_BUTTON_DPAD_LEFT + 1) && (k < MAX_GAMEPAD_BUTTONS); k++) @@ -3456,7 +3433,7 @@ static void PollInputEvents(void) gamepadAxisState[i][axis] = axes[k]; } - //Register buttons for 2nd triggers + // Register buttons for 2nd triggers (because GLFW doesn't count these as buttons but rather axis) currentGamepadState[i][GAMEPAD_BUTTON_LEFT_TRIGGER_2] = (char)(gamepadAxisState[i][GAMEPAD_AXIS_LEFT_TRIGGER] > 0.1); currentGamepadState[i][GAMEPAD_BUTTON_RIGHT_TRIGGER_2] = (char)(gamepadAxisState[i][GAMEPAD_AXIS_RIGHT_TRIGGER] > 0.1); diff --git a/src/raylib.h b/src/raylib.h index cf8543a2..3fb4ee6d 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -622,50 +622,51 @@ typedef enum { // Gamepad Buttons typedef enum { - //This is here just for error checking + // This is here just for error checking GAMEPAD_BUTTON_UNKNOWN = 0, - //This is normally ABXY/Circle, Triangle, Square, Cross. No support for 6 button controllers though.. + // This is normally ABXY/Circle, Triangle, Square, Cross. No support for 6 button controllers though.. GAMEPAD_BUTTON_LEFT_FACE_UP, GAMEPAD_BUTTON_LEFT_FACE_RIGHT, GAMEPAD_BUTTON_LEFT_FACE_DOWN, GAMEPAD_BUTTON_LEFT_FACE_LEFT, - //This is normally a DPAD + // This is normally a DPAD GAMEPAD_BUTTON_RIGHT_FACE_UP, GAMEPAD_BUTTON_RIGHT_FACE_RIGHT, GAMEPAD_BUTTON_RIGHT_FACE_DOWN, GAMEPAD_BUTTON_RIGHT_FACE_LEFT, - //Triggers + // Triggers GAMEPAD_BUTTON_LEFT_TRIGGER_1, GAMEPAD_BUTTON_LEFT_TRIGGER_2, GAMEPAD_BUTTON_RIGHT_TRIGGER_1, GAMEPAD_BUTTON_RIGHT_TRIGGER_2, - //These are buttons in the center of the gamepad + // These are buttons in the center of the gamepad GAMEPAD_BUTTON_MIDDLE_LEFT, //PS3 Select GAMEPAD_BUTTON_MIDDLE, //PS Button/XBOX Button GAMEPAD_BUTTON_MIDDLE_RIGHT, //PS3 Start - //These are the joystick press in buttons + // These are the joystick press in buttons GAMEPAD_BUTTON_LEFT_THUMB, GAMEPAD_BUTTON_RIGHT_THUMB } GamepadButton; typedef enum { + // This is here just for error checking GAMEPAD_AXIS_UNKNOWN = 0, - //Left stick + // Left stick GAMEPAD_AXIS_LEFT_X, GAMEPAD_AXIS_LEFT_Y, - //Right stick + // Right stick GAMEPAD_AXIS_RIGHT_X, GAMEPAD_AXIS_RIGHT_Y, - //Pressure levels + // Pressure levels for the back triggers GAMEPAD_AXIS_LEFT_TRIGGER, // [1..-1] (pressure-level) GAMEPAD_AXIS_RIGHT_TRIGGER // [1..-1] (pressure-level) } GamepadAxis; -- cgit v1.2.3 From 86eba249708a0bcfee5ba665c1fbb1d2050113ec Mon Sep 17 00:00:00 2001 From: Reece Mackie <20544390+Rover656@users.noreply.github.com> Date: Sun, 28 Apr 2019 16:08:07 +0100 Subject: This is dumb... --- src/raylib.h | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) (limited to 'src/raylib.h') diff --git a/src/raylib.h b/src/raylib.h index 3fb4ee6d..dfbebf90 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -627,21 +627,21 @@ typedef enum // This is normally ABXY/Circle, Triangle, Square, Cross. No support for 6 button controllers though.. GAMEPAD_BUTTON_LEFT_FACE_UP, - GAMEPAD_BUTTON_LEFT_FACE_RIGHT, - GAMEPAD_BUTTON_LEFT_FACE_DOWN, + GAMEPAD_BUTTON_LEFT_FACE_RIGHT, + GAMEPAD_BUTTON_LEFT_FACE_DOWN, GAMEPAD_BUTTON_LEFT_FACE_LEFT, // This is normally a DPAD - GAMEPAD_BUTTON_RIGHT_FACE_UP, - GAMEPAD_BUTTON_RIGHT_FACE_RIGHT, - GAMEPAD_BUTTON_RIGHT_FACE_DOWN, - GAMEPAD_BUTTON_RIGHT_FACE_LEFT, + GAMEPAD_BUTTON_RIGHT_FACE_UP, + GAMEPAD_BUTTON_RIGHT_FACE_RIGHT, + GAMEPAD_BUTTON_RIGHT_FACE_DOWN, + GAMEPAD_BUTTON_RIGHT_FACE_LEFT, // Triggers GAMEPAD_BUTTON_LEFT_TRIGGER_1, GAMEPAD_BUTTON_LEFT_TRIGGER_2, - GAMEPAD_BUTTON_RIGHT_TRIGGER_1, - GAMEPAD_BUTTON_RIGHT_TRIGGER_2, + GAMEPAD_BUTTON_RIGHT_TRIGGER_1, + GAMEPAD_BUTTON_RIGHT_TRIGGER_2, // These are buttons in the center of the gamepad GAMEPAD_BUTTON_MIDDLE_LEFT, //PS3 Select @@ -650,25 +650,25 @@ typedef enum // These are the joystick press in buttons GAMEPAD_BUTTON_LEFT_THUMB, - GAMEPAD_BUTTON_RIGHT_THUMB + GAMEPAD_BUTTON_RIGHT_THUMB } GamepadButton; typedef enum { - // This is here just for error checking + // This is here just for error checking GAMEPAD_AXIS_UNKNOWN = 0, // Left stick - GAMEPAD_AXIS_LEFT_X, - GAMEPAD_AXIS_LEFT_Y, + GAMEPAD_AXIS_LEFT_X, + GAMEPAD_AXIS_LEFT_Y, // Right stick - GAMEPAD_AXIS_RIGHT_X, - GAMEPAD_AXIS_RIGHT_Y, + GAMEPAD_AXIS_RIGHT_X, + GAMEPAD_AXIS_RIGHT_Y, // Pressure levels for the back triggers - GAMEPAD_AXIS_LEFT_TRIGGER, // [1..-1] (pressure-level) - GAMEPAD_AXIS_RIGHT_TRIGGER // [1..-1] (pressure-level) + GAMEPAD_AXIS_LEFT_TRIGGER, // [1..-1] (pressure-level) + GAMEPAD_AXIS_RIGHT_TRIGGER // [1..-1] (pressure-level) } GamepadAxis; // Shader location point type -- cgit v1.2.3 From 100c82e369c3b40c2aafe4c1faddd6ee7d37ba6a Mon Sep 17 00:00:00 2001 From: raysan5 Date: Sun, 28 Apr 2019 18:23:21 +0200 Subject: Review formatting to follow raylib style --- src/core.c | 114 +++++++++++++++++++++++++++++++---------------------------- src/raylib.h | 19 +++++----- 2 files changed, 68 insertions(+), 65 deletions(-) (limited to 'src/raylib.h') diff --git a/src/core.c b/src/core.c index 8bff8245..f2978d77 100644 --- a/src/core.c +++ b/src/core.c @@ -445,6 +445,8 @@ static void Wait(float ms); // Wait for some millise static bool GetKeyStatus(int key); // Returns if a key has been pressed static bool GetMouseButtonStatus(int button); // Returns if a mouse button has been pressed +static int GetGamepadButton(int button); // Get gamepad button generic to all platforms +static int GetGamepadAxis(int axis); // Get gamepad axis generic to all platforms static void PollInputEvents(void); // Register user events static void LogoAnimation(void); // Plays raylib logo appearing animation @@ -3081,95 +3083,97 @@ static bool GetMouseButtonStatus(int button) #endif } -static GamepadButton GetGamepadButton(int button) +// Get gamepad button generic to all platforms +static int GetGamepadButton(int button) { - GamepadButton b = GAMEPAD_BUTTON_UNKNOWN; + int btn = GAMEPAD_BUTTON_UNKNOWN; #if defined(PLATFORM_DESKTOP) switch (button) { - case GLFW_GAMEPAD_BUTTON_Y: b = GAMEPAD_BUTTON_RIGHT_FACE_UP; break; - case GLFW_GAMEPAD_BUTTON_B: b = GAMEPAD_BUTTON_RIGHT_FACE_RIGHT; break; - case GLFW_GAMEPAD_BUTTON_A: b = GAMEPAD_BUTTON_RIGHT_FACE_DOWN; break; - case GLFW_GAMEPAD_BUTTON_X: b = GAMEPAD_BUTTON_RIGHT_FACE_LEFT; break; + case GLFW_GAMEPAD_BUTTON_Y: btn = GAMEPAD_BUTTON_RIGHT_FACE_UP; break; + case GLFW_GAMEPAD_BUTTON_B: btn = GAMEPAD_BUTTON_RIGHT_FACE_RIGHT; break; + case GLFW_GAMEPAD_BUTTON_A: btn = GAMEPAD_BUTTON_RIGHT_FACE_DOWN; break; + case GLFW_GAMEPAD_BUTTON_X: btn = GAMEPAD_BUTTON_RIGHT_FACE_LEFT; break; - case GLFW_GAMEPAD_BUTTON_LEFT_BUMPER: b = GAMEPAD_BUTTON_LEFT_TRIGGER_1; break; - case GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER: b = GAMEPAD_BUTTON_RIGHT_TRIGGER_1; break; + case GLFW_GAMEPAD_BUTTON_LEFT_BUMPER: btn = GAMEPAD_BUTTON_LEFT_TRIGGER_1; break; + case GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER: btn = GAMEPAD_BUTTON_RIGHT_TRIGGER_1; break; - case GLFW_GAMEPAD_BUTTON_BACK: b = GAMEPAD_BUTTON_MIDDLE_LEFT; break; - case GLFW_GAMEPAD_BUTTON_GUIDE: b = GAMEPAD_BUTTON_MIDDLE; break; - case GLFW_GAMEPAD_BUTTON_START: b = GAMEPAD_BUTTON_MIDDLE_RIGHT; break; + case GLFW_GAMEPAD_BUTTON_BACK: btn = GAMEPAD_BUTTON_MIDDLE_LEFT; break; + case GLFW_GAMEPAD_BUTTON_GUIDE: btn = GAMEPAD_BUTTON_MIDDLE; break; + case GLFW_GAMEPAD_BUTTON_START: btn = GAMEPAD_BUTTON_MIDDLE_RIGHT; break; - case GLFW_GAMEPAD_BUTTON_DPAD_UP: b = GAMEPAD_BUTTON_LEFT_FACE_UP; break; - case GLFW_GAMEPAD_BUTTON_DPAD_RIGHT: b = GAMEPAD_BUTTON_LEFT_FACE_RIGHT; break; - case GLFW_GAMEPAD_BUTTON_DPAD_DOWN: b = GAMEPAD_BUTTON_LEFT_FACE_DOWN; break; - case GLFW_GAMEPAD_BUTTON_DPAD_LEFT: b = GAMEPAD_BUTTON_LEFT_FACE_LEFT; break; + case GLFW_GAMEPAD_BUTTON_DPAD_UP: btn = GAMEPAD_BUTTON_LEFT_FACE_UP; break; + case GLFW_GAMEPAD_BUTTON_DPAD_RIGHT: btn = GAMEPAD_BUTTON_LEFT_FACE_RIGHT; break; + case GLFW_GAMEPAD_BUTTON_DPAD_DOWN: btn = GAMEPAD_BUTTON_LEFT_FACE_DOWN; break; + case GLFW_GAMEPAD_BUTTON_DPAD_LEFT: btn = GAMEPAD_BUTTON_LEFT_FACE_LEFT; break; - case GLFW_GAMEPAD_BUTTON_LEFT_THUMB: b = GAMEPAD_BUTTON_LEFT_THUMB; break; - case GLFW_GAMEPAD_BUTTON_RIGHT_THUMB: b = GAMEPAD_BUTTON_RIGHT_THUMB; break; + case GLFW_GAMEPAD_BUTTON_LEFT_THUMB: btn = GAMEPAD_BUTTON_LEFT_THUMB; break; + case GLFW_GAMEPAD_BUTTON_RIGHT_THUMB: btn = GAMEPAD_BUTTON_RIGHT_THUMB; break; } #endif #if defined(PLATFORM_UWP) - b = button; // UWP will provide the correct button + btn = button; // UWP will provide the correct button #endif #if defined(PLATFORM_WEB) - // https://www.w3.org/TR/gamepad/#gamepad-interface + // Gamepad Buttons reference: https://www.w3.org/TR/gamepad/#gamepad-interface switch (button) { - case 0: b = GAMEPAD_BUTTON_RIGHT_FACE_DOWN; break; - case 1: b = GAMEPAD_BUTTON_RIGHT_FACE_RIGHT; break; - case 2: b = GAMEPAD_BUTTON_RIGHT_FACE_LEFT; break; - case 3: b = GAMEPAD_BUTTON_RIGHT_FACE_UP; break; - case 4: b = GAMEPAD_BUTTON_LEFT_TRIGGER_1; break; - case 5: b = GAMEPAD_BUTTON_RIGHT_TRIGGER_1; break; - case 6: b = GAMEPAD_BUTTON_LEFT_TRIGGER_2; break; - case 7: b = GAMEPAD_BUTTON_RIGHT_TRIGGER_2; break; - case 8: b = GAMEPAD_BUTTON_MIDDLE_LEFT; break; - case 9: b = GAMEPAD_BUTTON_MIDDLE_RIGHT; break; - case 10: b = GAMEPAD_BUTTON_LEFT_THUMB; break; - case 11: b = GAMEPAD_BUTTON_RIGHT_THUMB; break; - case 12: b = GAMEPAD_BUTTON_LEFT_FACE_UP; break; - case 13: b = GAMEPAD_BUTTON_LEFT_FACE_DOWN; break; - case 14: b = GAMEPAD_BUTTON_LEFT_FACE_LEFT; break; - case 15: b = GAMEPAD_BUTTON_LEFT_FACE_RIGHT; break; + case 0: btn = GAMEPAD_BUTTON_RIGHT_FACE_DOWN; break; + case 1: btn = GAMEPAD_BUTTON_RIGHT_FACE_RIGHT; break; + case 2: btn = GAMEPAD_BUTTON_RIGHT_FACE_LEFT; break; + case 3: btn = GAMEPAD_BUTTON_RIGHT_FACE_UP; break; + case 4: btn = GAMEPAD_BUTTON_LEFT_TRIGGER_1; break; + case 5: btn = GAMEPAD_BUTTON_RIGHT_TRIGGER_1; break; + case 6: btn = GAMEPAD_BUTTON_LEFT_TRIGGER_2; break; + case 7: btn = GAMEPAD_BUTTON_RIGHT_TRIGGER_2; break; + case 8: btn = GAMEPAD_BUTTON_MIDDLE_LEFT; break; + case 9: btn = GAMEPAD_BUTTON_MIDDLE_RIGHT; break; + case 10: btn = GAMEPAD_BUTTON_LEFT_THUMB; break; + case 11: btn = GAMEPAD_BUTTON_RIGHT_THUMB; break; + case 12: btn = GAMEPAD_BUTTON_LEFT_FACE_UP; break; + case 13: btn = GAMEPAD_BUTTON_LEFT_FACE_DOWN; break; + case 14: btn = GAMEPAD_BUTTON_LEFT_FACE_LEFT; break; + case 15: btn = GAMEPAD_BUTTON_LEFT_FACE_RIGHT; break; } #endif - return b; + return btn; } -static GamepadAxis GetGamepadAxis(int axis) +// Get gamepad axis generic to all platforms +static int GetGamepadAxis(int axis) { - GamepadAxis a = GAMEPAD_AXIS_UNKNOWN; + int axs = GAMEPAD_AXIS_UNKNOWN; #if defined(PLATFORM_DESKTOP) - switch(axis) + switch (axis) { - case GLFW_GAMEPAD_AXIS_LEFT_X: a = GAMEPAD_AXIS_LEFT_X; break; - case GLFW_GAMEPAD_AXIS_LEFT_Y: a = GAMEPAD_AXIS_LEFT_Y; break; - case GLFW_GAMEPAD_AXIS_RIGHT_X: a = GAMEPAD_AXIS_RIGHT_X; break; - case GLFW_GAMEPAD_AXIS_RIGHT_Y: a = GAMEPAD_AXIS_RIGHT_Y; break; - case GLFW_GAMEPAD_AXIS_LEFT_TRIGGER: a = GAMEPAD_AXIS_LEFT_TRIGGER; break; - case GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER: a = GAMEPAD_AXIS_RIGHT_TRIGGER; break; + case GLFW_GAMEPAD_AXIS_LEFT_X: axs = GAMEPAD_AXIS_LEFT_X; break; + case GLFW_GAMEPAD_AXIS_LEFT_Y: axs = GAMEPAD_AXIS_LEFT_Y; break; + case GLFW_GAMEPAD_AXIS_RIGHT_X: axs = GAMEPAD_AXIS_RIGHT_X; break; + case GLFW_GAMEPAD_AXIS_RIGHT_Y: axs = GAMEPAD_AXIS_RIGHT_Y; break; + case GLFW_GAMEPAD_AXIS_LEFT_TRIGGER: axs = GAMEPAD_AXIS_LEFT_TRIGGER; break; + case GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER: axs = GAMEPAD_AXIS_RIGHT_TRIGGER; break; } #endif #if defined(PLATFORM_UWP) - a = axis; // UWP will provide the correct axis + axs = axis; // UWP will provide the correct axis #endif #if defined(PLATFORM_WEB) - // https://www.w3.org/TR/gamepad/#gamepad-interface - switch(axis) + // Gamepad axis reference:https://www.w3.org/TR/gamepad/#gamepad-interface + switch (axis) { - case 0: a = GAMEPAD_AXIS_LEFT_X; - case 1: a = GAMEPAD_AXIS_LEFT_Y; - case 2: a = GAMEPAD_AXIS_RIGHT_X; - case 3: a = GAMEPAD_AXIS_RIGHT_X; + case 0: axs = GAMEPAD_AXIS_LEFT_X; + case 1: axs = GAMEPAD_AXIS_LEFT_Y; + case 2: axs = GAMEPAD_AXIS_RIGHT_X; + case 3: axs = GAMEPAD_AXIS_RIGHT_X; } #endif - return a; + return axs; } // Poll (store) all input events @@ -3429,7 +3433,7 @@ static void PollInputEvents(void) for (int k = 0; (axes != NULL) && (k < GLFW_GAMEPAD_AXIS_LAST + 1) && (k < MAX_GAMEPAD_AXIS); k++) { - const GamepadAxis axis = GetGamepadAxis(k); + const int axis = GetGamepadAxis(k); gamepadAxisState[i][axis] = axes[k]; } @@ -3485,7 +3489,7 @@ static void PollInputEvents(void) // Register axis data for every connected gamepad for (int j = 0; (j < gamepadState.numAxes) && (j < MAX_GAMEPAD_AXIS); j++) { - const GamepadAxis axis = GetGamepadAxis(j); + const int axis = GetGamepadAxis(j); gamepadAxisState[i][axis] = gamepadState.axis[j]; } diff --git a/src/raylib.h b/src/raylib.h index dfbebf90..3bd64b3b 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -620,12 +620,12 @@ typedef enum { } GamepadNumber; // Gamepad Buttons -typedef enum -{ +typedef enum { // This is here just for error checking GAMEPAD_BUTTON_UNKNOWN = 0, - // This is normally ABXY/Circle, Triangle, Square, Cross. No support for 6 button controllers though.. + // This is normally [A,B,X,Y]/[Circle,Triangle,Square,Cross] + // No support for 6 button controllers though.. GAMEPAD_BUTTON_LEFT_FACE_UP, GAMEPAD_BUTTON_LEFT_FACE_RIGHT, GAMEPAD_BUTTON_LEFT_FACE_DOWN, @@ -644,17 +644,16 @@ typedef enum GAMEPAD_BUTTON_RIGHT_TRIGGER_2, // These are buttons in the center of the gamepad - GAMEPAD_BUTTON_MIDDLE_LEFT, //PS3 Select - GAMEPAD_BUTTON_MIDDLE, //PS Button/XBOX Button - GAMEPAD_BUTTON_MIDDLE_RIGHT, //PS3 Start + GAMEPAD_BUTTON_MIDDLE_LEFT, //PS3 Select + GAMEPAD_BUTTON_MIDDLE, //PS Button/XBOX Button + GAMEPAD_BUTTON_MIDDLE_RIGHT, //PS3 Start // These are the joystick press in buttons GAMEPAD_BUTTON_LEFT_THUMB, GAMEPAD_BUTTON_RIGHT_THUMB } GamepadButton; -typedef enum -{ +typedef enum { // This is here just for error checking GAMEPAD_AXIS_UNKNOWN = 0, @@ -667,8 +666,8 @@ typedef enum GAMEPAD_AXIS_RIGHT_Y, // Pressure levels for the back triggers - GAMEPAD_AXIS_LEFT_TRIGGER, // [1..-1] (pressure-level) - GAMEPAD_AXIS_RIGHT_TRIGGER // [1..-1] (pressure-level) + GAMEPAD_AXIS_LEFT_TRIGGER, // [1..-1] (pressure-level) + GAMEPAD_AXIS_RIGHT_TRIGGER // [1..-1] (pressure-level) } GamepadAxis; // Shader location point type -- cgit v1.2.3 From 98243877739fa59ffd7e40cae20b57a13c2dbb93 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 7 May 2019 15:16:14 +0200 Subject: Added resource file for raylib.dll Some minor tweaks --- src/Makefile | 1 + src/raylib.dll.rc | 27 +++++++++++++++++++++++++++ src/raylib.h | 5 +++-- src/raylib.rc | 2 +- 4 files changed, 32 insertions(+), 3 deletions(-) create mode 100644 src/raylib.dll.rc (limited to 'src/raylib.h') diff --git a/src/Makefile b/src/Makefile index 35a10bc7..c50d4279 100644 --- a/src/Makefile +++ b/src/Makefile @@ -437,6 +437,7 @@ else ifeq ($(RAYLIB_LIBTYPE),SHARED) ifeq ($(PLATFORM),PLATFORM_DESKTOP) ifeq ($(PLATFORM_OS),WINDOWS) + # TODO: Compile resource file raylib.dll.rc for linkage on raylib.dll generation $(CC) -shared -o $(RAYLIB_RELEASE_PATH)/raylib.dll $(OBJS) -L$(RAYLIB_RELEASE_PATH) -static-libgcc -lopengl32 -lgdi32 -lwinmm -Wl,--out-implib,$(RAYLIB_RELEASE_PATH)/libraylibdll.a @echo "raylib dynamic library (raylib.dll) and import library (libraylibdll.a) generated!" endif diff --git a/src/raylib.dll.rc b/src/raylib.dll.rc new file mode 100644 index 00000000..e5dca309 --- /dev/null +++ b/src/raylib.dll.rc @@ -0,0 +1,27 @@ +GLFW_ICON ICON "raylib.ico" + +1 VERSIONINFO +FILEVERSION 2,5,0,0 +PRODUCTVERSION 2,5,0,0 +BEGIN + BLOCK "StringFileInfo" + BEGIN + //BLOCK "080904E4" // English UK + BLOCK "040904E4" // English US + BEGIN + //VALUE "CompanyName", "raylib technologies" + VALUE "FileDescription", "raylib dynamic library (www.raylib.com)" + VALUE "FileVersion", "2.5.0" + VALUE "InternalName", "raylib_dll" + VALUE "LegalCopyright", "(c) 2019 Ramon Santamaria (@raysan5)" + //VALUE "OriginalFilename", "raylib.dll" + VALUE "ProductName", "raylib" + VALUE "ProductVersion", "2.5.0" + END + END + BLOCK "VarFileInfo" + BEGIN + //VALUE "Translation", 0x809, 1252 // English UK + VALUE "Translation", 0x409, 1252 // English US + END +END diff --git a/src/raylib.h b/src/raylib.h index 3bd64b3b..21202f18 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1174,13 +1174,14 @@ RLAPI void DrawTextRecEx(Font font, const char *text, Rectangle rec, float fontS RLAPI int MeasureText(const char *text, int fontSize); // Measure string width for default font RLAPI Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing); // Measure string size for Font RLAPI int GetGlyphIndex(Font font, int character); // Get index position for a unicode character on font -RLAPI int GetNextCodepoint(const char* text, int* count); // Returns next codepoint in a UTF8 encoded `text` or 0x3f(`?`) on failure. `count` will hold the total number of bytes processed. +RLAPI int GetNextCodepoint(const char *text, int *count); // Returns next codepoint in a UTF8 encoded string + // NOTE: 0x3f(`?`) is returned on failure, `count` will hold the total number of bytes processed // Text strings management functions // NOTE: Some strings allocate memory internally for returned strings, just be careful! RLAPI bool TextIsEqual(const char *text1, const char *text2); // Check if two text string are equal RLAPI unsigned int TextLength(const char *text); // Get text length, checks for '\0' ending -RLAPI unsigned int TextCountCodepoints(const char *text); // Get total number of characters(codepoints) in a UTF8 encoded `text` until '\0' is found. +RLAPI unsigned int TextCountCodepoints(const char *text); // Get total number of characters (codepoints) in a UTF8 encoded string RLAPI const char *TextFormat(const char *text, ...); // Text formatting with variables (sprintf style) RLAPI const char *TextSubtext(const char *text, int position, int length); // Get a piece of a text string RLAPI const char *TextReplace(char *text, const char *replace, const char *by); // Replace text string (memory should be freed!) diff --git a/src/raylib.rc b/src/raylib.rc index c2fdfa46..4d204c65 100644 --- a/src/raylib.rc +++ b/src/raylib.rc @@ -10,7 +10,7 @@ BEGIN BLOCK "040904E4" // English US BEGIN //VALUE "CompanyName", "raylib technologies" - VALUE "FileDescription", "Created using raylib (www.raylib.com)" + VALUE "FileDescription", "raylib application (www.raylib.com)" VALUE "FileVersion", "2.5.0" VALUE "InternalName", "raylib app" VALUE "LegalCopyright", "(c) 2019 Ramon Santamaria (@raysan5)" -- cgit v1.2.3 From 498c172d8e8f35def505045970a6adf50730c392 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 6 Jun 2019 11:38:45 +0200 Subject: Review function prototype --- src/raylib.h | 2 +- src/rlgl.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'src/raylib.h') diff --git a/src/raylib.h b/src/raylib.h index 21202f18..9ce2e079 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1304,7 +1304,7 @@ RLAPI void SetShaderValueMatrix(Shader shader, int uniformLoc, Matrix mat); 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 +RLAPI Matrix GetMatrixModelview(void); // Get internal modelview matrix // Texture maps generation (PBR) // NOTE: Required shaders should be provided diff --git a/src/rlgl.h b/src/rlgl.h index 639a107e..10d21fcc 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -525,7 +525,7 @@ RLAPI void SetShaderValueV(Shader shader, int uniformLoc, const void *value, int RLAPI void SetShaderValueMatrix(Shader shader, int uniformLoc, Matrix mat); // Set shader uniform value (matrix 4x4) 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 +RLAPI Matrix GetMatrixModelview(void); // Get internal modelview matrix // Texture maps generation (PBR) // NOTE: Required shaders should be provided @@ -3137,7 +3137,7 @@ void SetMatrixModelview(Matrix view) } // Return internal modelview matrix -Matrix GetMatrixModelview() +Matrix GetMatrixModelview(void) { Matrix matrix = MatrixIdentity(); #if defined(GRAPHICS_API_OPENGL_11) -- cgit v1.2.3