diff --git a/code/foundation/src/platform/arch.h b/code/foundation/src/platform/arch.h index 7fe5552..7df5c76 100644 --- a/code/foundation/src/platform/arch.h +++ b/code/foundation/src/platform/arch.h @@ -98,6 +98,8 @@ void platform_get_block_realpos(float *x, float *y){ entity_view *e = game_world_view_active_get_entity(cam.ent_id); if (!e) return; float zoom = renderer_zoom_get(); + mpos.x = screenWidth-mpos.x; + mpos.y = screenHeight-mpos.y; mpos.x -= screenWidth/2.0f; mpos.y -= screenHeight/2.0f; cam.x += mpos.x*(1.0f/zoom); diff --git a/code/foundation/src/utils/raylib_helpers.h b/code/foundation/src/utils/raylib_helpers.h index 50a3314..2b1629a 100644 --- a/code/foundation/src/utils/raylib_helpers.h +++ b/code/foundation/src/utils/raylib_helpers.h @@ -2,6 +2,7 @@ #include #include "platform/system.h" #include "raylib.h" +#include "raymath.h" #include "world/blocks.h" #include "models/assets.h" @@ -283,3 +284,257 @@ void DrawSpriteTextureEco(Texture2D texture, Vector3 position, float width, floa rlSetTexture(0); } + +static inline +void Draw3DRectangle(Camera camera, float x, float y, float z, float w, float h, Color tint) +{ + rlPushMatrix(); + + // get the camera view matrix + Matrix mat = MatrixLookAt(camera.position, camera.target, camera.up); + // peel off just the rotation + Quaternion quat = QuaternionFromMatrix(mat); + mat = QuaternionToMatrix(quat); + + // apply just the rotation + rlMultMatrixf(MatrixToFloat(mat)); + + Vector3 position = (Vector3){x,z,y}; + position = Vector3Transform(position, MatrixInvert(mat)); + rlTranslatef(position.x, position.y, position.z); + + // draw the billboard + float width = w / 2; + float height = h / 2; + + rlCheckRenderBatchLimit(6); + + // draw quad + rlBegin(RL_QUADS); + rlColor4ub(tint.r, tint.g, tint.b, tint.a); + // Front Face + rlNormal3f(0.0f, 0.0f, 1.0f); // Normal Pointing Towards Viewer + rlVertex3f(-width, -height, 0); // Bottom Left Of The Texture and Quad + rlVertex3f(+width, -height, 0); // Bottom Right Of The Texture and Quad + rlVertex3f(+width, +height, 0); // Top Right Of The Texture and Quad + rlVertex3f(-width, +height, 0); // Top Left Of The Texture and Quad + + rlEnd(); + rlPopMatrix(); +} + +static inline +void Draw3DBillboardRec(Camera camera, Texture2D texture, Rectangle source, Vector3 position, Vector2 size, Color tint) +{ + rlPushMatrix(); + + // get the camera view matrix + Matrix mat = MatrixLookAt(camera.position, camera.target, camera.up); + // peel off just the rotation + Quaternion quat = QuaternionFromMatrix(mat); + mat = QuaternionToMatrix(quat); + + // apply just the rotation + rlMultMatrixf(MatrixToFloat(mat)); + + // translate backwards in the inverse rotated matrix to put the item where it goes in world space + position = Vector3Transform(position, MatrixInvert(mat)); + rlTranslatef(position.x, position.y, position.z); + + // draw the billboard + float width = size.x / 2; + float height = size.y / 2; + + rlCheckRenderBatchLimit(6); + + rlSetTexture(texture.id); + + // draw quad + rlBegin(RL_QUADS); + rlColor4ub(tint.r, tint.g, tint.b, tint.a); + // Front Face + rlNormal3f(0.0f, 0.0f, 1.0f); // Normal Pointing Towards Viewer + + rlTexCoord2f((float)source.x / texture.width, (float)(source.y + source.height) / texture.height); + rlVertex3f(-width, -height, 0); // Bottom Left Of The Texture and Quad + + rlTexCoord2f((float)(source.x + source.width) / texture.width, (float)(source.y + source.height) / texture.height); + rlVertex3f(+width, -height, 0); // Bottom Right Of The Texture and Quad + + rlTexCoord2f((float)(source.x + source.width) / texture.width, (float)source.y / texture.height); + rlVertex3f(+width, +height, 0); // Top Right Of The Texture and Quad + + rlTexCoord2f((float)source.x / texture.width, (float)source.y / texture.height); + rlVertex3f(-width, +height, 0); // Top Left Of The Texture and Quad + + rlEnd(); + rlSetTexture(0); + rlPopMatrix(); +} + +static inline +void Draw3DBillboard(Camera camera, Texture2D texture, Vector3 position, float size, Color tint) +{ + Draw3DBillboardRec(camera, texture, (Rectangle){ 0,0,(float)texture.width,(float)texture.height }, position, (Vector2){ size,size }, tint); +} + +// Draw codepoint at specified position in 3D space +static 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.glyphs[index].offsetX - font.glyphPadding)/(float)font.baseSize*scale; + position.z += (float)(font.glyphs[index].offsetY - font.glyphPadding)/(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.glyphPadding, font.recs[index].y - (float)font.glyphPadding, + font.recs[index].width + 2.0f*font.glyphPadding, font.recs[index].height + 2.0f*font.glyphPadding }; + + float width = (float)(font.recs[index].width + 2.0f*font.glyphPadding)/(float)font.baseSize*scale; + float height = (float)(font.recs[index].height + 2.0f*font.glyphPadding)/(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; + + rlCheckRenderBatchLimit(4 + 4*backface); + rlSetTexture(font.texture.id); + + 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(); + + rlSetTexture(0); + } +} + +// Draw a 2D text in 3D space +static 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') + { + 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.glyphs[index].advanceX == 0) textOffsetX += (float)(font.recs[index].width + fontSpacing)/(float)font.baseSize*scale; + else textOffsetX += (float)(font.glyphs[index].advanceX + fontSpacing)/(float)font.baseSize*scale; + } + + i += codepointByteCount; // Move text bytes counter to next codepoint + } +} + +// Measure a text in 3D. For some reason `MeasureTextEx()` just doesn't seem to work so i had to use this instead. +static 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.glyphs[index].advanceX != 0) textWidth += (font.glyphs[index].advanceX+fontSpacing)/(float)font.baseSize*scale; + else textWidth += (font.recs[index].width + font.glyphs[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; +} diff --git a/code/games/sandbox/src/platform.c b/code/games/sandbox/src/platform.c index bdade5b..9ce8ef6 100644 --- a/code/games/sandbox/src/platform.c +++ b/code/games/sandbox/src/platform.c @@ -61,10 +61,10 @@ void platform_input() { { float x=0.0f, y=0.0f; uint8_t use, sprint, drop, ctrl, pick; - if (IsKeyDown(KEY_RIGHT) || IsKeyDown(KEY_D)) x += 1.0f; - if (IsKeyDown(KEY_LEFT) || IsKeyDown(KEY_A)) x -= 1.0f; - if (IsKeyDown(KEY_UP) || IsKeyDown(KEY_W)) y += 1.0f; - if (IsKeyDown(KEY_DOWN) || IsKeyDown(KEY_S)) y -= 1.0f; + if (IsKeyDown(KEY_RIGHT) || IsKeyDown(KEY_D)) x -= 1.0f; + if (IsKeyDown(KEY_LEFT) || IsKeyDown(KEY_A)) x += 1.0f; + if (IsKeyDown(KEY_UP) || IsKeyDown(KEY_W)) y -= 1.0f; + if (IsKeyDown(KEY_DOWN) || IsKeyDown(KEY_S)) y += 1.0f; use = IsKeyPressed(KEY_SPACE); sprint = IsKeyDown(KEY_LEFT_SHIFT) || IsKeyDown(KEY_RIGHT_SHIFT); @@ -77,6 +77,8 @@ void platform_input() { mouse_pos.y /= screenHeight; mouse_pos.x -= 0.5f; mouse_pos.y -= 0.5f; + mouse_pos.x = screenWidth - mouse_pos.x; + mouse_pos.y = screenHeight - mouse_pos.y; mouse_pos = Vector2Normalize(mouse_pos); if (game_get_kind() == GAMEKIND_SINGLE && IsMouseButtonDown(MOUSE_MIDDLE_BUTTON)) { diff --git a/code/games/sandbox/src/renderer.c b/code/games/sandbox/src/renderer.c index 3863fe0..554a829 100644 --- a/code/games/sandbox/src/renderer.c +++ b/code/games/sandbox/src/renderer.c @@ -9,17 +9,18 @@ float zpl_lerp(float,float,float); float zpl_to_degrees(float); void DrawNametag(const char* name, uint64_t key, entity_view *data, float x, float y) { - float size = 16.f; - float font_size = lerp(4.0f, 32.0f, 0.5f/1.0f);//(float)render_camera.zoom); + float size = 72.f; + float font_size = lerp(12.0f, 72.0f, 0.5f/1.0f);//(float)render_camera.zoom); float font_spacing = 1.1f; float title_bg_offset = 4; float fixed_title_offset = 8.f; float health = (data->hp / data->max_hp); const char *title = TextFormat("%s %llu", name, key); float title_w = MeasureTextEco(title, font_size, font_spacing); - DrawRectangleEco(x-title_w/2.f-title_bg_offset/2.f, y-size-font_size-fixed_title_offset, title_w+title_bg_offset, font_size, ColorAlpha(BLACK, data->tran_time)); - DrawRectangleEco(x-title_w/2.f-title_bg_offset/2.f, y-size-fixed_title_offset, title_w*health+title_bg_offset, font_size*0.2f, ColorAlpha(RED, data->tran_time)); + Draw3DRectangle(render_camera, x-title_w/2.f-title_bg_offset/2.f, y, size+font_size+fixed_title_offset, title_w+title_bg_offset, font_size, ColorAlpha(BLACK, data->tran_time)); + Draw3DRectangle(render_camera, x-title_w/2.f-title_bg_offset/2.f, y, size+fixed_title_offset, title_w*health+title_bg_offset, font_size*0.2f, ColorAlpha(RED, data->tran_time)); DrawTextEco(title, x-title_w/2.f, y-size-font_size-fixed_title_offset, font_size, ColorAlpha(RAYWHITE, data->tran_time), font_spacing); + DrawText3D(GetFontDefault(), title, (Vector3){x-title_w/2.f, 64.f, y}, 24.f, 12.0f, 1.3f, true, WHITE); } void DEBUG_draw_ground(uint64_t key, entity_view * data) { @@ -40,7 +41,7 @@ void DEBUG_draw_ground(uint64_t key, entity_view * data) { tex.texture.height *= (int32_t)scale; // DrawTextureRec(tex.texture, (Rectangle){0, 0, size, -size}, (Vector2){x, y}, ColorAlpha(WHITE, data->tran_time)); DrawCubeTexture(tex.texture, (Vector3){x+half_size, 0.0f, y+half_size}, size, 0.01f, size, WHITE); - DrawCubeWires((Vector3){x, 0.f, y}, WORLD_BLOCK_SIZE*view->chunk_size, 66, WORLD_BLOCK_SIZE*view->chunk_size, BLUE); +// DrawCubeWires((Vector3){x, 0.f, y}, WORLD_BLOCK_SIZE*view->chunk_size, 66, WORLD_BLOCK_SIZE*view->chunk_size, BLUE); for (size_t ty = 0; ty < view->chunk_size; ty++) { for (size_t tx = 0; tx < view->chunk_size; tx++) { @@ -97,6 +98,7 @@ void DEBUG_draw_entities(uint64_t key, entity_view * data) { float bl_size = (float)(view->chunk_size * WORLD_BLOCK_SIZE); float half_size = bl_size / 2.0f; float half_block_size = (float)(WORLD_BLOCK_SIZE >> 1); + Texture2D sprite_tex = GetSpriteTexture2D(assets_find(data->asset)); switch (data->kind) { case EKIND_DEMO_NPC: { @@ -136,11 +138,7 @@ void DEBUG_draw_entities(uint64_t key, entity_view * data) { float y = data->y - 32.f; // DrawTexturePro(GetSpriteTexture2D(assets_find(data->asset)), ASSET_SRC_RECT(), ASSET_DST_RECT(x,y), (Vector2){0.5f,0.5f}, 0.0f, ALPHA(WHITE)); // DrawCubeTexture(GetSpriteTexture2D(assets_find(data->asset)), (Vector3){x + half_block_size, 32.0f, y + half_block_size}, 64, 0.01f, 64, WHITE); - DrawSpriteTextureEco(GetSpriteTexture2D(assets_find(data->asset)), (Vector3){x + half_block_size, half_block_size/2, y + half_block_size}, 32, 0.01f, 32, WHITE); - - if (data->asset == ASSET_BIG_TREE) { - DrawSpriteTextureEco(GetSpriteTexture2D(assets_find(data->asset)), (Vector3){x + half_block_size, 150, y + half_block_size}, 300, 0.01f, 300, WHITE); - } + Draw3DBillboard(render_camera, sprite_tex, (Vector3){x + half_block_size, 32.0f, y + half_block_size}, 64.0f, WHITE); if (data->quantity > 1) { DrawTextEco(zpl_bprintf("%d", data->quantity), x, y, 10, ALPHA(RAYWHITE), 0.0f); @@ -154,8 +152,7 @@ void DEBUG_draw_entities(uint64_t key, entity_view * data) { case EKIND_DEVICE: { float x = data->x - 32.f; float y = data->y - 32.f; - // DrawTexturePro(GetSpriteTexture2D(assets_find(data->asset)), ASSET_SRC_RECT(), ASSET_DST_RECT(x,y), (Vector2){0.5f,0.5f}, 0.0f, ALPHA(WHITE)); - DrawSpriteTextureEco(GetSpriteTexture2D(assets_find(data->asset)), (Vector3){x + half_block_size, 32, y + half_block_size}, 64, 0.01f, 64, WHITE); + Draw3DBillboard(render_camera, sprite_tex, (Vector3){x + half_block_size, 32.0f, y + half_block_size}, 64.0f, WHITE); if (data->quantity > 1) { DrawTextEco(zpl_bprintf("%d", data->quantity), x, y, 10, ALPHA(RAYWHITE), 0.0f); @@ -207,8 +204,8 @@ void renderer_draw(void) { camera game_camera = camera_get(); #if 1 - render_camera.position = (Vector3){(float)game_camera.x, 260.0f*(10.0f-cam_zoom), (float)game_camera.y+50.0f*(10.0f-cam_zoom/2.0f)}; - render_camera.target = (Vector3){(float)game_camera.x, 0.0f, (float)game_camera.y}; + render_camera.position = (Vector3){(float)game_camera.x, 260.0f*(10.0f-cam_zoom)+30.0f, (float)game_camera.y-50.0f*(10.0f-cam_zoom/2.0f)}; + render_camera.target = (Vector3){(float)game_camera.x, 30.0f, (float)game_camera.y}; #else UpdateCamera(&render_camera); #endif