diff --git a/.gitignore b/.gitignore index 9153a03ea..dcfa5bce2 100644 --- a/.gitignore +++ b/.gitignore @@ -57,6 +57,13 @@ packages/ *.a *.bc *.so +*.so.* + +# Ignore wasm data in examples/ +examples/**/*.wasm +examples/**/*.data +examples/**/*.js +examples/**/*.html # Ignore files build by xcode *.mode*v* diff --git a/BINDINGS.md b/BINDINGS.md index 87a076b00..b154dc7b2 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -96,6 +96,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | raylib-never | 3.0 | [Never](https://github.com/never-lang/never) | https://github.com/never-lang/raylib-never | | hb-raylib | 3.5 | [Harbour](https://harbour.github.io) | https://github.com/MarcosLeonardoMendezGerencir/hb-raylib | | Relib | 3.5 | [ReCT](https://github.com/RedCubeDev-ByteSpace/ReCT) | https://github.com/RedCubeDev-ByteSpace/Relib | +| rayex | 3.7 | [elixir](https://elixir-lang.org/) | https://github.com/shiryel/rayex | | raylib.cbl | 2.0 | [COBOL](https://en.wikipedia.org/wiki/COBOL) | *[code examples](https://github.com/Martinfx/Cobol/tree/master/OpenCobol/Games/raylib)* | ### Utility Wrapers diff --git a/CHANGELOG b/CHANGELOG index c37aa3162..54bf0db61 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -3,6 +3,17 @@ changelog Current Release: raylib 3.7.0 (26 April 2021) +------------------------------------------------------------------------- +Release: raylib 4.0 (November? 2021) +------------------------------------------------------------------------- +KEY CHANGES: + - + - + - + +Detailed changes: + + ------------------------------------------------------------------------- Release: raylib 3.7 (26 April 2021) ------------------------------------------------------------------------- diff --git a/examples/Makefile b/examples/Makefile index bded27e53..5abf0c708 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -244,7 +244,7 @@ ifeq ($(PLATFORM),PLATFORM_WEB) # --memory-init-file 0 # to avoid an external memory initialization code file (.mem) # --preload-file resources # specify a resources folder for data compilation # --source-map-base # allow debugging in browser with source map - CFLAGS += -s USE_GLFW=3 -s ASYNCIFY -s TOTAL_MEMORY=67108864 -s FORCE_FILESYSTEM=1 $(dir $<)resources@resources + CFLAGS += -s USE_GLFW=3 -s ASYNCIFY -s TOTAL_MEMORY=67108864 -s FORCE_FILESYSTEM=1 --preload-file $(dir $<)resources@resources # NOTE: Simple raylib examples are compiled to be interpreter with asyncify, that way, # we can compile same code for ALL platforms with no change required, but, working on bigger diff --git a/examples/others/rlgl_standalone.c b/examples/others/rlgl_standalone.c index c8c0b0deb..cc0eda2db 100644 --- a/examples/others/rlgl_standalone.c +++ b/examples/others/rlgl_standalone.c @@ -2,18 +2,17 @@ * * raylib [rlgl] example - Using rlgl module as standalone module * -* NOTE: This example requires OpenGL 3.3 or ES2 versions for shaders support, +* rlgl library is an abstraction layer for multiple OpenGL versions (1.1, 2.1, 3.3 Core, ES 2.0) +* that provides a pseudo-OpenGL 1.1 immediate-mode style API (rlVertex, rlTranslate, rlRotate...) +* +* NOTE: This example requires OpenGL 3.3 or OpenGL ES 2.0 for shaders support, * OpenGL 1.1 does not support shaders but it can also be used. * * DEPENDENCIES: -* rlgl.h - OpenGL 1.1 immediate-mode style coding translation layer -* glad.h - OpenGL extensions initialization library (required by rlgl) -* raymath.h - 3D math library (required by rlgl) * glfw3 - Windows and context initialization library -* -* rlgl library is provided as a single-file header-only library, this library -* allows coding in a pseudo-OpenGL 1.1 style while translating calls to multiple -* OpenGL versions backends (1.1, 2.1, 3.3, ES 2.0). +* rlgl.h - OpenGL abstraction layer to OpenGL 1.1, 3.3 or ES2 +* glad.h - OpenGL extensions initialization library (required by rlgl) +* raymath.h - 3D math library * * WINDOWS COMPILATION: * gcc -o rlgl_standalone.exe rlgl_standalone.c -s -Iexternal\include -I..\..\src \ @@ -21,15 +20,16 @@ * * APPLE COMPILATION: * gcc -o rlgl_standalone rlgl_standalone.c -I../../src -Iexternal/include -Lexternal/lib \ -* -lglfw3 -std=c99 -framework CoreVideo -framework OpenGL -framework OpenAL \ -* -framework IOKit -framework Cocoa -Wno-deprecated-declarations +* -lglfw3 -framework CoreVideo -framework OpenGL -framework IOKit -framework Cocoa +* -Wno-deprecated-declarations -std=c99 -DGRAPHICS_API_OPENGL_33 +* * * LICENSE: zlib/libpng * * 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-2019 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2021 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. @@ -107,7 +107,7 @@ static void DrawCube(Vector3 position, float width, float height, float length, static void DrawCubeWires(Vector3 position, float width, float height, float length, Color color); static void DrawRectangleV(Vector2 position, Vector2 size, Color color); -// NOTE: We used raymath to get this functionality but it can be implemented here +// NOTE: We use raymath to get this functionality but it could be implemented in this module //static Matrix MatrixIdentity(void); //static Matrix MatrixOrtho(double left, double right, double bottom, double top, double near, double far); //static Matrix MatrixPerspective(double fovy, double aspect, double near, double far); @@ -144,7 +144,6 @@ int main(void) glfwWindowHint( GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE ); #endif - GLFWwindow *window = glfwCreateWindow(screenWidth, screenHeight, "rlgl standalone", NULL, NULL); if (!window) @@ -219,8 +218,8 @@ int main(void) // Draw '2D' elements in the scene (GUI) //----------------------------------------------- -#define RLGL_CREATE_MATRIX_MANUALLY -#if defined(RLGL_CREATE_MATRIX_MANUALLY) +#define RLGL_SET_MATRIX_MANUALLY +#if defined(RLGL_SET_MATRIX_MANUALLY) matProj = MatrixOrtho(0.0, screenWidth, screenHeight, 0.0, 0.0, 1.0); matView = MatrixIdentity(); @@ -237,7 +236,7 @@ int main(void) #endif DrawRectangleV((Vector2){ 10.0f, 10.0f }, (Vector2){ 780.0f, 20.0f }, DARKGRAY); - // Draw internal render batch buffers (3D data) + // Draw internal render batch buffers (2D data) rlDrawRenderBatchActive(); //----------------------------------------------- diff --git a/examples/text/text_rectangle_bounds.c b/examples/text/text_rectangle_bounds.c index 9754cd456..c763fb2ec 100644 --- a/examples/text/text_rectangle_bounds.c +++ b/examples/text/text_rectangle_bounds.c @@ -13,6 +13,10 @@ #include "raylib.h" +static void DrawTextRec(Font font, const char *text, Rectangle rec, float fontSize, float spacing, bool wordWrap, Color tint); // Draw text using font inside rectangle limits +static void DrawTextRecEx(Font font, const char *text, Rectangle rec, float fontSize, float spacing, bool wordWrap, Color tint, int selectStart, int selectLength, Color selectTint, Color selectBackTint); // Draw text using font inside rectangle limits with support for text selection + +// Main entry point int main(void) { // Initialization @@ -119,4 +123,144 @@ tempor incididunt ut labore et dolore magna aliqua. Nec ullamcorper sit amet ris //-------------------------------------------------------------------------------------- return 0; +} + +//-------------------------------------------------------------------------------------- +// Module functions definition +//-------------------------------------------------------------------------------------- + +// Draw text using font inside rectangle limits +static void DrawTextRec(Font font, const char *text, Rectangle rec, float fontSize, float spacing, bool wordWrap, Color tint) +{ + DrawTextRecEx(font, text, rec, fontSize, spacing, wordWrap, tint, 0, 0, WHITE, WHITE); +} + +// Draw text using font inside rectangle limits with support for text selection +static void DrawTextRecEx(Font font, const char *text, Rectangle rec, float fontSize, float spacing, bool wordWrap, Color tint, int selectStart, int selectLength, Color selectTint, Color selectBackTint) +{ + int length = TextLength(text); // Total length in bytes of the text, scanned by codepoints in loop + + float textOffsetY = 0; // Offset between lines (on line break '\n') + float textOffsetX = 0.0f; // Offset X to next character to draw + + float scaleFactor = fontSize/(float)font.baseSize; // Character rectangle scaling factor + + // Word/character wrapping mechanism variables + enum { MEASURE_STATE = 0, DRAW_STATE = 1 }; + int state = wordWrap? MEASURE_STATE : DRAW_STATE; + + int startLine = -1; // Index where to begin drawing (where a line begins) + int endLine = -1; // Index where to stop drawing (where a line ends) + int lastk = -1; // Holds last value of the character position + + for (int i = 0, k = 0; i < length; i++, k++) + { + // Get next codepoint from byte string and glyph index in font + int codepointByteCount = 0; + int codepoint = GetCodepoint(&text[i], &codepointByteCount); + int index = GetGlyphIndex(font, codepoint); + + // 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 moving one byte + if (codepoint == 0x3f) codepointByteCount = 1; + i += (codepointByteCount - 1); + + float glyphWidth = 0; + if (codepoint != '\n') + { + glyphWidth = (font.chars[index].advanceX == 0) ? font.recs[index].width*scaleFactor : font.chars[index].advanceX*scaleFactor; + + if (i + 1 < length) glyphWidth = glyphWidth + spacing; + } + + // NOTE: When wordWrap is ON we first measure how much of the text we can draw before going outside of the rec container + // We store this info in startLine and endLine, then we change states, draw the text between those two variables + // and change states again and again recursively until the end of the text (or until we get outside of the container). + // When wordWrap is OFF we don't need the measure state so we go to the drawing state immediately + // and begin drawing on the next line before we can get outside 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 + // Ref: http://jkorpela.fi/chars/spaces.html + if ((codepoint == ' ') || (codepoint == '\t') || (codepoint == '\n')) endLine = i; + + if ((textOffsetX + glyphWidth) > rec.width) + { + endLine = (endLine < 1)? i : endLine; + if (i == endLine) endLine -= codepointByteCount; + if ((startLine + codepointByteCount) == endLine) endLine = (i - codepointByteCount); + + state = !state; + } + else if ((i + 1) == length) + { + endLine = i; + state = !state; + } + else if (codepoint == '\n') state = !state; + + if (state == DRAW_STATE) + { + textOffsetX = 0; + i = startLine; + glyphWidth = 0; + + // Save character position when we switch states + int tmp = lastk; + lastk = k - 1; + k = tmp; + } + } + else + { + if (codepoint == '\n') + { + if (!wordWrap) + { + textOffsetY += (font.baseSize + font.baseSize/2)*scaleFactor; + textOffsetX = 0; + } + } + else + { + if (!wordWrap && ((textOffsetX + glyphWidth) > rec.width)) + { + textOffsetY += (font.baseSize + font.baseSize/2)*scaleFactor; + textOffsetX = 0; + } + + // When text overflows rectangle height limit, just stop drawing + if ((textOffsetY + font.baseSize*scaleFactor) > rec.height) break; + + // Draw selection background + bool isGlyphSelected = false; + if ((selectStart >= 0) && (k >= selectStart) && (k < (selectStart + selectLength))) + { + DrawRectangleRec((Rectangle){ rec.x + textOffsetX - 1, rec.y + textOffsetY, glyphWidth, (float)font.baseSize*scaleFactor }, selectBackTint); + isGlyphSelected = true; + } + + // Draw current character glyph + if ((codepoint != ' ') && (codepoint != '\t')) + { + DrawTextCodepoint(font, codepoint, (Vector2){ rec.x + textOffsetX, rec.y + textOffsetY }, fontSize, isGlyphSelected? selectTint : tint); + } + } + + if (wordWrap && (i == endLine)) + { + textOffsetY += (font.baseSize + font.baseSize/2)*scaleFactor; + textOffsetX = 0; + startLine = endLine; + endLine = -1; + glyphWidth = 0; + selectStart += lastk - k; + k = lastk; + + state = !state; + } + } + + textOffsetX += glyphWidth; + } } \ No newline at end of file diff --git a/examples/text/text_unicode.c b/examples/text/text_unicode.c index fa336de7f..2954a28e1 100644 --- a/examples/text/text_unicode.c +++ b/examples/text/text_unicode.c @@ -133,6 +133,9 @@ struct { //-------------------------------------------------------------------------------------- static void RandomizeEmoji(void); // Fills the emoji array with random emojis +static void DrawTextRec(Font font, const char *text, Rectangle rec, float fontSize, float spacing, bool wordWrap, Color tint); // Draw text using font inside rectangle limits +static void DrawTextRecEx(Font font, const char *text, Rectangle rec, float fontSize, float spacing, bool wordWrap, Color tint, int selectStart, int selectLength, Color selectTint, Color selectBackTint); // Draw text using font inside rectangle limits with support for text selection + //-------------------------------------------------------------------------------------- // Global variables //-------------------------------------------------------------------------------------- @@ -145,6 +148,9 @@ struct { static int hovered = -1, selected = -1; +//-------------------------------------------------------------------------------------- +// Main entry point +//-------------------------------------------------------------------------------------- int main(int argc, char **argv) { // Initialization @@ -317,3 +323,143 @@ static void RandomizeEmoji(void) emoji[i].message = GetRandomValue(0, SIZEOF(messages) - 1); } } + +//-------------------------------------------------------------------------------------- +// Module functions definition +//-------------------------------------------------------------------------------------- + +// Draw text using font inside rectangle limits +static void DrawTextRec(Font font, const char *text, Rectangle rec, float fontSize, float spacing, bool wordWrap, Color tint) +{ + DrawTextRecEx(font, text, rec, fontSize, spacing, wordWrap, tint, 0, 0, WHITE, WHITE); +} + +// Draw text using font inside rectangle limits with support for text selection +static void DrawTextRecEx(Font font, const char *text, Rectangle rec, float fontSize, float spacing, bool wordWrap, Color tint, int selectStart, int selectLength, Color selectTint, Color selectBackTint) +{ + int length = TextLength(text); // Total length in bytes of the text, scanned by codepoints in loop + + float textOffsetY = 0; // Offset between lines (on line break '\n') + float textOffsetX = 0.0f; // Offset X to next character to draw + + float scaleFactor = fontSize/(float)font.baseSize; // Character rectangle scaling factor + + // Word/character wrapping mechanism variables + enum { MEASURE_STATE = 0, DRAW_STATE = 1 }; + int state = wordWrap? MEASURE_STATE : DRAW_STATE; + + int startLine = -1; // Index where to begin drawing (where a line begins) + int endLine = -1; // Index where to stop drawing (where a line ends) + int lastk = -1; // Holds last value of the character position + + for (int i = 0, k = 0; i < length; i++, k++) + { + // Get next codepoint from byte string and glyph index in font + int codepointByteCount = 0; + int codepoint = GetCodepoint(&text[i], &codepointByteCount); + int index = GetGlyphIndex(font, codepoint); + + // 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 moving one byte + if (codepoint == 0x3f) codepointByteCount = 1; + i += (codepointByteCount - 1); + + float glyphWidth = 0; + if (codepoint != '\n') + { + glyphWidth = (font.chars[index].advanceX == 0) ? font.recs[index].width*scaleFactor : font.chars[index].advanceX*scaleFactor; + + if (i + 1 < length) glyphWidth = glyphWidth + spacing; + } + + // NOTE: When wordWrap is ON we first measure how much of the text we can draw before going outside of the rec container + // We store this info in startLine and endLine, then we change states, draw the text between those two variables + // and change states again and again recursively until the end of the text (or until we get outside of the container). + // When wordWrap is OFF we don't need the measure state so we go to the drawing state immediately + // and begin drawing on the next line before we can get outside 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 + // Ref: http://jkorpela.fi/chars/spaces.html + if ((codepoint == ' ') || (codepoint == '\t') || (codepoint == '\n')) endLine = i; + + if ((textOffsetX + glyphWidth) > rec.width) + { + endLine = (endLine < 1)? i : endLine; + if (i == endLine) endLine -= codepointByteCount; + if ((startLine + codepointByteCount) == endLine) endLine = (i - codepointByteCount); + + state = !state; + } + else if ((i + 1) == length) + { + endLine = i; + state = !state; + } + else if (codepoint == '\n') state = !state; + + if (state == DRAW_STATE) + { + textOffsetX = 0; + i = startLine; + glyphWidth = 0; + + // Save character position when we switch states + int tmp = lastk; + lastk = k - 1; + k = tmp; + } + } + else + { + if (codepoint == '\n') + { + if (!wordWrap) + { + textOffsetY += (font.baseSize + font.baseSize/2)*scaleFactor; + textOffsetX = 0; + } + } + else + { + if (!wordWrap && ((textOffsetX + glyphWidth) > rec.width)) + { + textOffsetY += (font.baseSize + font.baseSize/2)*scaleFactor; + textOffsetX = 0; + } + + // When text overflows rectangle height limit, just stop drawing + if ((textOffsetY + font.baseSize*scaleFactor) > rec.height) break; + + // Draw selection background + bool isGlyphSelected = false; + if ((selectStart >= 0) && (k >= selectStart) && (k < (selectStart + selectLength))) + { + DrawRectangleRec((Rectangle){ rec.x + textOffsetX - 1, rec.y + textOffsetY, glyphWidth, (float)font.baseSize*scaleFactor }, selectBackTint); + isGlyphSelected = true; + } + + // Draw current character glyph + if ((codepoint != ' ') && (codepoint != '\t')) + { + DrawTextCodepoint(font, codepoint, (Vector2){ rec.x + textOffsetX, rec.y + textOffsetY }, fontSize, isGlyphSelected? selectTint : tint); + } + } + + if (wordWrap && (i == endLine)) + { + textOffsetY += (font.baseSize + font.baseSize/2)*scaleFactor; + textOffsetX = 0; + startLine = endLine; + endLine = -1; + glyphWidth = 0; + selectStart += lastk - k; + k = lastk; + + state = !state; + } + } + + textOffsetX += glyphWidth; + } +} \ No newline at end of file diff --git a/src/camera.h b/src/camera.h index 35eda8310..084480894 100644 --- a/src/camera.h +++ b/src/camera.h @@ -94,10 +94,6 @@ } CameraProjection; #endif -#ifdef __cplusplus -extern "C" { // Prevents name mangling of functions -#endif - //---------------------------------------------------------------------------------- // Global Variables Definition //---------------------------------------------------------------------------------- @@ -106,6 +102,11 @@ extern "C" { // Prevents name mangling of functions //---------------------------------------------------------------------------------- // Module Functions Declaration //---------------------------------------------------------------------------------- + +#ifdef __cplusplus +extern "C" { // Prevents name mangling of functions +#endif + #if defined(CAMERA_STANDALONE) void SetCameraMode(Camera camera, int mode); // Set camera mode (multiple camera modes available) void UpdateCamera(Camera *camera); // Update camera position for selected mode diff --git a/src/external/par_shapes.h b/src/external/par_shapes.h index 8b65e384a..994a605a9 100644 --- a/src/external/par_shapes.h +++ b/src/external/par_shapes.h @@ -21,8 +21,7 @@ // coordinates (one per vertex). That's it! If you need something fancier, // look elsewhere. // -// The MIT License -// Copyright (c) 2015 Philip Rideout +// Distributed under the MIT License, see bottom of file. #ifndef PAR_SHAPES_H #define PAR_SHAPES_H @@ -32,8 +31,7 @@ extern "C" { #endif #include - -// Ray: commented to avoid conflict with raylib bool +// Ray (@raysan5): Commented to avoid conflict with raylib bool /* #if !defined(_MSC_VER) # include @@ -41,9 +39,9 @@ extern "C" { # if _MSC_VER >= 1800 # include # else // stdbool.h missing prior to MSVC++ 12.0 (VS2013) -//# define bool int -//# define true 1 -//# define false 0 +# define bool int +# define true 1 +# define false 0 # endif #endif */ @@ -71,6 +69,14 @@ void par_shapes_free_mesh(par_shapes_mesh*); // both 1.0, but they can easily be changed with par_shapes_scale. par_shapes_mesh* par_shapes_create_cylinder(int slices, int stacks); +// Cone is similar to cylinder but the radius diminishes to zero as Z increases. +// Again, height and radius are 1.0, but can be changed with par_shapes_scale. +par_shapes_mesh* par_shapes_create_cone(int slices, int stacks); + +// Create a disk of radius 1.0 with texture coordinates and normals by squashing +// a cone flat on the Z=0 plane. +par_shapes_mesh* par_shapes_create_parametric_disk(int slices, int stacks); + // Create a donut that sits on the Z=0 plane with the specified inner radius. // The outer radius can be controlled with par_shapes_scale. par_shapes_mesh* par_shapes_create_torus(int slices, int stacks, float radius); @@ -172,6 +178,17 @@ par_shapes_mesh* par_shapes_weld(par_shapes_mesh const*, float epsilon, // Compute smooth normals by averaging adjacent facet normals. void par_shapes_compute_normals(par_shapes_mesh* m); +// Global Config --------------------------------------------------------------- + +void par_shapes_set_epsilon_welded_normals(float epsilon); +void par_shapes_set_epsilon_degenerate_sphere(float epsilon); + +// Advanced -------------------------------------------------------------------- + +void par_shapes__compute_welded_normals(par_shapes_mesh* m); +void par_shapes__connect(par_shapes_mesh* scene, par_shapes_mesh* cylinder, + int slices); + #ifndef PAR_PI #define PAR_PI (3.14159265359) #define PAR_MIN(a, b) (a > b ? b : a) @@ -205,11 +222,15 @@ void par_shapes_compute_normals(par_shapes_mesh* m); #include #include +static float par_shapes__epsilon_welded_normals = 0.001; +static float par_shapes__epsilon_degenerate_sphere = 0.0001; + static void par_shapes__sphere(float const* uv, float* xyz, void*); static void par_shapes__hemisphere(float const* uv, float* xyz, void*); static void par_shapes__plane(float const* uv, float* xyz, void*); static void par_shapes__klein(float const* uv, float* xyz, void*); static void par_shapes__cylinder(float const* uv, float* xyz, void*); +static void par_shapes__cone(float const* uv, float* xyz, void*); static void par_shapes__torus(float const* uv, float* xyz, void*); static void par_shapes__trefoil(float const* uv, float* xyz, void*); @@ -298,11 +319,12 @@ static float par_shapes__sqrdist3(float const* a, float const* b) return dx * dx + dy * dy + dz * dz; } -static void par_shapes__compute_welded_normals(par_shapes_mesh* m) +void par_shapes__compute_welded_normals(par_shapes_mesh* m) { + const float epsilon = par_shapes__epsilon_welded_normals; m->normals = PAR_MALLOC(float, m->npoints * 3); PAR_SHAPES_T* weldmap = PAR_MALLOC(PAR_SHAPES_T, m->npoints); - par_shapes_mesh* welded = par_shapes_weld(m, 0.01, weldmap); + par_shapes_mesh* welded = par_shapes_weld(m, epsilon, weldmap); par_shapes_compute_normals(welded); float* pdst = m->normals; for (int i = 0; i < m->npoints; i++, pdst += 3) { @@ -325,6 +347,24 @@ par_shapes_mesh* par_shapes_create_cylinder(int slices, int stacks) stacks, 0); } +par_shapes_mesh* par_shapes_create_cone(int slices, int stacks) +{ + if (slices < 3 || stacks < 1) { + return 0; + } + return par_shapes_create_parametric(par_shapes__cone, slices, + stacks, 0); +} + +par_shapes_mesh* par_shapes_create_parametric_disk(int slices, int stacks) +{ + par_shapes_mesh* m = par_shapes_create_cone(slices, stacks); + if (m) { + par_shapes_scale(m, 1.0f, 1.0f, 0.0f); + } + return m; +} + par_shapes_mesh* par_shapes_create_parametric_sphere(int slices, int stacks) { if (slices < 3 || stacks < 3) { @@ -332,7 +372,7 @@ par_shapes_mesh* par_shapes_create_parametric_sphere(int slices, int stacks) } par_shapes_mesh* m = par_shapes_create_parametric(par_shapes__sphere, slices, stacks, 0); - par_shapes_remove_degenerate(m, 0.0001); + par_shapes_remove_degenerate(m, par_shapes__epsilon_degenerate_sphere); return m; } @@ -343,7 +383,7 @@ par_shapes_mesh* par_shapes_create_hemisphere(int slices, int stacks) } par_shapes_mesh* m = par_shapes_create_parametric(par_shapes__hemisphere, slices, stacks, 0); - par_shapes_remove_degenerate(m, 0.0001); + par_shapes_remove_degenerate(m, par_shapes__epsilon_degenerate_sphere); return m; } @@ -579,6 +619,15 @@ static void par_shapes__cylinder(float const* uv, float* xyz, void* userdata) xyz[2] = uv[0]; } +static void par_shapes__cone(float const* uv, float* xyz, void* userdata) +{ + float r = 1.0f - uv[0]; + float theta = uv[1] * 2 * PAR_PI; + xyz[0] = r * sinf(theta); + xyz[1] = r * cosf(theta); + xyz[2] = uv[0]; +} + static void par_shapes__torus(float const* uv, float* xyz, void* userdata) { float major = 1; @@ -620,6 +669,14 @@ static void par_shapes__trefoil(float const* uv, float* xyz, void* userdata) xyz[2] = z + d * ww[2] * sin(v); } +void par_shapes_set_epsilon_welded_normals(float epsilon) { + par_shapes__epsilon_welded_normals = epsilon; +} + +void par_shapes_set_epsilon_degenerate_sphere(float epsilon) { + par_shapes__epsilon_degenerate_sphere = epsilon; +} + void par_shapes_merge(par_shapes_mesh* dst, par_shapes_mesh const* src) { PAR_SHAPES_T offset = dst->npoints; @@ -744,15 +801,15 @@ void par_shapes_rotate(par_shapes_mesh* mesh, float radians, float const* axis) p[1] = y; p[2] = z; } - p = mesh->normals; - if (p) { - for (int i = 0; i < mesh->npoints; i++, p += 3) { - float x = col0[0] * p[0] + col1[0] * p[1] + col2[0] * p[2]; - float y = col0[1] * p[0] + col1[1] * p[1] + col2[1] * p[2]; - float z = col0[2] * p[0] + col1[2] * p[1] + col2[2] * p[2]; - p[0] = x; - p[1] = y; - p[2] = z; + float* n = mesh->normals; + if (n) { + for (int i = 0; i < mesh->npoints; i++, n += 3) { + float x = col0[0] * n[0] + col1[0] * n[1] + col2[0] * n[2]; + float y = col0[1] * n[0] + col1[1] * n[1] + col2[1] * n[2]; + float z = col0[2] * n[0] + col1[2] * n[1] + col2[2] * n[2]; + n[0] = x; + n[1] = y; + n[2] = z; } } } @@ -765,6 +822,27 @@ void par_shapes_scale(par_shapes_mesh* m, float x, float y, float z) *points++ *= y; *points++ *= z; } + float* n = m->normals; + if (n && !(x == y && y == z)) { + bool x_zero = x == 0; + bool y_zero = y == 0; + bool z_zero = z == 0; + if (!x_zero && !y_zero && !z_zero) { + x = 1.0f / x; + y = 1.0f / y; + z = 1.0f / z; + } else { + x = x_zero && !y_zero && !z_zero; + y = y_zero && !x_zero && !z_zero; + z = z_zero && !x_zero && !y_zero; + } + for (int i = 0; i < m->npoints; i++, n += 3) { + n[0] *= x; + n[1] *= y; + n[2] *= z; + par_shapes__normalize3(n); + } + } } void par_shapes_merge_and_free(par_shapes_mesh* dst, par_shapes_mesh* src) @@ -1098,8 +1176,8 @@ static par_shapes_mesh* par_shapes__apply_turtle(par_shapes_mesh* mesh, return m; } -static void par_shapes__connect(par_shapes_mesh* scene, - par_shapes_mesh* cylinder, int slices) +void par_shapes__connect(par_shapes_mesh* scene, par_shapes_mesh* cylinder, + int slices) { int stacks = 1; int npoints = (slices + 1) * (stacks + 1); @@ -1118,7 +1196,8 @@ static void par_shapes__connect(par_shapes_mesh* scene, // Create the new triangle list. int ntriangles = scene->ntriangles + 2 * slices * stacks; PAR_SHAPES_T* triangles = PAR_MALLOC(PAR_SHAPES_T, ntriangles * 3); - memcpy(triangles, scene->triangles, 2 * scene->ntriangles * 3); + memcpy(triangles, scene->triangles, + sizeof(PAR_SHAPES_T) * scene->ntriangles * 3); int v = scene->npoints - (slices + 1); PAR_SHAPES_T* face = triangles + scene->ntriangles * 3; for (int stack = 0; stack < stacks; stack++) { @@ -1154,7 +1233,7 @@ par_shapes_mesh* par_shapes_create_lsystem(char const* text, int slices, while (cmd) { char *arg = strtok(0, " "); if (!arg) { - //puts("lsystem error: unexpected end of program."); + puts("lsystem error: unexpected end of program."); break; } if (!strcmp(cmd, "rule")) { @@ -1208,7 +1287,6 @@ par_shapes_mesh* par_shapes_create_lsystem(char const* text, int slices, // For testing purposes, dump out the parsed program. #ifdef TEST_PARSE - /* for (int i = 0; i < nrules; i++) { par_shapes__rule rule = rules[i]; printf("rule %s.%d\n", rule.name, rule.weight); @@ -1217,7 +1295,6 @@ par_shapes_mesh* par_shapes_create_lsystem(char const* text, int slices, printf("\t%s %s\n", cmd.cmd, cmd.arg); } } - */ #endif // Instantiate the aggregated shape and the template shapes. @@ -1258,7 +1335,8 @@ par_shapes_mesh* par_shapes_create_lsystem(char const* text, int slices, par_shapes__command* cmd = rule->commands + (frame->pc++); #ifdef DUMP_TRACE - //printf("%5s %5s %5s:%d %03d\n", cmd->cmd, cmd->arg, rule->name, frame->pc - 1, stackptr); + printf("%5s %5s %5s:%d %03d\n", cmd->cmd, cmd->arg, rule->name, + frame->pc - 1, stackptr); #endif float value; @@ -1620,7 +1698,7 @@ static void par_shapes__weld_points(par_shapes_mesh* mesh, int gridsize, PAR_SHAPES_T binvalue = *(bins + binindex); if (binvalue > 0) { if (nbins == 8) { - //printf("Epsilon value is too large.\n"); + printf("Epsilon value is too large.\n"); break; } nearby[nbins++] = binindex; @@ -1632,8 +1710,9 @@ static void par_shapes__weld_points(par_shapes_mesh* mesh, int gridsize, // Check for colocated points in each nearby bin. for (int b = 0; b < nbins; b++) { int binindex = nearby[b]; - PAR_SHAPES_T binvalue = *(bins + binindex); + PAR_SHAPES_T binvalue = bins[binindex]; PAR_SHAPES_T nindex = binvalue - 1; + assert(nindex < mesh->npoints); while (true) { // If this isn't "self" and it's colocated, then weld it! @@ -1699,6 +1778,9 @@ static void par_shapes__weld_points(par_shapes_mesh* mesh, int gridsize, PAR_SHAPES_T b = weldmap[tsrc[1]]; PAR_SHAPES_T c = weldmap[tsrc[2]]; if (a != b && a != c && b != c) { + assert(a < mesh->npoints); + assert(b < mesh->npoints); + assert(c < mesh->npoints); *tdst++ = a; *tdst++ = b; *tdst++ = c; @@ -2049,3 +2131,25 @@ void par_shapes_remove_degenerate(par_shapes_mesh* mesh, float mintriarea) #endif // PAR_SHAPES_IMPLEMENTATION #endif // PAR_SHAPES_H + +// par_shapes is distributed under the MIT license: +// +// Copyright (c) 2019 Philip Rideout +// +// 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. diff --git a/src/extras/physac.h b/src/extras/physac.h index 7bcdb0f47..642c25a72 100644 --- a/src/extras/physac.h +++ b/src/extras/physac.h @@ -16,16 +16,9 @@ * If not defined, the library is in header only mode and can be included in other headers * or source files without problems. But only ONE file should hold the implementation. * -* #define PHYSAC_STATIC (defined by default) -* The generated implementation will stay private inside implementation file and all -* internal symbols and functions will only be visible inside that file. -* * #define PHYSAC_DEBUG * Show debug traces log messages about physic bodies creation/destruction, physic system errors, -* some calculations results and NULL reference exceptions -* -* #define PHYSAC_DEFINE_VECTOR2_TYPE -* Forces library to define struct Vector2 data type (float x; float y) +* some calculations results and NULL reference exceptions. * * #define PHYSAC_AVOID_TIMMING_SYSTEM * Disables internal timming system, used by UpdatePhysics() to launch timmed physic steps, @@ -79,14 +72,8 @@ #if !defined(PHYSAC_H) #define PHYSAC_H -#if defined(PHYSAC_STATIC) - #define PHYSACDEF static // Functions just visible to module including this file -#else - #if defined(__cplusplus) - #define PHYSACDEF extern "C" // Functions visible from other files (no name mangling of functions in C++) - #else - #define PHYSACDEF extern // Functions visible from other files - #endif +#ifndef PHYSACDEF + #define PHYSACDEF // We are building or using physac as a static library #endif // Allow custom memory allocators @@ -127,7 +114,7 @@ typedef enum PhysicsShapeType { PHYSICS_CIRCLE = 0, PHYSICS_POLYGON } PhysicsSha // Previously defined to be used in PhysicsShape struct as circular dependencies typedef struct PhysicsBodyData *PhysicsBody; -#if defined(PHYSAC_DEFINE_VECTOR2_TYPE) +#if !defined(RL_VECTOR2_TYPE) // Vector2 type typedef struct Vector2 { float x; @@ -192,13 +179,13 @@ typedef struct PhysicsManifoldData { float staticFriction; // Mixed static friction during collision } PhysicsManifoldData, *PhysicsManifold; -#if defined(__cplusplus) -extern "C" { // Prevents name mangling of functions -#endif - //---------------------------------------------------------------------------------- // Module Functions Declaration //---------------------------------------------------------------------------------- + +#if defined(__cplusplus) +extern "C" { // Prevents name mangling of functions +#endif // Physics system management PHYSACDEF void InitPhysics(void); // Initializes physics system PHYSACDEF void UpdatePhysics(void); // Update physics system @@ -225,7 +212,6 @@ PHYSACDEF int GetPhysicsBodiesCount(void); PHYSACDEF int GetPhysicsShapeType(int index); // Returns the physics body shape type (PHYSICS_CIRCLE or PHYSICS_POLYGON) PHYSACDEF int GetPhysicsShapeVerticesCount(int index); // Returns the amount of vertices of a physics body shape PHYSACDEF Vector2 GetPhysicsShapeVertex(PhysicsBody body, int vertex); // Returns transformed position of a body shape (body position + vertex transformed position) - #if defined(__cplusplus) } #endif @@ -253,11 +239,17 @@ PHYSACDEF Vector2 GetPhysicsShapeVertex(PhysicsBody body, int vertex); #if !defined(PHYSAC_AVOID_TIMMING_SYSTEM) // Time management functionality - #include // Required for: time(), clock_gettime() + #include // Required for: time(), clock_gettime() #if defined(_WIN32) + #if defined(__cplusplus) + extern "C" { // Prevents name mangling of functions + #endif // Functions required to query time on Windows int __stdcall QueryPerformanceCounter(unsigned long long int *lpPerformanceCount); int __stdcall QueryPerformanceFrequency(unsigned long long int *lpFrequency); + #if defined(__cplusplus) + } + #endif #endif #if defined(__linux__) || defined(__FreeBSD__) #if _POSIX_C_SOURCE < 199309L @@ -366,7 +358,7 @@ static Vector2 MathTriangleBarycenter(Vector2 v1, Vector2 v2, Vector2 v3); //---------------------------------------------------------------------------------- // Initializes physics values, pointers and creates physics loop thread -PHYSACDEF void InitPhysics(void) +void InitPhysics(void) { #if !defined(PHYSAC_AVOID_TIMMING_SYSTEM) // Initialize high resolution timer @@ -377,21 +369,21 @@ PHYSACDEF void InitPhysics(void) } // Sets physics global gravity force -PHYSACDEF void SetPhysicsGravity(float x, float y) +void SetPhysicsGravity(float x, float y) { gravityForce.x = x; gravityForce.y = y; } // Creates a new circle physics body with generic parameters -PHYSACDEF PhysicsBody CreatePhysicsBodyCircle(Vector2 pos, float radius, float density) +PhysicsBody CreatePhysicsBodyCircle(Vector2 pos, float radius, float density) { PhysicsBody body = CreatePhysicsBodyPolygon(pos, radius, PHYSAC_DEFAULT_CIRCLE_VERTICES, density); return body; } // Creates a new rectangle physics body with generic parameters -PHYSACDEF PhysicsBody CreatePhysicsBodyRectangle(Vector2 pos, float width, float height, float density) +PhysicsBody CreatePhysicsBodyRectangle(Vector2 pos, float width, float height, float density) { // NOTE: Make sure body data is initialized to 0 PhysicsBody body = (PhysicsBody)PHYSAC_CALLOC(sizeof(PhysicsBodyData), 1); @@ -469,7 +461,7 @@ PHYSACDEF PhysicsBody CreatePhysicsBodyRectangle(Vector2 pos, float width, float } // Creates a new polygon physics body with generic parameters -PHYSACDEF PhysicsBody CreatePhysicsBodyPolygon(Vector2 pos, float radius, int sides, float density) +PhysicsBody CreatePhysicsBodyPolygon(Vector2 pos, float radius, int sides, float density) { PhysicsBody body = (PhysicsBody)PHYSAC_MALLOC(sizeof(PhysicsBodyData)); usedMemory += sizeof(PhysicsBodyData); @@ -551,19 +543,19 @@ PHYSACDEF PhysicsBody CreatePhysicsBodyPolygon(Vector2 pos, float radius, int si } // Adds a force to a physics body -PHYSACDEF void PhysicsAddForce(PhysicsBody body, Vector2 force) +void PhysicsAddForce(PhysicsBody body, Vector2 force) { if (body != NULL) body->force = MathVector2Add(body->force, force); } // Adds an angular force to a physics body -PHYSACDEF void PhysicsAddTorque(PhysicsBody body, float amount) +void PhysicsAddTorque(PhysicsBody body, float amount) { if (body != NULL) body->torque += amount; } // Shatters a polygon shape physics body to little physics bodies with explosion force -PHYSACDEF void PhysicsShatter(PhysicsBody body, Vector2 position, float force) +void PhysicsShatter(PhysicsBody body, Vector2 position, float force) { if (body != NULL) { @@ -700,13 +692,13 @@ PHYSACDEF void PhysicsShatter(PhysicsBody body, Vector2 position, float force) } // Returns the current amount of created physics bodies -PHYSACDEF int GetPhysicsBodiesCount(void) +int GetPhysicsBodiesCount(void) { return physicsBodiesCount; } // Returns a physics body of the bodies pool at a specific index -PHYSACDEF PhysicsBody GetPhysicsBody(int index) +PhysicsBody GetPhysicsBody(int index) { PhysicsBody body = NULL; @@ -722,7 +714,7 @@ PHYSACDEF PhysicsBody GetPhysicsBody(int index) } // Returns the physics body shape type (PHYSICS_CIRCLE or PHYSICS_POLYGON) -PHYSACDEF int GetPhysicsShapeType(int index) +int GetPhysicsShapeType(int index) { int result = -1; @@ -739,7 +731,7 @@ PHYSACDEF int GetPhysicsShapeType(int index) } // Returns the amount of vertices of a physics body shape -PHYSACDEF int GetPhysicsShapeVerticesCount(int index) +int GetPhysicsShapeVerticesCount(int index) { int result = 0; @@ -764,7 +756,7 @@ PHYSACDEF int GetPhysicsShapeVerticesCount(int index) } // Returns transformed position of a body shape (body position + vertex transformed position) -PHYSACDEF Vector2 GetPhysicsShapeVertex(PhysicsBody body, int vertex) +Vector2 GetPhysicsShapeVertex(PhysicsBody body, int vertex) { Vector2 position = { 0.0f, 0.0f }; @@ -791,7 +783,7 @@ PHYSACDEF Vector2 GetPhysicsShapeVertex(PhysicsBody body, int vertex) } // Sets physics body shape transform based on radians parameter -PHYSACDEF void SetPhysicsBodyRotation(PhysicsBody body, float radians) +void SetPhysicsBodyRotation(PhysicsBody body, float radians) { if (body != NULL) { @@ -802,7 +794,7 @@ PHYSACDEF void SetPhysicsBodyRotation(PhysicsBody body, float radians) } // Unitializes and destroys a physics body -PHYSACDEF void DestroyPhysicsBody(PhysicsBody body) +void DestroyPhysicsBody(PhysicsBody body) { if (body != NULL) { @@ -844,7 +836,7 @@ PHYSACDEF void DestroyPhysicsBody(PhysicsBody body) } // Destroys created physics bodies and manifolds and resets global values -PHYSACDEF void ResetPhysics(void) +void ResetPhysics(void) { if (physicsBodiesCount > 0) { @@ -886,7 +878,7 @@ PHYSACDEF void ResetPhysics(void) } // Unitializes physics pointers and exits physics loop thread -PHYSACDEF void ClosePhysics(void) +void ClosePhysics(void) { // Unitialize physics manifolds dynamic memory allocations if (physicsManifoldsCount > 0) @@ -912,91 +904,98 @@ PHYSACDEF void ClosePhysics(void) else TRACELOG("[PHYSAC] Physics module closed successfully\n"); } +// Update physics system +// Physics steps are launched at a fixed time step if enabled +void UpdatePhysics(void) +{ +#if !defined(PHYSAC_AVOID_TIMMING_SYSTEM) + static double deltaTimeAccumulator = 0.0; + + // Calculate current time (ms) + currentTime = GetCurrentTime(); + + // Calculate current delta time (ms) + const double delta = currentTime - startTime; + + // Store the time elapsed since the last frame began + deltaTimeAccumulator += delta; + + // Fixed time stepping loop + while (deltaTimeAccumulator >= deltaTime) + { + UpdatePhysicsStep(); + deltaTimeAccumulator -= deltaTime; + } + + // Record the starting of this frame + startTime = currentTime; +#else + UpdatePhysicsStep(); +#endif +} + +void SetPhysicsTimeStep(double delta) +{ + deltaTime = delta; +} + //---------------------------------------------------------------------------------- // Module Internal Functions Definition //---------------------------------------------------------------------------------- -// Finds a valid index for a new physics body initialization -static int FindAvailableBodyIndex() +#if !defined(PHYSAC_AVOID_TIMMING_SYSTEM) +// Initializes hi-resolution MONOTONIC timer +static void InitTimerHiRes(void) { - int index = -1; - for (int i = 0; i < PHYSAC_MAX_BODIES; i++) - { - int currentId = i; +#if defined(_WIN32) + QueryPerformanceFrequency((unsigned long long int *) &frequency); +#endif - // Check if current id already exist in other physics body - for (unsigned int k = 0; k < physicsBodiesCount; k++) - { - if (bodies[k]->id == currentId) - { - currentId++; - break; - } - } +#if defined(__EMSCRIPTEN__) || defined(__linux__) + struct timespec now; + if (clock_gettime(CLOCK_MONOTONIC, &now) == 0) frequency = 1000000000; +#endif - // If it is not used, use it as new physics body id - if (currentId == (int)i) - { - index = (int)i; - break; - } - } +#if defined(__APPLE__) + mach_timebase_info_data_t timebase; + mach_timebase_info(&timebase); + frequency = (timebase.denom*1e9)/timebase.numer; +#endif - return index; + baseClockTicks = (double)GetClockTicks(); // Get MONOTONIC clock time offset + startTime = GetCurrentTime(); // Get current time in milliseconds } -// Creates a default polygon shape with max vertex distance from polygon pivot -static PhysicsVertexData CreateDefaultPolygon(float radius, int sides) +// Get hi-res MONOTONIC time measure in clock ticks +static unsigned long long int GetClockTicks(void) { - PhysicsVertexData data = { 0 }; - data.vertexCount = sides; + unsigned long long int value = 0; - // Calculate polygon vertices positions - for (unsigned int i = 0; i < data.vertexCount; i++) - { - data.positions[i].x = (float)cosf(360.0f/sides*i*PHYSAC_DEG2RAD)*radius; - data.positions[i].y = (float)sinf(360.0f/sides*i*PHYSAC_DEG2RAD)*radius; - } +#if defined(_WIN32) + QueryPerformanceCounter((unsigned long long int *) &value); +#endif - // Calculate polygon faces normals - for (int i = 0; i < (int)data.vertexCount; i++) - { - int nextIndex = (((i + 1) < sides) ? (i + 1) : 0); - Vector2 face = MathVector2Subtract(data.positions[nextIndex], data.positions[i]); +#if defined(__linux__) + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + value = (unsigned long long int)now.tv_sec*(unsigned long long int)1000000000 + (unsigned long long int)now.tv_nsec; +#endif - data.normals[i] = CLITERAL(Vector2){ face.y, -face.x }; - MathVector2Normalize(&data.normals[i]); - } +#if defined(__APPLE__) + value = mach_absolute_time(); +#endif - return data; + return value; } -// Creates a rectangle polygon shape based on a min and max positions -static PhysicsVertexData CreateRectanglePolygon(Vector2 pos, Vector2 size) +// Get current time in milliseconds +static double GetCurrentTime(void) { - PhysicsVertexData data = { 0 }; - data.vertexCount = 4; - - // Calculate polygon vertices positions - data.positions[0] = CLITERAL(Vector2){ pos.x + size.x/2, pos.y - size.y/2 }; - data.positions[1] = CLITERAL(Vector2){ pos.x + size.x/2, pos.y + size.y/2 }; - data.positions[2] = CLITERAL(Vector2){ pos.x - size.x/2, pos.y + size.y/2 }; - data.positions[3] = CLITERAL(Vector2){ pos.x - size.x/2, pos.y - size.y/2 }; - - // Calculate polygon faces normals - for (unsigned int i = 0; i < data.vertexCount; i++) - { - int nextIndex = (((i + 1) < data.vertexCount) ? (i + 1) : 0); - Vector2 face = MathVector2Subtract(data.positions[nextIndex], data.positions[i]); - - data.normals[i] = CLITERAL(Vector2){ face.y, -face.x }; - MathVector2Normalize(&data.normals[i]); - } - - return data; + return (double)(GetClockTicks() - baseClockTicks)/frequency*1000; } +#endif // !PHYSAC_AVOID_TIMMING_SYSTEM // Update physics step (dynamics, collisions and position corrections) -void UpdatePhysicsStep(void) +static void UpdatePhysicsStep(void) { // Clear previous generated collisions information for (int i = (int)physicsManifoldsCount - 1; i >= 0; i--) @@ -1098,39 +1097,84 @@ void UpdatePhysicsStep(void) } } -// Update physics system -// Physics steps are launched at a fixed time step if enabled -PHYSACDEF void UpdatePhysics(void) +// Finds a valid index for a new physics body initialization +static int FindAvailableBodyIndex() { -#if !defined(PHYSAC_AVOID_TIMMING_SYSTEM) - static double deltaTimeAccumulator = 0.0; - - // Calculate current time (ms) - currentTime = GetCurrentTime(); - - // Calculate current delta time (ms) - const double delta = currentTime - startTime; - - // Store the time elapsed since the last frame began - deltaTimeAccumulator += delta; - - // Fixed time stepping loop - while (deltaTimeAccumulator >= deltaTime) + int index = -1; + for (int i = 0; i < PHYSAC_MAX_BODIES; i++) { - UpdatePhysicsStep(); - deltaTimeAccumulator -= deltaTime; + int currentId = i; + + // Check if current id already exist in other physics body + for (unsigned int k = 0; k < physicsBodiesCount; k++) + { + if (bodies[k]->id == currentId) + { + currentId++; + break; + } + } + + // If it is not used, use it as new physics body id + if (currentId == (int)i) + { + index = (int)i; + break; + } } - // Record the starting of this frame - startTime = currentTime; -#else - UpdatePhysicsStep(); -#endif + return index; } -PHYSACDEF void SetPhysicsTimeStep(double delta) +// Creates a default polygon shape with max vertex distance from polygon pivot +static PhysicsVertexData CreateDefaultPolygon(float radius, int sides) { - deltaTime = delta; + PhysicsVertexData data = { 0 }; + data.vertexCount = sides; + + // Calculate polygon vertices positions + for (unsigned int i = 0; i < data.vertexCount; i++) + { + data.positions[i].x = (float)cosf(360.0f/sides*i*PHYSAC_DEG2RAD)*radius; + data.positions[i].y = (float)sinf(360.0f/sides*i*PHYSAC_DEG2RAD)*radius; + } + + // Calculate polygon faces normals + for (int i = 0; i < (int)data.vertexCount; i++) + { + int nextIndex = (((i + 1) < sides) ? (i + 1) : 0); + Vector2 face = MathVector2Subtract(data.positions[nextIndex], data.positions[i]); + + data.normals[i] = CLITERAL(Vector2){ face.y, -face.x }; + MathVector2Normalize(&data.normals[i]); + } + + return data; +} + +// Creates a rectangle polygon shape based on a min and max positions +static PhysicsVertexData CreateRectanglePolygon(Vector2 pos, Vector2 size) +{ + PhysicsVertexData data = { 0 }; + data.vertexCount = 4; + + // Calculate polygon vertices positions + data.positions[0] = CLITERAL(Vector2){ pos.x + size.x/2, pos.y - size.y/2 }; + data.positions[1] = CLITERAL(Vector2){ pos.x + size.x/2, pos.y + size.y/2 }; + data.positions[2] = CLITERAL(Vector2){ pos.x - size.x/2, pos.y + size.y/2 }; + data.positions[3] = CLITERAL(Vector2){ pos.x - size.x/2, pos.y - size.y/2 }; + + // Calculate polygon faces normals + for (unsigned int i = 0; i < data.vertexCount; i++) + { + int nextIndex = (((i + 1) < data.vertexCount) ? (i + 1) : 0); + Vector2 face = MathVector2Subtract(data.positions[nextIndex], data.positions[i]); + + data.normals[i] = CLITERAL(Vector2){ face.y, -face.x }; + MathVector2Normalize(&data.normals[i]); + } + + return data; } // Finds a valid index for a new manifold initialization @@ -1844,59 +1888,6 @@ static Vector2 MathTriangleBarycenter(Vector2 v1, Vector2 v2, Vector2 v3) return result; } -#if !defined(PHYSAC_AVOID_TIMMING_SYSTEM) -// Initializes hi-resolution MONOTONIC timer -static void InitTimerHiRes(void) -{ -#if defined(_WIN32) - QueryPerformanceFrequency((unsigned long long int *) &frequency); -#endif - -#if defined(__EMSCRIPTEN__) || defined(__linux__) - struct timespec now; - if (clock_gettime(CLOCK_MONOTONIC, &now) == 0) frequency = 1000000000; -#endif - -#if defined(__APPLE__) - mach_timebase_info_data_t timebase; - mach_timebase_info(&timebase); - frequency = (timebase.denom*1e9)/timebase.numer; -#endif - - baseClockTicks = (double)GetClockTicks(); // Get MONOTONIC clock time offset - startTime = GetCurrentTime(); // Get current time in milliseconds -} - -// Get hi-res MONOTONIC time measure in clock ticks -static unsigned long long int GetClockTicks(void) -{ - unsigned long long int value = 0; - -#if defined(_WIN32) - QueryPerformanceCounter((unsigned long long int *) &value); -#endif - -#if defined(__linux__) - struct timespec now; - clock_gettime(CLOCK_MONOTONIC, &now); - value = (unsigned long long int)now.tv_sec*(unsigned long long int)1000000000 + (unsigned long long int)now.tv_nsec; -#endif - -#if defined(__APPLE__) - value = mach_absolute_time(); -#endif - - return value; -} - -// Get current time in milliseconds -static double GetCurrentTime(void) -{ - return (double)(GetClockTicks() - baseClockTicks)/frequency*1000; -} -#endif // !PHYSAC_AVOID_TIMMING_SYSTEM - - // Returns the cross product of a vector and a value static inline Vector2 MathVector2Product(Vector2 vector, float value) { diff --git a/src/gestures.h b/src/gestures.h index 2ed9f8fd9..355f60817 100644 --- a/src/gestures.h +++ b/src/gestures.h @@ -99,10 +99,6 @@ typedef struct { Vector2 position[4]; } GestureEvent; -#ifdef __cplusplus -extern "C" { // Prevents name mangling of functions -#endif - //---------------------------------------------------------------------------------- // Global Variables Definition //---------------------------------------------------------------------------------- @@ -111,11 +107,15 @@ extern "C" { // Prevents name mangling of functions //---------------------------------------------------------------------------------- // Module Functions Declaration //---------------------------------------------------------------------------------- + +#ifdef __cplusplus +extern "C" { // Prevents name mangling of functions +#endif + void ProcessGestureEvent(GestureEvent event); // Process gesture event and translate it into gestures void UpdateGestures(void); // Update gestures detected (must be called every frame) - #if defined(GESTURES_STANDALONE) -void SetGesturesEnabled(unsigned int flags); // Enable a set of gestures using flags +void SetGesturesEnabled(unsigned int flags); // Enable a set of gestures using flags bool IsGestureDetected(int gesture); // Check if a gesture have been detected int GetGestureDetected(void); // Get latest detected gesture int GetTouchPointsCount(void); // Get touch points count @@ -141,9 +141,15 @@ float GetGesturePinchAngle(void); // Get gesture pinch ang #if defined(GESTURES_IMPLEMENTATION) #if defined(_WIN32) + #if defined(__cplusplus) + extern "C" { // Prevents name mangling of functions + #endif // Functions required to query time on Windows int __stdcall QueryPerformanceCounter(unsigned long long int *lpPerformanceCount); int __stdcall QueryPerformanceFrequency(unsigned long long int *lpFrequency); + #if defined(__cplusplus) + } + #endif #elif defined(__linux__) #if _POSIX_C_SOURCE < 199309L #undef _POSIX_C_SOURCE diff --git a/src/models.c b/src/models.c index bce9c8d82..fd7daee6f 100644 --- a/src/models.c +++ b/src/models.c @@ -2084,6 +2084,61 @@ Mesh GenMeshCylinder(float radius, float height, int slices) return mesh; } +// Generate cone/pyramid mesh +Mesh GenMeshCone(float radius, float height, int slices) +{ + Mesh mesh = { 0 }; + + if (slices >= 3) + { + // Instance a cone that sits on the Z=0 plane using the given tessellation + // levels across the UV domain. Think of "slices" like a number of pizza + // slices, and "stacks" like a number of stacked rings. + // Height and radius are both 1.0, but they can easily be changed with par_shapes_scale + par_shapes_mesh *cone = par_shapes_create_cone(slices, 8); + par_shapes_scale(cone, radius, radius, height); + par_shapes_rotate(cone, -PI/2.0f, (float[]){ 1, 0, 0 }); + par_shapes_rotate(cone, PI/2.0f, (float[]){ 0, 1, 0 }); + + // Generate an orientable disk shape (bottom cap) + par_shapes_mesh *capBottom = par_shapes_create_disk(radius, slices, (float[]){ 0, 0, 0 }, (float[]){ 0, 0, -1 }); + capBottom->tcoords = PAR_MALLOC(float, 2*capBottom->npoints); + for (int i = 0; i < 2*capBottom->npoints; i++) capBottom->tcoords[i] = 0.95f; + par_shapes_rotate(capBottom, PI/2.0f, (float[]){ 1, 0, 0 }); + + par_shapes_merge_and_free(cone, capBottom); + + mesh.vertices = (float *)RL_MALLOC(cone->ntriangles*3*3*sizeof(float)); + mesh.texcoords = (float *)RL_MALLOC(cone->ntriangles*3*2*sizeof(float)); + mesh.normals = (float *)RL_MALLOC(cone->ntriangles*3*3*sizeof(float)); + + mesh.vertexCount = cone->ntriangles*3; + mesh.triangleCount = cone->ntriangles; + + for (int k = 0; k < mesh.vertexCount; k++) + { + mesh.vertices[k*3] = cone->points[cone->triangles[k]*3]; + mesh.vertices[k*3 + 1] = cone->points[cone->triangles[k]*3 + 1]; + mesh.vertices[k*3 + 2] = cone->points[cone->triangles[k]*3 + 2]; + + mesh.normals[k*3] = cone->normals[cone->triangles[k]*3]; + mesh.normals[k*3 + 1] = cone->normals[cone->triangles[k]*3 + 1]; + mesh.normals[k*3 + 2] = cone->normals[cone->triangles[k]*3 + 2]; + + mesh.texcoords[k*2] = cone->tcoords[cone->triangles[k]*2]; + mesh.texcoords[k*2 + 1] = cone->tcoords[cone->triangles[k]*2 + 1]; + } + + par_shapes_free_mesh(cone); + + // Upload vertex data to GPU (static mesh) + UploadMesh(&mesh, false); + } + else TRACELOG(LOG_WARNING, "MESH: Failed to generate mesh: cone"); + + return mesh; +} + // Generate torus mesh Mesh GenMeshTorus(float radius, float size, int radSeg, int sides) { @@ -3123,11 +3178,10 @@ RayCollision GetRayCollisionBox(Ray ray, BoundingBox box) collision.normal = Vector3Scale(collision.normal, 2.01f); collision.normal = Vector3Divide(collision.normal, Vector3Subtract(box.max, box.min)); // The relevant elemets of the vector are now slightly larger than 1.0f (or smaller than -1.0f) - // and the others are somewhere between -1.0 and 1.0 - // casting to int is exactly our wanted normal! - collision.normal.x = (int)collision.normal.x; - collision.normal.y = (int)collision.normal.y; - collision.normal.z = (int)collision.normal.z; + // and the others are somewhere between -1.0 and 1.0 casting to int is exactly our wanted normal! + collision.normal.x = (float)((int)collision.normal.x); + collision.normal.y = (float)((int)collision.normal.y); + collision.normal.z = (float)((int)collision.normal.z); collision.normal = Vector3Normalize(collision.normal); @@ -3316,7 +3370,7 @@ static Model LoadOBJ(const char *fileName) if (ret != TINYOBJ_SUCCESS) TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to load OBJ data", fileName); else TRACELOG(LOG_INFO, "MODEL: [%s] OBJ data loaded successfully: %i meshes/%i materials", fileName, meshCount, materialCount); - model.meshCount = materialCount; // TODO: REVIEW!!! + model.meshCount = materialCount; // Init model materials array if (materialCount > 0) @@ -3335,7 +3389,7 @@ static Model LoadOBJ(const char *fileName) model.meshMaterial = (int *)RL_CALLOC(model.meshCount, sizeof(int)); // Count the faces for each material - int *matFaces = RL_CALLOC(meshCount, sizeof(int)); + int *matFaces = RL_CALLOC(materialCount, sizeof(int)); for (unsigned int mi = 0; mi < meshCount; mi++) { @@ -3346,7 +3400,6 @@ static Model LoadOBJ(const char *fileName) matFaces[idx]++; } } - //-------------------------------------- // Create the material meshes @@ -4937,7 +4990,7 @@ static ModelAnimation *LoadGLTFModelAnimations(const char *fileName, int *animCo { output->framePoses[frame] = RL_MALLOC(output->boneCount*sizeof(Transform)); - for (unsigned int i = 0; i < output->boneCount; i++) + for (int i = 0; i < output->boneCount; i++) { if (data->nodes[i].has_translation) memcpy(&output->framePoses[frame][i].translation, data->nodes[i].translation, 3*sizeof(float)); else output->framePoses[frame][i].translation = Vector3Zero(); @@ -5130,7 +5183,7 @@ void LoadGLTFMesh(cgltf_data* data, cgltf_mesh* mesh, Model* outModel, Matrix cu outModel->meshes[(*primitiveIndex)].vertices = ReadGLTFValuesAs(acc, cgltf_component_type_r_32f, false); // Transform using the nodes matrix attributes - for (unsigned int v = 0; v < outModel->meshes[(*primitiveIndex)].vertexCount; v++) + for (int v = 0; v < outModel->meshes[(*primitiveIndex)].vertexCount; v++) { Vector3 vertex = { outModel->meshes[(*primitiveIndex)].vertices[(v*3 + 0)], @@ -5156,7 +5209,7 @@ void LoadGLTFMesh(cgltf_data* data, cgltf_mesh* mesh, Model* outModel, Matrix cu outModel->meshes[(*primitiveIndex)].normals = ReadGLTFValuesAs(acc, cgltf_component_type_r_32f, false); // Transform using the nodes matrix attributes - for (unsigned int v = 0; v < outModel->meshes[(*primitiveIndex)].vertexCount; v++) + for (int v = 0; v < outModel->meshes[(*primitiveIndex)].vertexCount; v++) { Vector3 normal = { outModel->meshes[(*primitiveIndex)].normals[(v*3 + 0)], diff --git a/src/raylib.h b/src/raylib.h index 781c14202..2e963ddc5 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -81,19 +81,18 @@ #include // Required for: va_list - Only used by TraceLogCallback -#define RAYLIB_VERSION "3.8-dev" +#define RAYLIB_VERSION "4.0-dev" + +#ifndef RLAPI + #define RLAPI // We are building or using rlgl as a static library (or Linux shared library) +#endif #if defined(_WIN32) - // Microsoft attibutes to tell compiler that symbols are imported/exported from a .dll #if defined(BUILD_LIBTYPE_SHARED) #define RLAPI __declspec(dllexport) // We are building raylib as a Win32 shared library (.dll) #elif defined(USE_LIBTYPE_SHARED) #define RLAPI __declspec(dllimport) // We are using raylib as a Win32 shared library (.dll) - #else - #define RLAPI // We are building or using raylib as a static library #endif -#else - #define RLAPI // We are building or using raylib as a static library (or Linux shared library) #endif //---------------------------------------------------------------------------------- @@ -282,23 +281,23 @@ typedef struct NPatchInfo { int layout; // Layout of the n-patch: 3x3, 1x3 or 3x1 } NPatchInfo; -// CharInfo, font character info -typedef struct CharInfo { +// GlyphInfo, font characters glyphs info +typedef struct GlyphInfo { int value; // Character value (Unicode) int offsetX; // Character offset X when drawing int offsetY; // Character offset Y when drawing int advanceX; // Character advance position X Image image; // Character image data -} CharInfo; +} GlyphInfo; -// Font, font texture and CharInfo array data +// Font, font texture and GlyphInfo array data typedef struct Font { int baseSize; // Base size (default chars height) int charsCount; // Number of characters int charsPadding; // Padding around the chars Texture2D texture; // Characters texture atlas Rectangle *recs; // Characters rectangles in texture - CharInfo *chars; // Characters info data + GlyphInfo *chars; // Characters glyphs info data } Font; // Camera, defines position/orientation in 3d space @@ -894,11 +893,6 @@ typedef bool (*SaveFileDataCallback)(const char *fileName, void *data, unsigned typedef char *(*LoadFileTextCallback)(const char *fileName); // FileIO: Load text data typedef bool (*SaveFileTextCallback)(const char *fileName, char *text); // FileIO: Save text data - -#if defined(__cplusplus) -extern "C" { // Prevents name mangling of functions -#endif - //------------------------------------------------------------------------------------ // Global Variables Definition //------------------------------------------------------------------------------------ @@ -908,6 +902,10 @@ extern "C" { // Prevents name mangling of functions // Window and Graphics Device Functions (Module: core) //------------------------------------------------------------------------------------ +#if defined(__cplusplus) +extern "C" { // Prevents name mangling of functions +#endif + // Window-related functions RLAPI void InitWindow(int width, int height, const char *title); // Initialize window and OpenGL context RLAPI bool WindowShouldClose(void); // Check if KEY_ESCAPE pressed or Close icon pressed @@ -1077,8 +1075,8 @@ RLAPI bool IsKeyDown(int key); // Check if a key RLAPI bool IsKeyReleased(int key); // Check if a key has been released once RLAPI bool IsKeyUp(int key); // Check if a key is NOT being pressed RLAPI void SetExitKey(int key); // Set a custom key to exit program (default is ESC) -RLAPI int GetKeyPressed(void); // Get key pressed (keycode), call it multiple times for keys queued -RLAPI int GetCharPressed(void); // Get char pressed (unicode), call it multiple times for chars queued +RLAPI int GetKeyPressed(void); // Get key pressed (keycode), call it multiple times for keys queued, returns 0 when the queue is empty +RLAPI int GetCharPressed(void); // Get char pressed (unicode), call it multiple times for chars queued, returns 0 when the queue is empty // Input-related functions: gamepads RLAPI bool IsGamepadAvailable(int gamepad); // Check if a gamepad is available @@ -1217,7 +1215,7 @@ RLAPI Image GenImageGradientRadial(int width, int height, float density, Color i RLAPI Image GenImageChecked(int width, int height, int checksX, int checksY, Color col1, Color col2); // Generate image: checked RLAPI Image GenImageWhiteNoise(int width, int height, float factor); // Generate image: white noise RLAPI Image GenImagePerlinNoise(int width, int height, int offsetX, int offsetY, float scale); // Generate image: perlin noise -RLAPI Image GenImageCellular(int width, int height, int tileSize); // Generate image: cellular algorithm. Bigger tileSize means bigger cells +RLAPI Image GenImageCellular(int width, int height, int tileSize); // Generate image: cellular algorithm, bigger tileSize means bigger cells // Image manipulation functions RLAPI Image ImageCopy(Image image); // Create an image duplicate (useful for transformations) @@ -1320,34 +1318,42 @@ RLAPI Font LoadFont(const char *fileName); RLAPI Font LoadFontEx(const char *fileName, int fontSize, int *fontChars, int charsCount); // Load font from file with extended parameters RLAPI Font LoadFontFromImage(Image image, Color key, int firstChar); // Load font from Image (XNA style) RLAPI Font LoadFontFromMemory(const char *fileType, const unsigned char *fileData, int dataSize, int fontSize, int *fontChars, int charsCount); // Load font from memory buffer, fileType refers to extension: i.e. '.ttf' -RLAPI CharInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSize, int *fontChars, int charsCount, int type); // Load font data for further use -RLAPI Image GenImageFontAtlas(const CharInfo *chars, Rectangle **recs, int charsCount, int fontSize, int padding, int packMethod); // Generate image font atlas using chars info -RLAPI void UnloadFontData(CharInfo *chars, int charsCount); // Unload font chars info data (RAM) +RLAPI GlyphInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSize, int *fontChars, int charsCount, int type); // Load font data for further use +RLAPI Image GenImageFontAtlas(const GlyphInfo *chars, Rectangle **recs, int charsCount, int fontSize, int padding, int packMethod); // Generate image font atlas using chars info +RLAPI void UnloadFontData(GlyphInfo *chars, int charsCount); // Unload font chars info data (RAM) RLAPI void UnloadFont(Font font); // Unload Font from GPU memory (VRAM) // Text drawing functions RLAPI void DrawFPS(int posX, int posY); // Draw current FPS 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 DrawTextPro(Font font, const char *text, Vector2 position, Vector2 origin, float rotation, float fontSize, float spacing, Color tint); -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, int selectStart, int selectLength, Color selectTint, Color selectBackTint); // Draw text using font inside rectangle limits with support for text selection +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 DrawTextPro(Font font, const char *text, Vector2 position, Vector2 origin, float rotation, float fontSize, float spacing, Color tint); // Draw text using Font and pro parameters (rotation) RLAPI void DrawTextCodepoint(Font font, int codepoint, Vector2 position, float fontSize, Color tint); // Draw one character (codepoint) -// Text misc. functions +// Text font info functions 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 codepoint); // Get index position for a unicode character on font +RLAPI int GetGlyphIndex(Font font, int codepoint); // Get glyph index position in font for a codepoint (unicode character), fallback to '?' if not found +RLAPI GlyphInfo GetGlyphInfo(Font font, int codepoint); // Get glyph font info data for a codepoint (unicode character), fallback to '?' if not found +RLAPI Rectangle GetGlyphAtlasRec(Font font, int codepoint); // Get glyph rectangle in font atlas for a codepoint (unicode character), fallback to '?' if not found -// Text strings management functions (no utf8 strings, only byte chars) +// Text codepoints management functions (unicode characters) +RLAPI int *LoadCodepoints(const char *text, int *count); // Load all codepoints from a UTF-8 text string, codepoints count returned by parameter +RLAPI void UnloadCodepoints(int *codepoints); // Unload codepoints data from memory +RLAPI int GetCodepointsCount(const char *text); // Get total number of codepoints in a UTF-8 encoded string +RLAPI int GetCodepoint(const char *text, int *bytesProcessed); // Get next codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure +RLAPI const char *CodepointToUTF8(int codepoint, int *byteLength); // Encode one codepoint into UTF-8 byte array (array length returned as parameter) +RLAPI char *TextCodepointsToUTF8(int *codepoints, int length); // Encode text as codepoints array into UTF-8 text string (WARNING: memory must be freed!) + +// Text strings management functions (no UTF-8 strings, only byte chars) // NOTE: Some strings allocate memory internally for returned strings, just be careful! RLAPI int TextCopy(char *dst, const char *src); // Copy one string to another, returns bytes copied 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 const char *TextFormat(const char *text, ...); // Text formatting with variables (sprintf style) +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 char *TextReplace(char *text, const char *replace, const char *by); // Replace text string (memory must be freed!) -RLAPI char *TextInsert(const char *text, const char *insert, int position); // Insert text in a position (memory must be freed!) +RLAPI char *TextReplace(char *text, const char *replace, const char *by); // Replace text string (WARNING: memory must be freed!) +RLAPI char *TextInsert(const char *text, const char *insert, int position); // Insert text in a position (WARNING: memory must 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 void TextAppend(char *text, const char *append, int *position); // Append text at specific position and move cursor! @@ -1356,14 +1362,6 @@ RLAPI const char *TextToUpper(const char *text); // Get upp RLAPI const char *TextToLower(const char *text); // Get lower case version of provided string RLAPI const char *TextToPascal(const char *text); // Get Pascal case notation version of provided string RLAPI int TextToInteger(const char *text); // Get integer value from text (negative values not supported) -RLAPI char *TextToUtf8(int *codepoints, int length); // Encode text codepoint into utf8 text (memory must be freed!) - -// UTF8 text strings management functions -RLAPI int *LoadCodepoints(const char *text, int *count); // Load all codepoints from a UTF8 text string, codepoints count returned by parameter -RLAPI void UnloadCodepoints(int *codepoints); // Unload codepoints data from memory -RLAPI int GetCodepointsCount(const char *text); // Get total number of characters (codepoints) in a UTF8 encoded string -RLAPI int GetCodepoint(const char *text, int *bytesProcessed); // Get next codepoint in a UTF8 encoded string, 0x3f('?') is returned on failure -RLAPI const char *CodepointToUtf8(int codepoint, int *byteLength); // Encode codepoint into utf8 text (char array length returned as parameter) //------------------------------------------------------------------------------------ // Basic 3d Shapes Drawing Functions (Module: models) @@ -1428,6 +1426,7 @@ RLAPI Mesh GenMeshCube(float width, float height, float length); RLAPI Mesh GenMeshSphere(float radius, int rings, int slices); // Generate sphere mesh (standard sphere) RLAPI Mesh GenMeshHemiSphere(float radius, int rings, int slices); // Generate half-sphere mesh (no bottom cap) RLAPI Mesh GenMeshCylinder(float radius, float height, int slices); // Generate cylinder mesh +RLAPI Mesh GenMeshCone(float radius, float height, int slices); // Generate cone/pyramid mesh RLAPI Mesh GenMeshTorus(float radius, float size, int radSeg, int sides); // Generate torus mesh RLAPI Mesh GenMeshKnot(float radius, float size, int radSeg, int sides); // Generate trefoil knot mesh RLAPI Mesh GenMeshHeightmap(Image heightmap, Vector3 size); // Generate heightmap mesh from image data diff --git a/src/raymath.h b/src/raymath.h index 2eb7bbd0d..0909eb7e5 100644 --- a/src/raymath.h +++ b/src/raymath.h @@ -138,11 +138,11 @@ typedef struct Matrix { // NOTE: Helper types to be used instead of array return types for *ToFloat functions typedef struct float3 { - float v[3]; + float v[3]; } float3; typedef struct float16 { - float v[16]; + float v[16]; } float16; #include // Required for: sinf(), cosf(), tan(), atan2f(), sqrtf(), fminf(), fmaxf(), fabs() diff --git a/src/rlgl.h b/src/rlgl.h index 76b5114af..3c995c79e 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -457,13 +457,14 @@ typedef enum { RL_SHADER_ATTRIB_VEC4 // Shader attribute type: vec4 (4 float) } rlShaderAttributeDataType; +//------------------------------------------------------------------------------------ +// Functions Declaration - Matrix operations +//------------------------------------------------------------------------------------ + #if defined(__cplusplus) extern "C" { // Prevents name mangling of functions #endif -//------------------------------------------------------------------------------------ -// Functions Declaration - Matrix operations -//------------------------------------------------------------------------------------ RLAPI void rlMatrixMode(int mode); // Choose the current matrix to be transformed RLAPI void rlPushMatrix(void); // Push the current matrix to stack RLAPI void rlPopMatrix(void); // Pop lattest inserted matrix from stack @@ -506,8 +507,8 @@ RLAPI void rlDisableVertexBufferElement(void); // Disable vertex buffer RLAPI void rlEnableVertexAttribute(unsigned int index); // Enable vertex attribute index RLAPI void rlDisableVertexAttribute(unsigned int index);// Disable vertex attribute index #if defined(GRAPHICS_API_OPENGL_11) -RLAPI void rlEnableStatePointer(int vertexAttribType, void *buffer); -RLAPI void rlDisableStatePointer(int vertexAttribType); +RLAPI void rlEnableStatePointer(int vertexAttribType, void *buffer); // Enable attribute state pointer +RLAPI void rlDisableStatePointer(int vertexAttribType); // Disable attribute state pointer #endif // Textures state @@ -525,8 +526,11 @@ RLAPI void rlDisableShader(void); // Disable shader progra // Framebuffer state RLAPI void rlEnableFramebuffer(unsigned int id); // Enable render texture (fbo) RLAPI void rlDisableFramebuffer(void); // Disable render texture (fbo), return to default framebuffer +RLAPI void rlActiveDrawBuffers(int count); // Activate multiple draw color buffers // General render state +RLAPI void rlEnableColorBlend(void); // Enable color blending +RLAPI void rlDisableColorBlend(void); // Disable color blending RLAPI void rlEnableDepthTest(void); // Enable depth test RLAPI void rlDisableDepthTest(void); // Disable depth test RLAPI void rlEnableDepthMask(void); // Enable depth write @@ -584,7 +588,7 @@ RLAPI void rlSetTexture(unsigned int id); // Set current texture for r RLAPI unsigned int rlLoadVertexArray(void); // Load vertex array (vao) if supported RLAPI unsigned int rlLoadVertexBuffer(void *buffer, int size, bool dynamic); // Load a vertex buffer attribute RLAPI unsigned int rlLoadVertexBufferElement(void *buffer, int size, bool dynamic); // Load a new attributes element buffer -RLAPI void rlUpdateVertexBuffer(int bufferId, void *data, int dataSize, int offset); // Update GPU buffer with new data +RLAPI void rlUpdateVertexBuffer(unsigned int bufferId, void *data, int dataSize, int offset); // Update GPU buffer with new data RLAPI void rlUnloadVertexArray(unsigned int vaoId); RLAPI void rlUnloadVertexBuffer(unsigned int vboId); RLAPI void rlSetVertexAttribute(unsigned int index, int compSize, int type, bool normalized, int stride, void *pointer); @@ -639,6 +643,7 @@ RLAPI void rlSetMatrixViewOffsetStereo(Matrix right, Matrix left); // Set e // Quick and dirty cube/quad buffers load->draw->unload RLAPI void rlLoadDrawCube(void); // Load and draw a cube RLAPI void rlLoadDrawQuad(void); // Load and draw a quad + #if defined(__cplusplus) } #endif @@ -1544,6 +1549,49 @@ void rlDisableFramebuffer(void) #endif } +// Activate multiple draw color buffers +// NOTE: One color buffer is always active by default +void rlActiveDrawBuffers(int count) +{ +#if (defined(GRAPHICS_API_OPENGL_33) && defined(SUPPORT_RENDER_TEXTURES_HINT)) + // NOTE: Maximum number of draw buffers supported is implementation dependant, + // it can be queried with glGet*() but it must be at least 8 + //GLint maxDrawBuffers = 0; + //glGetIntegerv(GL_MAX_DRAW_BUFFERS, &maxDrawBuffers); + + if (count > 0) + { + if (count > 8) TRACELOG(LOG_WARNING, "GL: Max color buffers limited to 8"); + else + { + unsigned int buffers[8] = { + GL_COLOR_ATTACHMENT0, + GL_COLOR_ATTACHMENT1, + GL_COLOR_ATTACHMENT2, + GL_COLOR_ATTACHMENT3, + GL_COLOR_ATTACHMENT4, + GL_COLOR_ATTACHMENT5, + GL_COLOR_ATTACHMENT6, + GL_COLOR_ATTACHMENT7, + }; + + glDrawBuffers(count, buffers); + } + } + else TRACELOG(LOG_WARNING, "GL: One color buffer active by default"); +#endif +} + +//---------------------------------------------------------------------------------- +// General render state configuration +//---------------------------------------------------------------------------------- + +// Enable color blending +void rlEnableColorBlend(void) { glEnable(GL_BLEND); } + +// Disable color blending +void rlDisableColorBlend(void) { glDisable(GL_BLEND); } + // Enable depth test void rlEnableDepthTest(void) { glEnable(GL_DEPTH_TEST); } @@ -1588,11 +1636,9 @@ void rlDisableWireMode(void) glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); #endif } + // Set the line drawing width -void rlSetLineWidth(float width) -{ - glLineWidth(width); -} +void rlSetLineWidth(float width) { glLineWidth(width); } // Get the line drawing width float rlGetLineWidth(void) @@ -3179,6 +3225,7 @@ unsigned int rlLoadVertexBufferElement(void *buffer, int size, bool dynamic) return id; } +// Enable vertex buffer (VBO) void rlEnableVertexBuffer(unsigned int id) { #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) @@ -3186,6 +3233,7 @@ void rlEnableVertexBuffer(unsigned int id) #endif } +// Disable vertex buffer (VBO) void rlDisableVertexBuffer(void) { #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) @@ -3193,6 +3241,7 @@ void rlDisableVertexBuffer(void) #endif } +// Enable vertex buffer element (VBO element) void rlEnableVertexBufferElement(unsigned int id) { #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) @@ -3200,6 +3249,7 @@ void rlEnableVertexBufferElement(unsigned int id) #endif } +// Disable vertex buffer element (VBO element) void rlDisableVertexBufferElement(void) { #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) @@ -3207,16 +3257,27 @@ void rlDisableVertexBufferElement(void) #endif } -// Update GPU buffer with new data +// Update vertex buffer with new data // NOTE: dataSize and offset must be provided in bytes -void rlUpdateVertexBuffer(int bufferId, void *data, int dataSize, int offset) +void rlUpdateVertexBuffer(unsigned int id, void *data, int dataSize, int offset) { #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - glBindBuffer(GL_ARRAY_BUFFER, bufferId); + glBindBuffer(GL_ARRAY_BUFFER, id); glBufferSubData(GL_ARRAY_BUFFER, offset, dataSize, data); #endif } +// Update vertex buffer elements with new data +// NOTE: dataSize and offset must be provided in bytes +void rlUpdateVertexBufferElements(unsigned int id, void *data, int dataSize, int offset) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, id); + glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, offset, dataSize, data); +#endif +} + +// Enable vertex array object (VAO) bool rlEnableVertexArray(unsigned int vaoId) { bool result = false; @@ -3230,6 +3291,7 @@ bool rlEnableVertexArray(unsigned int vaoId) return result; } +// Disable vertex array object (VAO) void rlDisableVertexArray(void) { #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) @@ -3237,6 +3299,7 @@ void rlDisableVertexArray(void) #endif } +// Enable vertex attribute index void rlEnableVertexAttribute(unsigned int index) { #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) @@ -3244,6 +3307,7 @@ void rlEnableVertexAttribute(unsigned int index) #endif } +// Disable vertex attribute index void rlDisableVertexAttribute(unsigned int index) { #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) @@ -3251,16 +3315,19 @@ void rlDisableVertexAttribute(unsigned int index) #endif } +// Draw vertex array void rlDrawVertexArray(int offset, int count) { glDrawArrays(GL_TRIANGLES, offset, count); } +// Draw vertex array elements void rlDrawVertexArrayElements(int offset, int count, void *buffer) { glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_SHORT, (unsigned short *)buffer + offset); } +// Draw vertex array instanced void rlDrawVertexArrayInstanced(int offset, int count, int instances) { #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) @@ -3268,6 +3335,7 @@ void rlDrawVertexArrayInstanced(int offset, int count, int instances) #endif } +// Draw vertex array elements instanced void rlDrawVertexArrayElementsInstanced(int offset, int count, void *buffer, int instances) { #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) @@ -3276,6 +3344,7 @@ void rlDrawVertexArrayElementsInstanced(int offset, int count, void *buffer, int } #if defined(GRAPHICS_API_OPENGL_11) +// Enable vertex state pointer void rlEnableStatePointer(int vertexAttribType, void *buffer) { if (buffer != NULL) glEnableClientState(vertexAttribType); @@ -3290,12 +3359,14 @@ void rlEnableStatePointer(int vertexAttribType, void *buffer) } } +// Disable vertex state pointer void rlDisableStatePointer(int vertexAttribType) { glDisableClientState(vertexAttribType); } #endif +// Load vertex array object (VAO) unsigned int rlLoadVertexArray(void) { unsigned int vaoId = 0; @@ -3308,6 +3379,7 @@ unsigned int rlLoadVertexArray(void) return vaoId; } +// Set vertex attribute void rlSetVertexAttribute(unsigned int index, int compSize, int type, bool normalized, int stride, void *pointer) { #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) @@ -3315,6 +3387,7 @@ void rlSetVertexAttribute(unsigned int index, int compSize, int type, bool norma #endif } +// Set vertex attribute divisor void rlSetVertexAttributeDivisor(unsigned int index, int divisor) { #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) @@ -3322,6 +3395,7 @@ void rlSetVertexAttributeDivisor(unsigned int index, int divisor) #endif } +// Unload vertex array object (VAO) void rlUnloadVertexArray(unsigned int vaoId) { #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) @@ -3334,6 +3408,7 @@ void rlUnloadVertexArray(unsigned int vaoId) #endif } +// Unload vertex buffer (VBO) void rlUnloadVertexBuffer(unsigned int vboId) { #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) diff --git a/src/text.c b/src/text.c index 0371a0cec..9d336f218 100644 --- a/src/text.c +++ b/src/text.c @@ -53,14 +53,15 @@ #include "config.h" // Defines module configuration flags #endif +#include "utils.h" // Required for: LoadFileText() +#include "rlgl.h" // OpenGL abstraction layer to OpenGL 1.1, 2.1, 3.3+ or ES2 -> Only DrawTextPro() + #include // Required for: malloc(), free() #include // Required for: vsprintf() #include // Required for: strcmp(), strstr(), strcpy(), strncpy() [Used in TextReplace()], sscanf() [Used in LoadBMFont()] #include // Required for: va_list, va_start(), vsprintf(), va_end() [Used in TextFormat()] #include // Requried for: toupper(), tolower() [Used in TextToUpper(), TextToLower()] -#include "utils.h" // Required for: LoadFileText() - #if defined(SUPPORT_FILEFORMAT_TTF) #define STB_RECT_PACK_IMPLEMENTATION #include "external/stb_rect_pack.h" // Required for: ttf font rectangles packaging @@ -125,7 +126,7 @@ extern void LoadFontDefault(void) { #define BIT_CHECK(a,b) ((a) & (1u << (b))) - // NOTE: Using UTF8 encoding table for Unicode U+0000..U+00FF Basic Latin + Latin-1 Supplement + // NOTE: Using UTF-8 encoding table for Unicode U+0000..U+00FF Basic Latin + Latin-1 Supplement // Ref: http://www.utf8-chartable.de/unicode-utf8-table.pl defaultFont.charsCount = 224; // Number of chars included in our default font @@ -224,7 +225,7 @@ extern void LoadFontDefault(void) // Allocate space for our characters info data // NOTE: This memory should be freed at end! --> CloseWindow() - defaultFont.chars = (CharInfo *)RL_MALLOC(defaultFont.charsCount*sizeof(CharInfo)); + defaultFont.chars = (GlyphInfo *)RL_MALLOC(defaultFont.charsCount*sizeof(GlyphInfo)); defaultFont.recs = (Rectangle *)RL_MALLOC(defaultFont.charsCount*sizeof(Rectangle)); int currentLine = 0; @@ -453,7 +454,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 - font.chars = (CharInfo *)RL_MALLOC(font.charsCount*sizeof(CharInfo)); + font.chars = (GlyphInfo *)RL_MALLOC(font.charsCount*sizeof(GlyphInfo)); font.recs = (Rectangle *)RL_MALLOC(font.charsCount*sizeof(Rectangle)); for (int i = 0; i < font.charsCount; i++) @@ -523,7 +524,7 @@ Font LoadFontFromMemory(const char *fileType, const unsigned char *fileData, int // Load font data for further use // NOTE: Requires TTF font memory data and can generate SDF data -CharInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSize, int *fontChars, int charsCount, int type) +GlyphInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSize, int *fontChars, int charsCount, int type) { // NOTE: Using some SDF generation default values, // trades off precision with ability to handle *smaller* sizes @@ -540,7 +541,7 @@ CharInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSize #define FONT_BITMAP_ALPHA_THRESHOLD 80 // Bitmap (B&W) font generation alpha threshold #endif - CharInfo *chars = NULL; + GlyphInfo *chars = NULL; #if defined(SUPPORT_FILEFORMAT_TTF) // Load font data (including pixel data) from TTF memory file @@ -573,7 +574,7 @@ CharInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSize genFontChars = true; } - chars = (CharInfo *)RL_MALLOC(charsCount*sizeof(CharInfo)); + chars = (GlyphInfo *)RL_MALLOC(charsCount*sizeof(GlyphInfo)); // NOTE: Using simple packaging, one char after another for (int i = 0; i < charsCount; i++) @@ -649,7 +650,7 @@ CharInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSize // Generate image font atlas using chars info // NOTE: Packing method: 0-Default, 1-Skyline #if defined(SUPPORT_FILEFORMAT_TTF) -Image GenImageFontAtlas(const CharInfo *chars, Rectangle **charRecs, int charsCount, int fontSize, int padding, int packMethod) +Image GenImageFontAtlas(const GlyphInfo *chars, Rectangle **charRecs, int charsCount, int fontSize, int padding, int packMethod) { Image atlas = { 0 }; @@ -792,7 +793,7 @@ Image GenImageFontAtlas(const CharInfo *chars, Rectangle **charRecs, int charsCo #endif // Unload font chars info data (RAM) -void UnloadFontData(CharInfo *chars, int charsCount) +void UnloadFontData(GlyphInfo *chars, int charsCount) { for (int i = 0; i < charsCount; i++) UnloadImage(chars[i].image); @@ -890,140 +891,18 @@ void DrawTextEx(Font font, const char *text, Vector2 position, float fontSize, f } } -// Draw text using font inside rectangle limits -void DrawTextRec(Font font, const char *text, Rectangle rec, float fontSize, float spacing, bool wordWrap, Color tint) +// Draw text using Font and pro parameters (rotation) +void DrawTextPro(Font font, const char *text, Vector2 position, Vector2 origin, float rotation, float fontSize, float spacing, Color tint) { - DrawTextRecEx(font, text, rec, fontSize, spacing, wordWrap, tint, 0, 0, WHITE, WHITE); -} + rlPushMatrix(); -// Draw text using font inside rectangle limits with support for text selection -void DrawTextRecEx(Font font, const char *text, Rectangle rec, float fontSize, float spacing, bool wordWrap, Color tint, int selectStart, int selectLength, Color selectTint, Color selectBackTint) -{ - int length = TextLength(text); // Total length in bytes of the text, scanned by codepoints in loop - - float textOffsetY = 0; // Offset between lines (on line break '\n') - float textOffsetX = 0.0f; // Offset X to next character to draw - - float scaleFactor = fontSize/(float)font.baseSize; // Character quad scaling factor - - // Word/character wrapping mechanism variables - enum { MEASURE_STATE = 0, DRAW_STATE = 1 }; - int state = wordWrap? MEASURE_STATE : DRAW_STATE; - - int startLine = -1; // Index where to begin drawing (where a line begins) - int endLine = -1; // Index where to stop drawing (where a line ends) - int lastk = -1; // Holds last value of the character position - - for (int i = 0, k = 0; i < length; i++, k++) - { - // Get next codepoint from byte string and glyph index in font - int codepointByteCount = 0; - int codepoint = GetCodepoint(&text[i], &codepointByteCount); - int index = GetGlyphIndex(font, codepoint); - - // 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 moving one byte - if (codepoint == 0x3f) codepointByteCount = 1; - i += (codepointByteCount - 1); - - float glyphWidth = 0; - if (codepoint != '\n') - { - glyphWidth = (font.chars[index].advanceX == 0) ? font.recs[index].width*scaleFactor : font.chars[index].advanceX*scaleFactor; - - if (i + 1 < length) glyphWidth = glyphWidth + spacing; - } - - // NOTE: When wordWrap is ON we first measure how much of the text we can draw before going outside of the rec container - // We store this info in startLine and endLine, then we change states, draw the text between those two variables - // and change states again and again recursively until the end of the text (or until we get outside of the container). - // When wordWrap is OFF we don't need the measure state so we go to the drawing state immediately - // and begin drawing on the next line before we can get outside 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 - // Ref: http://jkorpela.fi/chars/spaces.html - if ((codepoint == ' ') || (codepoint == '\t') || (codepoint == '\n')) endLine = i; - - if ((textOffsetX + glyphWidth) > rec.width) - { - endLine = (endLine < 1)? i : endLine; - if (i == endLine) endLine -= codepointByteCount; - if ((startLine + codepointByteCount) == endLine) endLine = (i - codepointByteCount); - - state = !state; - } - else if ((i + 1) == length) - { - endLine = i; - state = !state; - } - else if (codepoint == '\n') state = !state; - - if (state == DRAW_STATE) - { - textOffsetX = 0; - i = startLine; - glyphWidth = 0; - - // Save character position when we switch states - int tmp = lastk; - lastk = k - 1; - k = tmp; - } - } - else - { - if (codepoint == '\n') - { - if (!wordWrap) - { - textOffsetY += (font.baseSize + font.baseSize/2)*scaleFactor; - textOffsetX = 0; - } - } - else - { - if (!wordWrap && ((textOffsetX + glyphWidth) > rec.width)) - { - textOffsetY += (font.baseSize + font.baseSize/2)*scaleFactor; - textOffsetX = 0; - } - - // When text overflows rectangle height limit, just stop drawing - if ((textOffsetY + font.baseSize*scaleFactor) > rec.height) break; - - // Draw selection background - bool isGlyphSelected = false; - if ((selectStart >= 0) && (k >= selectStart) && (k < (selectStart + selectLength))) - { - DrawRectangleRec((Rectangle){ rec.x + textOffsetX - 1, rec.y + textOffsetY, glyphWidth, (float)font.baseSize*scaleFactor }, selectBackTint); - isGlyphSelected = true; - } - - // Draw current character glyph - if ((codepoint != ' ') && (codepoint != '\t')) - { - DrawTextCodepoint(font, codepoint, (Vector2){ rec.x + textOffsetX, rec.y + textOffsetY }, fontSize, isGlyphSelected? selectTint : tint); - } - } - - if (wordWrap && (i == endLine)) - { - textOffsetY += (font.baseSize + font.baseSize/2)*scaleFactor; - textOffsetX = 0; - startLine = endLine; - endLine = -1; - glyphWidth = 0; - selectStart += lastk - k; - k = lastk; - - state = !state; - } - } - - textOffsetX += glyphWidth; - } + rlTranslatef(position.x, position.y, 0.0f); + rlRotatef(rotation, 0.0f, 0.0f, 1.0f); + rlTranslatef(-origin.x, -origin.y, 0.0f); + + DrawTextEx(font, text, (Vector2){ 0.0f, 0.0f }, fontSize, spacing, tint); + + rlPopMatrix(); } // Draw one character (codepoint) @@ -1123,6 +1002,7 @@ Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing } // Get index position for a unicode character on font +// NOTE: If codepoint is not found in the font it fallbacks to '?' int GetGlyphIndex(Font font, int codepoint) { #ifndef GLYPH_NOTFOUND_CHAR_FALLBACK @@ -1149,6 +1029,28 @@ int GetGlyphIndex(Font font, int codepoint) #endif } +// Get glyph font info data for a codepoint (unicode character) +// NOTE: If codepoint is not found in the font it fallbacks to '?' +GlyphInfo GetGlyphInfo(Font font, int codepoint) +{ + GlyphInfo info = { 0 }; + + info = font.chars[GetGlyphIndex(font, codepoint)]; + + return info; +} + +// Get glyph rectangle in font atlas for a codepoint (unicode character) +// NOTE: If codepoint is not found in the font it fallbacks to '?' +Rectangle GetGlyphAtlasRec(Font font, int codepoint) +{ + Rectangle rec = { 0 }; + + rec = font.recs[GetGlyphIndex(font, codepoint)]; + + return rec; +} + //---------------------------------------------------------------------------------- // Text strings management functions //---------------------------------------------------------------------------------- @@ -1446,7 +1348,7 @@ const char *TextToUpper(const char *text) buffer[i] = (char)toupper(text[i]); //if ((text[i] >= 'a') && (text[i] <= 'z')) buffer[i] = text[i] - 32; - // TODO: Support Utf8 diacritics! + // TODO: Support UTF-8 diacritics! //if ((text[i] >= 'à') && (text[i] <= 'ý')) buffer[i] = text[i] - 32; } else { buffer[i] = '\0'; break; } @@ -1499,10 +1401,10 @@ const char *TextToPascal(const char *text) return buffer; } -// Encode text codepoint into utf8 text +// Encode text codepoint into UTF-8 text // REQUIRES: memcpy() // WARNING: Allocated memory should be manually freed -char *TextToUtf8(int *codepoints, int length) +char *TextCodepointsToUTF8(int *codepoints, int length) { // We allocate enough memory fo fit all possible codepoints // NOTE: 5 bytes for every codepoint should be enough @@ -1512,7 +1414,7 @@ char *TextToUtf8(int *codepoints, int length) for (int i = 0, bytes = 0; i < length; i++) { - utf8 = CodepointToUtf8(codepoints[i], &bytes); + utf8 = CodepointToUTF8(codepoints[i], &bytes); memcpy(text + size, utf8, bytes); size += bytes; } @@ -1526,7 +1428,8 @@ char *TextToUtf8(int *codepoints, int length) } // Encode codepoint into utf8 text (char array length returned as parameter) -RLAPI const char *CodepointToUtf8(int codepoint, int *byteLength) +// NOTE: It uses a static array to store UTF-8 bytes +RLAPI const char *CodepointToUTF8(int codepoint, int *byteLength) { static char utf8[6] = { 0 }; int length = 0; @@ -1563,7 +1466,7 @@ RLAPI const char *CodepointToUtf8(int codepoint, int *byteLength) return utf8; } -// Load all codepoints from a UTF8 text string, codepoints count returned by parameter +// Load all codepoints from a UTF-8 text string, codepoints count returned by parameter int *LoadCodepoints(const char *text, int *count) { int textLength = TextLength(text); @@ -1595,8 +1498,8 @@ void UnloadCodepoints(int *codepoints) RL_FREE(codepoints); } -// Get total number of characters(codepoints) in a UTF8 encoded text, until '\0' is found -// NOTE: If an invalid UTF8 sequence is encountered a '?'(0x3f) codepoint is counted instead +// Get total number of characters(codepoints) in a UTF-8 encoded text, until '\0' is found +// NOTE: If an invalid UTF-8 sequence is encountered a '?'(0x3f) codepoint is counted instead int GetCodepointsCount(const char *text) { unsigned int len = 0; @@ -1617,8 +1520,8 @@ int GetCodepointsCount(const char *text) } #endif // SUPPORT_TEXT_MANIPULATION -// Get 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 +// Get next codepoint in a UTF-8 encoded text, scanning until '\0' is found +// When a invalid UTF-8 byte is encountered we exit as soon as possible and a '?'(0x3f) codepoint is returned // Total number of bytes processed are returned as a parameter // 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 @@ -1626,7 +1529,7 @@ int GetCodepointsCount(const char *text) int GetCodepoint(const char *text, int *bytesProcessed) { /* - UTF8 specs from https://www.ietf.org/rfc/rfc3629.txt + UTF-8 specs from https://www.ietf.org/rfc/rfc3629.txt Char. number range | UTF-8 octet sequence (hexadecimal) | (binary) @@ -1845,7 +1748,7 @@ static Font LoadBMFont(const char *fileName) font.baseSize = fontSize; font.charsCount = charsCount; font.charsPadding = 0; - font.chars = (CharInfo *)RL_MALLOC(charsCount*sizeof(CharInfo)); + font.chars = (GlyphInfo *)RL_MALLOC(charsCount*sizeof(GlyphInfo)); font.recs = (Rectangle *)RL_MALLOC(charsCount*sizeof(Rectangle)); int charId, charX, charY, charWidth, charHeight, charOffsetX, charOffsetY, charAdvanceX; diff --git a/src/utils.h b/src/utils.h index 50f704201..c9b331815 100644 --- a/src/utils.h +++ b/src/utils.h @@ -55,9 +55,7 @@ //---------------------------------------------------------------------------------- // Types and Structures Definition //---------------------------------------------------------------------------------- -#ifdef __cplusplus -extern "C" { // Prevents name mangling of functions -#endif +//... //---------------------------------------------------------------------------------- // Global Variables Definition @@ -67,6 +65,10 @@ extern "C" { // Prevents name mangling of functions //---------------------------------------------------------------------------------- // Module Functions Declaration //---------------------------------------------------------------------------------- +#ifdef __cplusplus +extern "C" { // Prevents name mangling of functions +#endif + #if defined(PLATFORM_ANDROID) void InitAssetManager(AAssetManager *manager, const char *dataPath); // Initialize asset manager from android app FILE *android_fopen(const char *fileName, const char *mode); // Replacement for fopen() -> Read-only!