From 7d1fc6906adecc88ba562ea2abcbc9c475fb6012 Mon Sep 17 00:00:00 2001 From: Dominic Masters Date: Tue, 14 Mar 2023 18:55:10 -0700 Subject: [PATCH] Adding bitmap fonts --- assets/bmfont.png | Bin 0 -> 2747 bytes lib/SDL | 2 +- lib/openal-soft | 2 +- src/CMakeLists.txt | 2 +- src/dawn/display/font/BitmapFont.cpp | 124 ++++++++++++++++++ src/dawn/display/font/BitmapFont.hpp | 31 +++++ src/dawn/display/font/CMakeLists.txt | 21 +-- src/dawn/scene/components/ui/CMakeLists.txt | 1 + src/dawn/scene/components/ui/UIComponent.cpp | 3 +- src/dawn/scene/components/ui/UIImage.cpp | 66 ++++++++++ src/dawn/scene/components/ui/UIImage.hpp | 30 +++++ src/dawn/scene/components/ui/UILabel.cpp | 1 - src/dawnhelloworld/CMakeLists.txt | 5 +- src/dawnhelloworld/scenes/HelloWorldScene.hpp | 32 +++++ .../display/shader/ShaderProgram.cpp | 5 + .../display/shader/ShaderProgram.hpp | 13 +- .../shader/SimpleTexturedShaderProgram.hpp | 3 + src/dawntools/tools/CMakeLists.txt | 7 +- 18 files changed, 328 insertions(+), 20 deletions(-) create mode 100644 assets/bmfont.png create mode 100644 src/dawn/display/font/BitmapFont.cpp create mode 100644 src/dawn/display/font/BitmapFont.hpp create mode 100644 src/dawn/scene/components/ui/UIImage.cpp create mode 100644 src/dawn/scene/components/ui/UIImage.hpp diff --git a/assets/bmfont.png b/assets/bmfont.png new file mode 100644 index 0000000000000000000000000000000000000000..43b9f57c4d91699b7d98f8e9652c06a2a089af23 GIT binary patch literal 2747 zcmV;s3PkmZP)J|9?66Wv7==kOcN5X^UPG4@O`cW1M8W$4$Wf ze!oZ0kK;Hh^5ZxT`#!Q0J@+ihoL95OxIPxZmi{r!Hwep^ z+_pXMDU0kyJk^C>1zVt%1&bCY8zb6u(R`lvBL9x#I6fjLBlyKb##eV?b+WAv3d(b0 zGx_lk8Ml>38Oic@Hw^_d#a^f@T0q61}&i(R&6om@K_TJ0{Xa{-L0X>@vBM z-8^3>7|`a*GOCx`Adv&0n*y_ep8c7;>X&`~>|-MH<93UK)z3jByBhKlu#y2-u&~{T z{9*DP{wwm8BarD=GldVN?1(DW0G=!}oH?s5SR`KwpdJ2a6KM53fAtvj0T!k3OgEER zU$P@?Db2FM?n+~9$6axRs62{3%ZQ#U8ym`8h|{mrL%UOmpmR5SZX=qdSj2&{jN&)a zU6HSHzX~6>o9-N&0Q*`2)3uS?NwK~^3&748kY8+?{=N<3hWMA*-cFMDml_qInO@P8e|Ku z$Ul*Bl`t}pRmpz9Ojkj50;)lz=p)W^+-LSuYF?c0l|r z0HQwC-KgkyfELjEe=a1u0U-|mZ0kaGBHTK5yYcjicArNj74MM$MEr<>tn9wZ4jBMs z)vA4t`qSJbG zsix1#eb0pqzN{>YB%+b%il;{$`$C#y$T_m=dM;PmO8rN}JB zTds(oaVS2n^r;*ZT^~C7jJNcQppJt!z1!D_H`?Wuw2HEf;v)-AmYups(A9X?W9b1q zqQ9E~UX9h=KNt)KJ$yU+y4SEwpUI_&VN)@XGWBBN~w0y zGu0ne&ib#m?Q`TySAGxB;m`aJzg(!^#aDI+v%f3DtoUEC(Ls0Tt2}Ugl^w@+Z^cR5 zRw7*W7wwP^Uxnuhp{OjqP-V$sA^q`!N(H_8IY1a;?IL9iE3(xc zUg4>N{+&Q|2ity?Gq7qO^+CR6T&p`M3LaDkvHA1%R~J3fZ)f4FqHr)63hijlRd6|Q@@9rRAe2P7rf{}hT7}W8u zERy#T@mG1J|1W?|5ZYalW7RW?=`-I~ceqW#Y<$Q!6a+i6Xg1eZXXwFTFc|zu%+L9lGKh3!bx2huhk)d>!4$hxt8#(NlK1 zM>M(UoL|kbtGZnuzLalo%j%plLj{Ni-US>H~{O`1-Zv#<;De_eyes&FH zyT{A2g+y|N7md$nn1}=Eui)DUL79WrneHTaf%xpiu@sGt2*n2SZ*|;^-}=(E5#iha z8^|JXcLs=3I6KEGHvF!Nbd?>d+PNYFDt2xAnf8~#&Zxm)Fc@5hy94XP^sCnp`%I~T zQF}!{^FfiLW2-YC5eJ%o#s$~7>L4ha+24iJ2D&9xKP9y7N|1SdBrpFSpfj0_-`>+Rn=1=h8DAGCf8?XGsWN>V zJZ%0Df7Wl??*TIS0d@540HRFbkAc z!C)|W3fL7VdX26zzMthoDSkyB`N2o5gYmE0-@)Ip`*jOA{=v7;_t~pAe9Cq$!0v@q z_;*&_NFQB0cknBCxCLZ^fL{Gzd?2hiXMH>US>>zpuU6H&Z%v55Ge0lS0Ic7YawSN; zynD^f^sDw+o9TNWUY-FeI}ph;TV9Ut%^iLQ)jy`cLmv6?VFlS_q1Q+!Gd6fXeElTs zc8eF01px}4R}5%S!(@Ozj&C)9ZQJ+g?DV_6yZcwNhRsMFo~TC1#@OL!`po{S{~4sO zgMS*bK<)6XWVC7#+|lpYx6glN0Ib#@NMGT;(*AD!FaSDt%Zf8QBb)KF9p>fUx844u zqx1@X2I)VHP@$eZT|2w^cb@AE=re{_@!xL$ZUKY8iwS@~jtPK2j&Gk6ckiJL3jdX# zBJQxiazFP_Ih3MQv6tDW*FZ*i0mOzU&q~n^`KPkEBcuaiX5Huo%x73(EKwlxW-inkrCy2 zb%IryBJbF*_!nU{0CxGGwdd~yZo+AU-H?!O)JM54s`DFR{!ZW~ocxiG%BITnZSb)9 zL;P94`8$C(oZc0=m7xs4bR&Ir{@L8T`nK_F@QmYRFc=J;gZ6oR70`M2Th22-k4l~N z7Gq~cka9kvepVLsV>UAR-5~1chE@Y;D>__G450 z@7lMvI&;JHqcv2=AblOI4B+gSO=5NNJBxrOughuC3E=E{*0J5?@Vd4wWq=BzAh>$x zXJd#mn7vn@AFf21Va1OuJF?BgcvpN(=cr1g8tP<#sE<$4j017{Ku|fZhF_ejZ!1Rt z{hYpHfGto*UI~C#)EGY 0); + assertTrue(this->isReady()); + + // Initialize primitive + mesh->createBuffers( + QUAD_VERTICE_COUNT * text.size(), + QUAD_INDICE_COUNT * text.size() + ); + + // Setup Scales + info->length = 0; + info->realLength = 0; + info->lines.clear(); + info->lineHeight = this->getLineHeight(fontSize); + + // Prepare the line counters + info->addLine(0, 0); + + // Reset Dimensions + char c; + Tile tile; + info->width = info->height = 0; + float_t x = 0; + float_t y = 0; + size_t i = 0; + size_t j = 0; + glm::vec2 xy0(0, 0); + glm::vec2 tileSize = glm::vec2(tileset->getTileWidth(), tileset->getTileHeight()); + + // Buffer quads + while(c = text[i++]) { + if(c == FONT_SPACE) { + + // Did this space cause a newline? + if(maxWidth != -1 && xy0.x > maxWidth) { + info->addLine(i, 0); + info->width = mathMax(info->width, xy0.x); + xy0.x = 0; + xy0.y += tileSize.y; + info->height = mathMax(info->height, xy0.y); + continue; + } + + xy0.x += tileSize.x; + continue; + } + + if(c == FONT_NEWLINE) { + info->addLine(i, 0); + info->width = mathMax(info->width, xy0.x); + xy0.x = 0; + xy0.y += tileSize.y; + info->height = mathMax(info->height, xy0.y); + continue; + } + + // Check for wrapping, todo. + if(maxWidth != -1 && (xy0.x+tileSize.x) > maxWidth) { + // We've exceeded the edge, go back to the start of the word and newline. + + // Go back to the previous (still current) line and remove the chars + + // Next line begins with this word + } + + tile = this->tileset->getTile(c); + QuadMesh::bufferQuadMesh(mesh, + xy0, tile.uv0, + xy0+tileSize, tile.uv1, + j * QUAD_VERTICE_COUNT, j * QUAD_INDICE_COUNT + ); + xy0.x += tileSize.x;//TODO: Spacing? + j++; + } + + info->width = mathMax(info->width, xy0.x); + info->height = mathMax(info->height, xy0.y + (xy0.x > 0 ? tileSize.y : 0)); +} + +bool_t BitmapFont::isReady() { + if(this->texture == nullptr) return false; + if(this->tileset == nullptr) return false; + return this->texture->isReady(); +} + +Texture * BitmapFont::getTexture() { + return this->texture; +} + +void BitmapFont::draw(Mesh *mesh, int32_t start, int32_t len) { + assertNotNull(mesh); + + mesh->draw( + MESH_DRAW_MODE_TRIANGLES, + start * QUAD_INDICE_COUNT, + len == -1 ? len : len * QUAD_INDICE_COUNT + ); +} + +float_t BitmapFont::getLineHeight(float_t fontSize) { + return 16.0f; +} + +float_t BitmapFont::getDefaultFontSize() { + return 16.0f; +} \ No newline at end of file diff --git a/src/dawn/display/font/BitmapFont.hpp b/src/dawn/display/font/BitmapFont.hpp new file mode 100644 index 00000000..be18639b --- /dev/null +++ b/src/dawn/display/font/BitmapFont.hpp @@ -0,0 +1,31 @@ +// Copyright (c) 2023 Dominic Masters +// +// This software is released under the MIT License. +// https://opensource.org/licenses/MIT + +#pragma once +#include "Font.hpp" +#include "display/Tileset.hpp" + +namespace Dawn { + class BitmapFont : public Font { + protected: + + public: + Texture *texture = nullptr; + TilesetGrid *tileset = nullptr; + + void buffer( + std::string text, + float_t fontSize, + float_t maxWidth, + Mesh *mesh, + struct FontMeasure *info + ) override; + bool_t isReady() override; + Texture * getTexture() override; + void draw(Mesh *mesh, int32_t startCharacter, int32_t length) override; + float_t getLineHeight(float_t fontSize) override; + float_t getDefaultFontSize() override; + }; +} \ No newline at end of file diff --git a/src/dawn/display/font/CMakeLists.txt b/src/dawn/display/font/CMakeLists.txt index f70ece11..ebbca010 100644 --- a/src/dawn/display/font/CMakeLists.txt +++ b/src/dawn/display/font/CMakeLists.txt @@ -1,11 +1,12 @@ -# Copyright (c) 2022 Dominic Masters -# -# This software is released under the MIT License. -# https://opensource.org/licenses/MIT - -# Sources -target_sources(${DAWN_TARGET_NAME} - PRIVATE - TrueTypeFont.cpp - FontMeasure.cpp +# Copyright (c) 2022 Dominic Masters +# +# This software is released under the MIT License. +# https://opensource.org/licenses/MIT + +# Sources +target_sources(${DAWN_TARGET_NAME} + PRIVATE + BitmapFont.cpp + TrueTypeFont.cpp + FontMeasure.cpp ) \ No newline at end of file diff --git a/src/dawn/scene/components/ui/CMakeLists.txt b/src/dawn/scene/components/ui/CMakeLists.txt index baaae6f9..0fa3da19 100644 --- a/src/dawn/scene/components/ui/CMakeLists.txt +++ b/src/dawn/scene/components/ui/CMakeLists.txt @@ -9,6 +9,7 @@ target_sources(${DAWN_TARGET_NAME} UICanvas.cpp UIComponent.cpp UILabel.cpp + UIImage.cpp ) add_subdirectory(menu) \ No newline at end of file diff --git a/src/dawn/scene/components/ui/UIComponent.cpp b/src/dawn/scene/components/ui/UIComponent.cpp index 5d570b7b..739bf0bf 100644 --- a/src/dawn/scene/components/ui/UIComponent.cpp +++ b/src/dawn/scene/components/ui/UIComponent.cpp @@ -28,8 +28,8 @@ UIComponentDimensional * UIComponent::getParentDimensional() { void UIComponent::updateAlignment() { if(!this->alignmentNeedsUpdating) return; - auto dimensional = this->getParentDimensional(); auto align = (glm::vec4)this->alignment; + auto dimensional = this->getParentDimensional(); auto translate = this->transform->getLocalPosition(); UIComponent::calculateDimensions( @@ -51,7 +51,6 @@ void UIComponent::updateAlignment() { this->transform->setLocalPosition(translate); this->alignmentNeedsUpdating = false; - this->eventAlignmentUpdated.invoke(); } diff --git a/src/dawn/scene/components/ui/UIImage.cpp b/src/dawn/scene/components/ui/UIImage.cpp new file mode 100644 index 00000000..69228d48 --- /dev/null +++ b/src/dawn/scene/components/ui/UIImage.cpp @@ -0,0 +1,66 @@ +// Copyright (c) 2023 Dominic Masters +// +// This software is released under the MIT License. +// https://opensource.org/licenses/MIT + +#include "UIImage.hpp" +#include "game/DawnGame.hpp" + +using namespace Dawn; + +UIImage::UIImage(SceneItem *item) : + texture(nullptr), + UIComponent(item) +{ + +} + +float_t UIImage::getContentWidth() { + if(this->texture != nullptr) return this->texture->getWidth(); + return this->width; +} + +float_t UIImage::getContentHeight() { + if(this->texture != nullptr) return this->texture->getHeight(); + return this->height; +} + +std::vector UIImage::getPassItems( + glm::mat4 proj, glm::mat4 view +) { + struct ShaderPassItem item; + auto shader = &getGame()->renderManager.uiShaderProgram; + item.shaderProgram = shader; + item.colorValues[shader->paramColor] = this->color; + item.matrixValues[shader->paramProjection] = proj; + item.matrixValues[shader->paramView] = view; + item.matrixValues[shader->paramModel] = this->transform->getWorldTransform(); + if(this->texture == nullptr) { + item.boolValues[shader->paramHasTexture] = false; + } else { + item.boolValues[shader->paramHasTexture] = true; + item.textureSlots[0] = this->texture; + item.textureValues[shader->paramTexture] = 0; + } + item.w = this->transform->getWorldPosition().z; + item.renderFlags = RENDER_MANAGER_RENDER_FLAG_BLEND; + item.mesh = &mesh; + + return { item }; +} + +void UIImage::onStart() { + UIComponent::onStart(); + + useEvent([&]{ + QuadMesh::bufferPositions(&mesh, + glm::vec2(0, 0), glm::vec2(width, height), 0 + ); + }, this->eventAlignmentUpdated); + + QuadMesh::initQuadMesh(&mesh, + glm::vec2(0, 0), glm::vec2(0, 0), + glm::vec2(width, height), glm::vec2(1, 1), + 0.0f + ); +} \ No newline at end of file diff --git a/src/dawn/scene/components/ui/UIImage.hpp b/src/dawn/scene/components/ui/UIImage.hpp new file mode 100644 index 00000000..f0ce0992 --- /dev/null +++ b/src/dawn/scene/components/ui/UIImage.hpp @@ -0,0 +1,30 @@ +// Copyright (c) 2023 Dominic Masters +// +// This software is released under the MIT License. +// https://opensource.org/licenses/MIT + +#pragma once +#include "UIComponent.hpp" +#include "display/mesh/QuadMesh.hpp" + +namespace Dawn { + class UIImage : public UIComponent, public UIComponentRenderable { + private: + Mesh mesh; + + public: + struct Color color = COLOR_WHITE; + // StateProperty width; + // StateProperty height; + StateProperty texture; + + UIImage(SceneItem *item); + + float_t getContentWidth() override; + float_t getContentHeight() override; + std::vector getPassItems( + glm::mat4 proj, glm::mat4 view + ) override; + void onStart() override; + }; +} \ No newline at end of file diff --git a/src/dawn/scene/components/ui/UILabel.cpp b/src/dawn/scene/components/ui/UILabel.cpp index 23b5697c..2b1d46b2 100644 --- a/src/dawn/scene/components/ui/UILabel.cpp +++ b/src/dawn/scene/components/ui/UILabel.cpp @@ -82,7 +82,6 @@ std::vector UILabel::getPassItems( } float_t UILabel::getContentWidth() { - if(this->maxWidth > 0) return this->maxWidth; if(!this->hasText()) return 0; this->updateMesh(); return this->measure.getWidth(); diff --git a/src/dawnhelloworld/CMakeLists.txt b/src/dawnhelloworld/CMakeLists.txt index 8de667f3..71900d88 100644 --- a/src/dawnhelloworld/CMakeLists.txt +++ b/src/dawnhelloworld/CMakeLists.txt @@ -14,4 +14,7 @@ target_include_directories(${DAWN_TARGET_NAME} # Subdirs add_subdirectory(game) -add_subdirectory(save) \ No newline at end of file +add_subdirectory(save) + +# Assets +tool_bitmapfont(testbitmap bmfont.png 16 16) \ No newline at end of file diff --git a/src/dawnhelloworld/scenes/HelloWorldScene.hpp b/src/dawnhelloworld/scenes/HelloWorldScene.hpp index 3189df48..de35a7b2 100644 --- a/src/dawnhelloworld/scenes/HelloWorldScene.hpp +++ b/src/dawnhelloworld/scenes/HelloWorldScene.hpp @@ -6,24 +6,56 @@ #pragma once #include "scene/Scene.hpp" #include "prefabs/SimpleSpinningCubePrefab.hpp" +#include "scene/components/ui/UILabel.hpp" +#include "scene/components/ui/UIImage.hpp" +#include "display/font/BitmapFont.hpp" namespace Dawn { class HelloWorldScene : public Scene { protected: Camera *camera; SimpleSpinningCubePrefab *cube; + UICanvas *canvas; + UILabel *label; + UIImage *image; + BitmapFont font; void stage() override { camera = Camera::create(this); camera->transform->lookAt(glm::vec3(0, 0, 8), glm::vec3(0, 0, 0)); cube = SimpleSpinningCubePrefab::create(this); + + canvas = UICanvas::create(this); + + auto imageItem = this->createSceneItem(); + image = imageItem->addComponent(); + image->color = COLOR_BLACK; + imageItem->transform.setParent(canvas->transform); + + auto labelItem = this->createSceneItem(); + label = labelItem->addComponent(); + labelItem->transform.setParent(canvas->transform); + + auto assMan = &this->game->assetManager; + auto texture = assMan->get("testbitmap_texture"); + auto tileset = assMan->get("testbitmap_tileset"); + this->font.texture = &texture->texture; + this->font.tileset = &tileset->tileset; + + label->text = "Hello World, how are you today? I hope you are doing well. I really like the fact I can ramble in my text for once."; + label->font = &font; + label->maxWidth = 275; + + image->alignment = glm::vec4(0, 0, label->getContentWidth(), label->getContentHeight()); } std::vector getRequiredAssets() override { auto assMan = &this->game->assetManager; std::vector assets; vectorAppend(&assets, SimpleSpinningCubePrefab::getRequiredAssets(assMan)); + assets.push_back(assMan->get("testbitmap_texture")); + assets.push_back(assMan->get("testbitmap_tileset")); return assets; } diff --git a/src/dawnopengl/display/shader/ShaderProgram.cpp b/src/dawnopengl/display/shader/ShaderProgram.cpp index a1d4f1fd..4cbfec0c 100644 --- a/src/dawnopengl/display/shader/ShaderProgram.cpp +++ b/src/dawnopengl/display/shader/ShaderProgram.cpp @@ -69,6 +69,11 @@ void ShaderProgram::compileShader( glBindAttribLocation(this->shaderProgram, 1, "aTexCoord"); } +void ShaderProgram::bindAttributeLocation(std::string name, int32_t location) { + if(this->shaderProgram == -1) throw "Shader has not yet been compiled"; + glBindAttribLocation(this->shaderProgram, location, name.c_str()); +} + void ShaderProgram::setTexture(shaderparameter_t param, textureslot_t slot) { glUniform1i(param, slot); } diff --git a/src/dawnopengl/display/shader/ShaderProgram.hpp b/src/dawnopengl/display/shader/ShaderProgram.hpp index 4dc8f504..532de603 100644 --- a/src/dawnopengl/display/shader/ShaderProgram.hpp +++ b/src/dawnopengl/display/shader/ShaderProgram.hpp @@ -25,14 +25,23 @@ namespace Dawn { protected: /** - * Compiles a GLSL shader and stores it on the GPU, updates the underlying - * pointers for you. + * Compiles a GLSL/HLSL shader and stores it on the GPU, updates the + * underlying pointers for you. * * @param vertexShader The string source of the vertex shader. * @param fragmentShader The string source of the fragment shader. */ void compileShader(std::string vertexShader, std::string fragmentShader); + /** + * Typically HLSL only, this method allows you to specify where vbo + * attributes are bound. Typically 0 for positions, 1 for coordinates, + * etc. + * + * @param name Attribute name in the HLSL shader. + * @param location Index pointing to which location it is to be bound to. + */ + void bindAttributeLocation(std::string name, int32_t location); public: /** diff --git a/src/dawnopengl/display/shader/SimpleTexturedShaderProgram.hpp b/src/dawnopengl/display/shader/SimpleTexturedShaderProgram.hpp index c81111fb..458211ee 100644 --- a/src/dawnopengl/display/shader/SimpleTexturedShaderProgram.hpp +++ b/src/dawnopengl/display/shader/SimpleTexturedShaderProgram.hpp @@ -83,6 +83,9 @@ namespace Dawn { "return o_Color;\n" "}\n" ); + + this->bindAttributeLocation("aPos", 0); + this->bindAttributeLocation("aTexCoord", 1); #else #error Shader Type must be either GLSL or HLSL #endif diff --git a/src/dawntools/tools/CMakeLists.txt b/src/dawntools/tools/CMakeLists.txt index 07e79cd9..cc0a33ab 100644 --- a/src/dawntools/tools/CMakeLists.txt +++ b/src/dawntools/tools/CMakeLists.txt @@ -36,6 +36,11 @@ function(tool_tileset targetTileset targetTexture in cols rows) add_dependencies(${DAWN_TARGET_NAME} ${targetTileset}) endfunction() +# Bitmap Font +function(tool_bitmapfont target in columns rows) + tool_tileset(${target}_tileset ${target}_texture ${in} ${columns} ${rows}) +endfunction() + # TrueType Tool function(tool_truetype target in out width height fontSize) add_custom_target(${target} @@ -46,7 +51,7 @@ function(tool_truetype target in out width height fontSize) add_dependencies(${DAWN_TARGET_NAME} ${target}) endfunction() -# UI Tool +# UI Tool function(tool_ui target in) add_custom_target(${target} COMMAND uigen --input="${DAWN_ASSETS_SOURCE_DIR}/${in}" --output="${DAWN_GENERATED_DIR}/prefabs/ui/${target}"