diff --git a/code/game/src/utils/raylib_helpers.h b/code/game/src/utils/raylib_helpers.h index e730dc3..759603c 100644 --- a/code/game/src/utils/raylib_helpers.h +++ b/code/game/src/utils/raylib_helpers.h @@ -1,368 +1,377 @@ -#pragma once -#include "system.h" -#include "raylib.h" -#include "world/blocks.h" -#include "assets.h" - -#define RAYLIB_NEW_RLGL -#include "rlgl.h" - -static inline float lerp(float a, float b, float t) { return a * (1.0f - t) + b * t; } - -static inline -void DrawTextEco(const char *text, float posX, float posY, int fontSize, Color color, float spacing) { -#if 1 - // Check if default font has been loaded - if (GetFontDefault().texture.id != 0) { - Vector2 position = { (float)posX , (float)posY }; - - int defaultFontSize = 10; // Default Font chars height in pixel - int new_spacing = spacing == 0.0f ? fontSize/defaultFontSize : spacing; - - DrawTextEx(GetFontDefault(), text, position, (float)fontSize , (float)new_spacing , color); - } -#endif -} - -static inline -int MeasureTextEco(const char *text, int fontSize, float spacing) { -#if 1 - Vector2 vec = { 0.0f, 0.0f }; - - // Check if default font has been loaded - if (GetFontDefault().texture.id != 0) { - int defaultFontSize = 10; // Default Font chars height in pixel - int new_spacing = spacing == 0.0f ? fontSize/defaultFontSize : spacing; - - vec = MeasureTextEx(GetFontDefault(), text, (float)fontSize, (float)new_spacing); - } - - return (int)vec.x; -#else - return 0; -#endif -} - -static inline -void DrawCircleEco(float centerX, float centerY, float radius, Color color) -{ - DrawCircleV((Vector2){ (float)centerX , (float)centerY }, radius , color); -} - -static inline -void DrawRectangleEco(float posX, float posY, float width, float height, Color color) -{ - DrawRectangleV((Vector2){ (float)posX , (float)posY }, (Vector2){ width , height }, color); -} - -static inline -Texture2D GetBlockImage(uint8_t id) { - return *(Texture2D*)blocks_get_img(id); -} - -static inline -RenderTexture2D GetChunkTexture(uint64_t id) { - RenderTexture2D *tex = (RenderTexture2D*)blocks_get_chunk_tex(id); - if (!tex) return (RenderTexture2D){0}; - return *tex; -} - -static inline -Texture2D GetSpriteTexture2D(uint16_t id) { - return *(Texture2D*)assets_get_tex(id); -} - -static inline -Sound GetSound(uint16_t id) { - return *(Sound*)assets_get_snd(id); -} - -// Draw cube -// NOTE: Cube position is the center position -static inline -void EcoDrawCube(Vector3 position, float width, float height, float length, float heading, Color color) -{ - float x = 0.0f; - float y = 0.0f; - float z = 0.0f; - - rlCheckRenderBatchLimit(36); - - rlPushMatrix(); - // NOTE: Transformation is applied in inverse order (scale -> rotate -> translate) - rlTranslatef(position.x, position.y, position.z); - rlRotatef(heading, 0, 1, 0); - //rlScalef(1.0f, 1.0f, 1.0f); // NOTE: Vertices are directly scaled on definition - - rlBegin(RL_TRIANGLES); - rlColor4ub(color.r, color.g, color.b, color.a); - - // Front face - rlVertex3f(x - width/2, y - height/2, z + length/2); // Bottom Left - rlVertex3f(x + width/2, y - height/2, z + length/2); // Bottom Right - rlVertex3f(x - width/2, y + height/2, z + length/2); // Top Left - - rlVertex3f(x + width/2, y + height/2, z + length/2); // Top Right - rlVertex3f(x - width/2, y + height/2, z + length/2); // Top Left - rlVertex3f(x + width/2, y - height/2, z + length/2); // Bottom Right - - // Back face - rlVertex3f(x - width/2, y - height/2, z - length/2); // Bottom Left - rlVertex3f(x - width/2, y + height/2, z - length/2); // Top Left - rlVertex3f(x + width/2, y - height/2, z - length/2); // Bottom Right - - rlVertex3f(x + width/2, y + height/2, z - length/2); // Top Right - rlVertex3f(x + width/2, y - height/2, z - length/2); // Bottom Right - rlVertex3f(x - width/2, y + height/2, z - length/2); // Top Left - - // Top face - rlVertex3f(x - width/2, y + height/2, z - length/2); // Top Left - rlVertex3f(x - width/2, y + height/2, z + length/2); // Bottom Left - rlVertex3f(x + width/2, y + height/2, z + length/2); // Bottom Right - - rlVertex3f(x + width/2, y + height/2, z - length/2); // Top Right - rlVertex3f(x - width/2, y + height/2, z - length/2); // Top Left - rlVertex3f(x + width/2, y + height/2, z + length/2); // Bottom Right - - // Bottom face - rlVertex3f(x - width/2, y - height/2, z - length/2); // Top Left - rlVertex3f(x + width/2, y - height/2, z + length/2); // Bottom Right - rlVertex3f(x - width/2, y - height/2, z + length/2); // Bottom Left - - rlVertex3f(x + width/2, y - height/2, z - length/2); // Top Right - rlVertex3f(x + width/2, y - height/2, z + length/2); // Bottom Right - rlVertex3f(x - width/2, y - height/2, z - length/2); // Top Left - - // Right face - rlVertex3f(x + width/2, y - height/2, z - length/2); // Bottom Right - rlVertex3f(x + width/2, y + height/2, z - length/2); // Top Right - rlVertex3f(x + width/2, y + height/2, z + length/2); // Top Left - - rlVertex3f(x + width/2, y - height/2, z + length/2); // Bottom Left - rlVertex3f(x + width/2, y - height/2, z - length/2); // Bottom Right - rlVertex3f(x + width/2, y + height/2, z + length/2); // Top Left - - // Left face - rlVertex3f(x - width/2, y - height/2, z - length/2); // Bottom Right - rlVertex3f(x - width/2, y + height/2, z + length/2); // Top Left - rlVertex3f(x - width/2, y + height/2, z - length/2); // Top Right - - rlVertex3f(x - width/2, y - height/2, z + length/2); // Bottom Left - rlVertex3f(x - width/2, y + height/2, z + length/2); // Top Left - rlVertex3f(x - width/2, y - height/2, z - length/2); // Bottom Right - rlEnd(); - rlPopMatrix(); -} - -// Draw codepoint at specified position in 3D space -void DrawTextCodepoint3D(Font font, int codepoint, Vector3 position, float fontSize, bool backface, Color tint) -{ - // Character index position in sprite font - // NOTE: In case a codepoint is not available in the font, index returned points to '?' - int index = GetGlyphIndex(font, codepoint); - float scale = fontSize/(float)font.baseSize; - - // Character destination rectangle on screen - // NOTE: We consider charsPadding on drawing - position.x += (float)(font.chars[index].offsetX - font.charsPadding)/(float)font.baseSize*scale; - position.z += (float)(font.chars[index].offsetY - font.charsPadding)/(float)font.baseSize*scale; - - // Character source rectangle from font texture atlas - // NOTE: We consider chars padding when drawing, it could be required for outline/glow shader effects - Rectangle srcRec = { font.recs[index].x - (float)font.charsPadding, font.recs[index].y - (float)font.charsPadding, - font.recs[index].width + 2.0f*font.charsPadding, font.recs[index].height + 2.0f*font.charsPadding }; - - float width = (float)(font.recs[index].width + 2.0f*font.charsPadding)/(float)font.baseSize*scale; - float height = (float)(font.recs[index].height + 2.0f*font.charsPadding)/(float)font.baseSize*scale; - - if (font.texture.id > 0) - { - const float x = 0.0f; - const float y = 0.0f; - const float z = 0.0f; - - // normalized texture coordinates of the glyph inside the font texture (0.0f -> 1.0f) - const float tx = srcRec.x/font.texture.width; - const float ty = srcRec.y/font.texture.height; - const float tw = (srcRec.x+srcRec.width)/font.texture.width; - const float th = (srcRec.y+srcRec.height)/font.texture.height; - - { -#if defined(RAYLIB_NEW_RLGL) - } - rlCheckRenderBatchLimit(4 + 4*backface); - rlSetTexture(font.texture.id); -#else - if (rlCheckBufferLimit(4 + 4*backface)) rlglDraw(); - rlEnableTexture(font.texture.id); -#endif - rlPushMatrix(); - rlTranslatef(position.x, position.y, position.z); - - rlBegin(RL_QUADS); - rlColor4ub(tint.r, tint.g, tint.b, tint.a); - - // Front Face - rlNormal3f(0.0f, 1.0f, 0.0f); // Normal Pointing Up - rlTexCoord2f(tx, ty); rlVertex3f(x, y, z); // Top Left Of The Texture and Quad - rlTexCoord2f(tx, th); rlVertex3f(x, y, z + height); // Bottom Left Of The Texture and Quad - rlTexCoord2f(tw, th); rlVertex3f(x + width, y, z + height); // Bottom Right Of The Texture and Quad - rlTexCoord2f(tw, ty); rlVertex3f(x + width, y, z); // Top Right Of The Texture and Quad - - if (backface) - { - // Back Face - rlNormal3f(0.0f, -1.0f, 0.0f); // Normal Pointing Down - rlTexCoord2f(tx, ty); rlVertex3f(x, y, z); // Top Right Of The Texture and Quad - rlTexCoord2f(tw, ty); rlVertex3f(x + width, y, z); // Top Left Of The Texture and Quad - rlTexCoord2f(tw, th); rlVertex3f(x + width, y, z + height); // Bottom Left Of The Texture and Quad - rlTexCoord2f(tx, th); rlVertex3f(x, y, z + height); // Bottom Right Of The Texture and Quad - } - rlEnd(); - rlPopMatrix(); - -#if defined(RAYLIB_NEW_RLGL) - rlSetTexture(0); -#else - rlDisableTexture(); -#endif - } -} - -void DrawText3D(Font font, const char *text, Vector3 position, float fontSize, float fontSpacing, float lineSpacing, bool backface, Color tint) { - int length = TextLength(text); // Total length in bytes of the text, scanned by codepoints in loop - - float textOffsetY = 0.0f; // Offset between lines (on line break '\n') - float textOffsetX = 0.0f; // Offset X to next character to draw - - float scale = fontSize/(float)font.baseSize; - - for (int i = 0; i < length;) - { - // 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; - - if (codepoint == '\n') - { - // NOTE: Fixed line spacing of 1.5 line-height - // TODO: Support custom line spacing defined by user - textOffsetY += scale + lineSpacing/(float)font.baseSize*scale; - textOffsetX = 0.0f; - } - else - { - if ((codepoint != ' ') && (codepoint != '\t')) - { - DrawTextCodepoint3D(font, codepoint, (Vector3){ position.x + textOffsetX, position.y, position.z + textOffsetY }, fontSize, backface, tint); - } - - if (font.chars[index].advanceX == 0) textOffsetX += (float)(font.recs[index].width + fontSpacing)/(float)font.baseSize*scale; - else textOffsetX += (float)(font.chars[index].advanceX + fontSpacing)/(float)font.baseSize*scale; - } - - i += codepointByteCount; // Move text bytes counter to next codepoint - } -} - -Vector3 MeasureText3D(Font font, const char* text, float fontSize, float fontSpacing, float lineSpacing) { - int len = TextLength(text); - int tempLen = 0; // Used to count longer text line num chars - int lenCounter = 0; - - float tempTextWidth = 0.0f; // Used to count longer text line width - - float scale = fontSize/(float)font.baseSize; - float textHeight = scale; - float textWidth = 0.0f; - - int letter = 0; // Current character - int index = 0; // Index position in sprite font - - for (int i = 0; i < len; i++) - { - lenCounter++; - - int next = 0; - letter = GetCodepoint(&text[i], &next); - index = GetGlyphIndex(font, letter); - - // NOTE: normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f) - // but we need to draw all of the bad bytes using the '?' symbol so to not skip any we set next = 1 - if (letter == 0x3f) next = 1; - i += next - 1; - - if (letter != '\n') - { - if (font.chars[index].advanceX != 0) textWidth += (font.chars[index].advanceX+fontSpacing)/(float)font.baseSize*scale; - else textWidth += (font.recs[index].width + font.chars[index].offsetX)/(float)font.baseSize*scale; - } - else - { - if (tempTextWidth < textWidth) tempTextWidth = textWidth; - lenCounter = 0; - textWidth = 0.0f; - textHeight += scale + lineSpacing/(float)font.baseSize*scale; - } - - if (tempLen < lenCounter) tempLen = lenCounter; - } - - if (tempTextWidth < textWidth) tempTextWidth = textWidth; - - Vector3 vec = { 0 }; - vec.x = tempTextWidth + (float)((tempLen - 1)*fontSpacing/(float)font.baseSize*scale); // Adds chars spacing to measure - vec.y = 0.25f; - vec.z = textHeight; - - return vec; -} - - -Color GenerateRandomColor(float s, float v) { - const float Phi = 0.618033988749895f; // Golden ratio conjugate - float h = GetRandomValue(0, 360); - h = fmodf((h + h*Phi), 360.0f); - return ColorFromHSV(h, s, v); -} - - -// Draw circle outline -void DrawCircleLinesEco(float centerX, float centerY, float radius, Color color) -{ - rlCheckRenderBatchLimit(2*36); - - rlBegin(RL_LINES); - rlColor4ub(color.r, color.g, color.b, color.a); - - // NOTE: Circle outline is drawn pixel by pixel every degree (0 to 360) - for (int i = 0; i < 360; i += 10) - { - rlVertex2f(centerX + sinf(DEG2RAD*i)*radius, centerY + cosf(DEG2RAD*i)*radius); - rlVertex2f(centerX + sinf(DEG2RAD*(i + 10))*radius, centerY + cosf(DEG2RAD*(i + 10))*radius); - } - rlEnd(); -} - -void DrawRectangleLinesEco(float posX, float posY, float width, float height, Color color) -{ - rlBegin(RL_LINES); - rlColor4ub(color.r, color.g, color.b, color.a); - rlVertex2f(posX + 1, posY + 1); - rlVertex2f(posX + width, posY + 1); - - rlVertex2f(posX + width, posY + 1); - rlVertex2f(posX + width, posY + height); - - rlVertex2f(posX + width, posY + height); - rlVertex2f(posX + 1, posY + height); - - rlVertex2f(posX + 1, posY + height); - rlVertex2f(posX + 1, posY + 1); - rlEnd(); -} +#pragma once +#include "system.h" +#include "raylib.h" +#include "world/blocks.h" +#include "assets.h" + +#define RAYLIB_NEW_RLGL +#include "rlgl.h" + +static inline float lerp(float a, float b, float t) { return a * (1.0f - t) + b * t; } + +static inline +void DrawTextEco(const char *text, float posX, float posY, int fontSize, Color color, float spacing) { +#if 1 + // Check if default font has been loaded + if (GetFontDefault().texture.id != 0) { + Vector2 position = { (float)posX , (float)posY }; + + int defaultFontSize = 10; // Default Font chars height in pixel + int new_spacing = spacing == 0.0f ? fontSize/defaultFontSize : spacing; + + DrawTextEx(GetFontDefault(), text, position, (float)fontSize , (float)new_spacing , color); + } +#endif +} + +static inline +int MeasureTextEco(const char *text, int fontSize, float spacing) { +#if 1 + Vector2 vec = { 0.0f, 0.0f }; + + // Check if default font has been loaded + if (GetFontDefault().texture.id != 0) { + int defaultFontSize = 10; // Default Font chars height in pixel + int new_spacing = spacing == 0.0f ? fontSize/defaultFontSize : spacing; + + vec = MeasureTextEx(GetFontDefault(), text, (float)fontSize, (float)new_spacing); + } + + return (int)vec.x; +#else + return 0; +#endif +} + +static inline +void DrawCircleEco(float centerX, float centerY, float radius, Color color) +{ + DrawCircleV((Vector2){ (float)centerX , (float)centerY }, radius , color); +} + +static inline +void DrawRectangleEco(float posX, float posY, float width, float height, Color color) +{ + DrawRectangleV((Vector2){ (float)posX , (float)posY }, (Vector2){ width , height }, color); +} + +static inline +Texture2D GetBlockImage(uint8_t id) { + return *(Texture2D*)blocks_get_img(id); +} + +static inline +RenderTexture2D GetChunkTexture(uint64_t id) { + RenderTexture2D *tex = (RenderTexture2D*)blocks_get_chunk_tex(id); + if (!tex) return (RenderTexture2D){0}; + return *tex; +} + +static inline +Texture2D GetSpriteTexture2D(uint16_t id) { + return *(Texture2D*)assets_get_tex(id); +} + +static inline +Sound GetSound(uint16_t id) { + return *(Sound*)assets_get_snd(id); +} + +// Draw cube +// NOTE: Cube position is the center position +static inline +void EcoDrawCube(Vector3 position, float width, float height, float length, float heading, Color color) +{ + float x = 0.0f; + float y = 0.0f; + float z = 0.0f; + + rlCheckRenderBatchLimit(36); + + rlPushMatrix(); + // NOTE: Transformation is applied in inverse order (scale -> rotate -> translate) + rlTranslatef(position.x, position.y, position.z); + rlRotatef(heading, 0, 1, 0); + //rlScalef(1.0f, 1.0f, 1.0f); // NOTE: Vertices are directly scaled on definition + + rlBegin(RL_TRIANGLES); + rlColor4ub(color.r, color.g, color.b, color.a); + + // Front face + rlVertex3f(x - width/2, y - height/2, z + length/2); // Bottom Left + rlVertex3f(x + width/2, y - height/2, z + length/2); // Bottom Right + rlVertex3f(x - width/2, y + height/2, z + length/2); // Top Left + + rlVertex3f(x + width/2, y + height/2, z + length/2); // Top Right + rlVertex3f(x - width/2, y + height/2, z + length/2); // Top Left + rlVertex3f(x + width/2, y - height/2, z + length/2); // Bottom Right + + // Back face + rlVertex3f(x - width/2, y - height/2, z - length/2); // Bottom Left + rlVertex3f(x - width/2, y + height/2, z - length/2); // Top Left + rlVertex3f(x + width/2, y - height/2, z - length/2); // Bottom Right + + rlVertex3f(x + width/2, y + height/2, z - length/2); // Top Right + rlVertex3f(x + width/2, y - height/2, z - length/2); // Bottom Right + rlVertex3f(x - width/2, y + height/2, z - length/2); // Top Left + + // Top face + rlVertex3f(x - width/2, y + height/2, z - length/2); // Top Left + rlVertex3f(x - width/2, y + height/2, z + length/2); // Bottom Left + rlVertex3f(x + width/2, y + height/2, z + length/2); // Bottom Right + + rlVertex3f(x + width/2, y + height/2, z - length/2); // Top Right + rlVertex3f(x - width/2, y + height/2, z - length/2); // Top Left + rlVertex3f(x + width/2, y + height/2, z + length/2); // Bottom Right + + // Bottom face + rlVertex3f(x - width/2, y - height/2, z - length/2); // Top Left + rlVertex3f(x + width/2, y - height/2, z + length/2); // Bottom Right + rlVertex3f(x - width/2, y - height/2, z + length/2); // Bottom Left + + rlVertex3f(x + width/2, y - height/2, z - length/2); // Top Right + rlVertex3f(x + width/2, y - height/2, z + length/2); // Bottom Right + rlVertex3f(x - width/2, y - height/2, z - length/2); // Top Left + + // Right face + rlVertex3f(x + width/2, y - height/2, z - length/2); // Bottom Right + rlVertex3f(x + width/2, y + height/2, z - length/2); // Top Right + rlVertex3f(x + width/2, y + height/2, z + length/2); // Top Left + + rlVertex3f(x + width/2, y - height/2, z + length/2); // Bottom Left + rlVertex3f(x + width/2, y - height/2, z - length/2); // Bottom Right + rlVertex3f(x + width/2, y + height/2, z + length/2); // Top Left + + // Left face + rlVertex3f(x - width/2, y - height/2, z - length/2); // Bottom Right + rlVertex3f(x - width/2, y + height/2, z + length/2); // Top Left + rlVertex3f(x - width/2, y + height/2, z - length/2); // Top Right + + rlVertex3f(x - width/2, y - height/2, z + length/2); // Bottom Left + rlVertex3f(x - width/2, y + height/2, z + length/2); // Top Left + rlVertex3f(x - width/2, y - height/2, z - length/2); // Bottom Right + rlEnd(); + rlPopMatrix(); +} + +// Draw codepoint at specified position in 3D space +void DrawTextCodepoint3D(Font font, int codepoint, Vector3 position, float fontSize, bool backface, Color tint) +{ +#if 0 + // Character index position in sprite font + // NOTE: In case a codepoint is not available in the font, index returned points to '?' + int index = GetGlyphIndex(font, codepoint); + float scale = fontSize/(float)font.baseSize; + + // Character destination rectangle on screen + // NOTE: We consider charsPadding on drawing + position.x += (float)(font.chars[index].offsetX - font.charsPadding)/(float)font.baseSize*scale; + position.z += (float)(font.chars[index].offsetY - font.charsPadding)/(float)font.baseSize*scale; + + // Character source rectangle from font texture atlas + // NOTE: We consider chars padding when drawing, it could be required for outline/glow shader effects + Rectangle srcRec = { font.recs[index].x - (float)font.charsPadding, font.recs[index].y - (float)font.charsPadding, + font.recs[index].width + 2.0f*font.charsPadding, font.recs[index].height + 2.0f*font.charsPadding }; + + float width = (float)(font.recs[index].width + 2.0f*font.charsPadding)/(float)font.baseSize*scale; + float height = (float)(font.recs[index].height + 2.0f*font.charsPadding)/(float)font.baseSize*scale; + + if (font.texture.id > 0) + { + const float x = 0.0f; + const float y = 0.0f; + const float z = 0.0f; + + // normalized texture coordinates of the glyph inside the font texture (0.0f -> 1.0f) + const float tx = srcRec.x/font.texture.width; + const float ty = srcRec.y/font.texture.height; + const float tw = (srcRec.x+srcRec.width)/font.texture.width; + const float th = (srcRec.y+srcRec.height)/font.texture.height; + + { +#if defined(RAYLIB_NEW_RLGL) + } + rlCheckRenderBatchLimit(4 + 4*backface); + rlSetTexture(font.texture.id); +#else + if (rlCheckBufferLimit(4 + 4*backface)) rlglDraw(); + rlEnableTexture(font.texture.id); +#endif + rlPushMatrix(); + rlTranslatef(position.x, position.y, position.z); + + rlBegin(RL_QUADS); + rlColor4ub(tint.r, tint.g, tint.b, tint.a); + + // Front Face + rlNormal3f(0.0f, 1.0f, 0.0f); // Normal Pointing Up + rlTexCoord2f(tx, ty); rlVertex3f(x, y, z); // Top Left Of The Texture and Quad + rlTexCoord2f(tx, th); rlVertex3f(x, y, z + height); // Bottom Left Of The Texture and Quad + rlTexCoord2f(tw, th); rlVertex3f(x + width, y, z + height); // Bottom Right Of The Texture and Quad + rlTexCoord2f(tw, ty); rlVertex3f(x + width, y, z); // Top Right Of The Texture and Quad + + if (backface) + { + // Back Face + rlNormal3f(0.0f, -1.0f, 0.0f); // Normal Pointing Down + rlTexCoord2f(tx, ty); rlVertex3f(x, y, z); // Top Right Of The Texture and Quad + rlTexCoord2f(tw, ty); rlVertex3f(x + width, y, z); // Top Left Of The Texture and Quad + rlTexCoord2f(tw, th); rlVertex3f(x + width, y, z + height); // Bottom Left Of The Texture and Quad + rlTexCoord2f(tx, th); rlVertex3f(x, y, z + height); // Bottom Right Of The Texture and Quad + } + rlEnd(); + rlPopMatrix(); + +#if defined(RAYLIB_NEW_RLGL) + rlSetTexture(0); +#else + rlDisableTexture(); +#endif + } +#endif +} + +void DrawText3D(Font font, const char *text, Vector3 position, float fontSize, float fontSpacing, float lineSpacing, bool backface, Color tint) { +#if 0 + int length = TextLength(text); // Total length in bytes of the text, scanned by codepoints in loop + + float textOffsetY = 0.0f; // Offset between lines (on line break '\n') + float textOffsetX = 0.0f; // Offset X to next character to draw + + float scale = fontSize/(float)font.baseSize; + + for (int i = 0; i < length;) + { + // 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; + + if (codepoint == '\n') + { + // NOTE: Fixed line spacing of 1.5 line-height + // TODO: Support custom line spacing defined by user + textOffsetY += scale + lineSpacing/(float)font.baseSize*scale; + textOffsetX = 0.0f; + } + else + { + if ((codepoint != ' ') && (codepoint != '\t')) + { + DrawTextCodepoint3D(font, codepoint, (Vector3){ position.x + textOffsetX, position.y, position.z + textOffsetY }, fontSize, backface, tint); + } + + if (font.chars[index].advanceX == 0) textOffsetX += (float)(font.recs[index].width + fontSpacing)/(float)font.baseSize*scale; + else textOffsetX += (float)(font.chars[index].advanceX + fontSpacing)/(float)font.baseSize*scale; + } + + i += codepointByteCount; // Move text bytes counter to next codepoint + + } +#endif +} + +Vector3 MeasureText3D(Font font, const char* text, float fontSize, float fontSpacing, float lineSpacing) { + +#if 0 + int len = TextLength(text); + int tempLen = 0; // Used to count longer text line num chars + int lenCounter = 0; + + float tempTextWidth = 0.0f; // Used to count longer text line width + + float scale = fontSize/(float)font.baseSize; + float textHeight = scale; + float textWidth = 0.0f; + + int letter = 0; // Current character + int index = 0; // Index position in sprite font + + for (int i = 0; i < len; i++) + { + lenCounter++; + + int next = 0; + letter = GetCodepoint(&text[i], &next); + index = GetGlyphIndex(font, letter); + + // NOTE: normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f) + // but we need to draw all of the bad bytes using the '?' symbol so to not skip any we set next = 1 + if (letter == 0x3f) next = 1; + i += next - 1; + + if (letter != '\n') + { + if (font.chars[index].advanceX != 0) textWidth += (font.chars[index].advanceX+fontSpacing)/(float)font.baseSize*scale; + else textWidth += (font.recs[index].width + font.chars[index].offsetX)/(float)font.baseSize*scale; + } + else + { + if (tempTextWidth < textWidth) tempTextWidth = textWidth; + lenCounter = 0; + textWidth = 0.0f; + textHeight += scale + lineSpacing/(float)font.baseSize*scale; + } + + if (tempLen < lenCounter) tempLen = lenCounter; + } + + if (tempTextWidth < textWidth) tempTextWidth = textWidth; + + Vector3 vec = { 0 }; + vec.x = tempTextWidth + (float)((tempLen - 1)*fontSpacing/(float)font.baseSize*scale); // Adds chars spacing to measure + vec.y = 0.25f; + vec.z = textHeight; + + return vec; +#endif + +} + + +Color GenerateRandomColor(float s, float v) { + const float Phi = 0.618033988749895f; // Golden ratio conjugate + float h = GetRandomValue(0, 360); + h = fmodf((h + h*Phi), 360.0f); + return ColorFromHSV(h, s, v); +} + + +// Draw circle outline +void DrawCircleLinesEco(float centerX, float centerY, float radius, Color color) +{ + rlCheckRenderBatchLimit(2*36); + + rlBegin(RL_LINES); + rlColor4ub(color.r, color.g, color.b, color.a); + + // NOTE: Circle outline is drawn pixel by pixel every degree (0 to 360) + for (int i = 0; i < 360; i += 10) + { + rlVertex2f(centerX + sinf(DEG2RAD*i)*radius, centerY + cosf(DEG2RAD*i)*radius); + rlVertex2f(centerX + sinf(DEG2RAD*(i + 10))*radius, centerY + cosf(DEG2RAD*(i + 10))*radius); + } + rlEnd(); +} + +void DrawRectangleLinesEco(float posX, float posY, float width, float height, Color color) +{ + rlBegin(RL_LINES); + rlColor4ub(color.r, color.g, color.b, color.a); + rlVertex2f(posX + 1, posY + 1); + rlVertex2f(posX + width, posY + 1); + + rlVertex2f(posX + width, posY + 1); + rlVertex2f(posX + width, posY + height); + + rlVertex2f(posX + width, posY + height); + rlVertex2f(posX + 1, posY + height); + + rlVertex2f(posX + 1, posY + height); + rlVertex2f(posX + 1, posY + 1); + rlEnd(); +}