From 4f6e55c0b065121f95c192a85bf4172034a8056e Mon Sep 17 00:00:00 2001 From: Guido Diego Quispe Robles Date: Sun, 28 May 2023 13:47:48 -0300 Subject: [PATCH] new Tutorial tutorial with sprite offset, sprite sheet and texture filtering --- .../inc/renderer/core/2d/renderer_core_2d.hpp | 3 + engine/inc/renderer/core/2d/sprite/sprite.hpp | 2 +- .../src/renderer/core/2d/renderer_core_2d.cpp | 13 +- engine/src/renderer/core/2d/sprite/sprite.cpp | 1 + tutorials/10-sprite-sheet/inc/fontSprite.hpp | 34 ++++ tutorials/10-sprite-sheet/inc/tutorial_10.hpp | 9 + tutorials/10-sprite-sheet/src/fontSprite.cpp | 106 ++++++++++++ tutorials/10-sprite-sheet/src/main.cpp | 7 +- tutorials/10-sprite-sheet/src/tutorial_10.cpp | 162 +++++++++++++++++- 9 files changed, 321 insertions(+), 16 deletions(-) create mode 100644 tutorials/10-sprite-sheet/inc/fontSprite.hpp create mode 100644 tutorials/10-sprite-sheet/src/fontSprite.cpp diff --git a/engine/inc/renderer/core/2d/renderer_core_2d.hpp b/engine/inc/renderer/core/2d/renderer_core_2d.hpp index aaa1d4d..bbb35a8 100644 --- a/engine/inc/renderer/core/2d/renderer_core_2d.hpp +++ b/engine/inc/renderer/core/2d/renderer_core_2d.hpp @@ -13,6 +13,7 @@ #include "debug/debug.hpp" #include "renderer/core/2d/sprite/sprite.hpp" #include "renderer/core/texture/renderer_core_texture_buffers.hpp" +#include "renderer/3d/pipeline/shared/pipeline_texture_mapping_type.hpp" #include "renderer/core/texture/models/texture.hpp" #include "renderer/renderer_settings.hpp" #include @@ -30,6 +31,8 @@ class RendererCore2D { void render(const Sprite& sprite, const RendererCoreTextureBuffers& texBuffers, Texture* texture); + void setTextureMappingType(const PipelineTextureMappingType textureMappingType); + private: void setPrim(); void setLod(); diff --git a/engine/inc/renderer/core/2d/sprite/sprite.hpp b/engine/inc/renderer/core/2d/sprite/sprite.hpp index 6580a2d..439a5c2 100644 --- a/engine/inc/renderer/core/2d/sprite/sprite.hpp +++ b/engine/inc/renderer/core/2d/sprite/sprite.hpp @@ -24,7 +24,7 @@ class Sprite { ~Sprite(); u32 id; - Vec2 position, size; + Vec2 position, size, offset; float scale; Color color; SpriteMode mode; diff --git a/engine/src/renderer/core/2d/renderer_core_2d.cpp b/engine/src/renderer/core/2d/renderer_core_2d.cpp index 3f4f397..0b83faa 100644 --- a/engine/src/renderer/core/2d/renderer_core_2d.cpp +++ b/engine/src/renderer/core/2d/renderer_core_2d.cpp @@ -84,10 +84,10 @@ void RendererCore2D::render(const Sprite& sprite, else if (sizeY > sizeX) texS = texMax / (sizeY / sizeX); - rect->t0.s = sprite.flipHorizontal ? texS : 0.0F; - rect->t0.t = sprite.flipVertical ? texT : 0.0F; - rect->t1.s = sprite.flipHorizontal ? 0.0F : texS; - rect->t1.t = sprite.flipVertical ? 0.0F : texT; + rect->t0.s = sprite.flipHorizontal ? (texS+sprite.offset.x) : sprite.offset.x; + rect->t0.t = sprite.flipVertical ? (texT+sprite.offset.y) : sprite.offset.y; + rect->t1.s = sprite.flipHorizontal ? sprite.offset.x : (texS+sprite.offset.x); + rect->t1.t = sprite.flipVertical ? sprite.offset.y : (texT+sprite.offset.y); rect->color.r = sprite.color.r; rect->color.g = sprite.color.g; @@ -131,4 +131,9 @@ void RendererCore2D::render(const Sprite& sprite, context = !context; } +void RendererCore2D::setTextureMappingType(const PipelineTextureMappingType textureMappingType){ + lod.mag_filter = textureMappingType; + lod.min_filter = textureMappingType; +} + } // namespace Tyra diff --git a/engine/src/renderer/core/2d/sprite/sprite.cpp b/engine/src/renderer/core/2d/sprite/sprite.cpp index 59a0e86..821a9f2 100644 --- a/engine/src/renderer/core/2d/sprite/sprite.cpp +++ b/engine/src/renderer/core/2d/sprite/sprite.cpp @@ -18,6 +18,7 @@ Sprite::Sprite() { flipVertical = false; size.set(32.0F, 32.0F); position.set(100.0F, 100.0F); + offset.set(0.0F,0.0F); scale = 1.0F; mode = MODE_REPEAT; setDefaultColor(); diff --git a/tutorials/10-sprite-sheet/inc/fontSprite.hpp b/tutorials/10-sprite-sheet/inc/fontSprite.hpp new file mode 100644 index 0000000..3d089e3 --- /dev/null +++ b/tutorials/10-sprite-sheet/inc/fontSprite.hpp @@ -0,0 +1,34 @@ +/* +# _____ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licensed under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include + +namespace Tyra{ + +extern const int charSize; +extern const int font_char[96]; +extern const int font_charWidth[96]; + +class Font{ + public: + Font(); + void LoadFont(TextureRepository& repository, Renderer2D* renderer); + void FreeFont(TextureRepository& repository); + void DrawText(const char* text, int x, int y, Color color); + void DrawText(const std::string& text, int x, int y, Color color); + private: + Renderer2D* renderer2D; + Sprite all_font; + std::array font; +}; + +} // namespace Tyra \ No newline at end of file diff --git a/tutorials/10-sprite-sheet/inc/tutorial_10.hpp b/tutorials/10-sprite-sheet/inc/tutorial_10.hpp index 3f14763..f0e0779 100644 --- a/tutorials/10-sprite-sheet/inc/tutorial_10.hpp +++ b/tutorials/10-sprite-sheet/inc/tutorial_10.hpp @@ -11,6 +11,7 @@ #pragma once #include +#include "fontSprite.hpp" namespace Tyra { @@ -27,8 +28,16 @@ class Tutorial10 : public Game { void loadSprite(); Engine* engine; + Pad* pad; + Font font; Sprite sprite; + Sprite spriteFlip; + Sprite spriteScale; + Sprite spriteStrech; + std::string str_filter; + + PipelineTextureMappingType textureFilter; }; } // namespace Tyra diff --git a/tutorials/10-sprite-sheet/src/fontSprite.cpp b/tutorials/10-sprite-sheet/src/fontSprite.cpp new file mode 100644 index 0000000..38f842e --- /dev/null +++ b/tutorials/10-sprite-sheet/src/fontSprite.cpp @@ -0,0 +1,106 @@ +#include "fontSprite.hpp" + +namespace Tyra{ + +const int charSize = 96; + +const int font_char[charSize]{ + ' ', '!', '"', ' ', '$', '%', ' ', '´', '(', ')', ' ', '+', ',', '-', '.', '/', + '0', '1', '2', '3', '4', '5', '6', '2', '8', '9', ':', ';', '“', '=', '”', '?', + ' ', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', + 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', ' ', ' ', ' ', ' ', ' ', + ' ', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', + 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '[', '♪', ']', '~', ' ' +}; + +const int font_charWidth[charSize] { + 0, 1, 3, 0, 5, 9, 0, 9, 3, 3, 0, 5, 2, 2, 1, 4, + 4, 2, 4, 4, 5, 4, 4, 4, 4, 4, 1, 2, 4, 5, 4, 4, + 0, 6, 5, 5, 5, 4, 4, 5, 5, 1, 4, 5, 4, 7, 5, 5, + 5, 5, 5, 5, 5, 5, 6, 7, 5, 5, 4, 0, 0, 0, 0, 0, + 0, 5, 4, 4, 4, 4, 3, 4, 4, 1, 2, 4, 1, 7, 4, 4, + 4, 4, 3, 4, 3, 4, 5, 7, 4, 4, 4, 2, 5, 2, 6, 0, +}; + +Font::Font(){ } + + +void Font::LoadFont(TextureRepository& repository, Renderer2D* renderer){ + renderer2D = renderer; + + float height = 16.0F; + float width = 16.0F; + + all_font.mode = MODE_REPEAT; + all_font.size = Vec2(255,127); + + auto filepath = FileUtils::fromCwd("earthbound-Font.png"); + auto* texture = repository.add(filepath); + texture->addLink(all_font.id); + + int column = 0; + int arrow = 0; + + for(int i=0; i < 96; i++){ + font[i].id = all_font.id; + font[i].mode = MODE_REPEAT; + font[i].size = Vec2(width,height); + font[i].offset = Vec2(width*column,height*arrow); + column++; + if(column == 16){ + arrow++; + column = 0; + } + } +} + +void Font::FreeFont(TextureRepository& repository){ + repository.freeBySprite(all_font); + for(int i=0;i<96;i++){ repository.freeBySprite(font[i]); } +} + +void Font::DrawText(const char* text, int x, int y, Color color){ + DrawText(std::string(text),x,y,color); +} + +void Font::DrawText(const std::string& text, int x, int y, Color color){ + int sizeText = text.size(); + + int offsetY = 0; + int offsetX = 0; + + Sprite spr_font; + int pos_font; + + for (int i = 0; i < sizeText; i++) + { + pos_font = text[i]; + spr_font = font[0]; + + for(int j=0; j < charSize; j++){ + if(pos_font == font_char[j]){ + pos_font = j; + spr_font = font[j]; + spr_font.color = color; + spr_font.position = Vec2(x + offsetX, y + offsetY); + break; + } + } + + if (pos_font == '\n') + { + offsetY += 18; + offsetX = 0.0f; + }else{ + if ((pos_font != ' ') && (pos_font != '\t')){ + renderer2D->render(spr_font); + offsetX += font_charWidth[pos_font] + 2; + }else{ + offsetX += 2; + } + + } + } +} + +} // namespace Tyra \ No newline at end of file diff --git a/tutorials/10-sprite-sheet/src/main.cpp b/tutorials/10-sprite-sheet/src/main.cpp index 087f27c..21559e1 100644 --- a/tutorials/10-sprite-sheet/src/main.cpp +++ b/tutorials/10-sprite-sheet/src/main.cpp @@ -12,10 +12,9 @@ #include "tutorial_10.hpp" /** - * In this tutorial we will learn how to: - * - Load png file and create texture from it - * - Create sprite class - * - Clear screen and render sprite + * In this tutorial we will learn: + * - How works the offset of the sprite + * - how works the texture mapping */ int main() { diff --git a/tutorials/10-sprite-sheet/src/tutorial_10.cpp b/tutorials/10-sprite-sheet/src/tutorial_10.cpp index 09db4e6..9733a34 100644 --- a/tutorials/10-sprite-sheet/src/tutorial_10.cpp +++ b/tutorials/10-sprite-sheet/src/tutorial_10.cpp @@ -13,30 +13,150 @@ namespace Tyra { -Tutorial10::Tutorial10(Engine* t_engine) : engine(t_engine) {} +Tutorial10::Tutorial10(Engine* t_engine) : engine(t_engine), pad(&t_engine->pad){ } Tutorial10::~Tutorial10() { engine->renderer.getTextureRepository().freeBySprite(sprite); + engine->renderer.getTextureRepository().freeBySprite(spriteFlip); + engine->renderer.getTextureRepository().freeBySprite(spriteScale); + engine->renderer.getTextureRepository().freeBySprite(spriteStrech); + font.FreeFont(engine->renderer.getTextureRepository()); } void Tutorial10::init() { engine->renderer.setClearScreenColor(Color(32.0F, 32.0F, 32.0F)); + /** Load all the sprites of the sprite sheet. */ + font.LoadFont(engine->renderer.getTextureRepository(),&engine->renderer.renderer2D); + /** Sprite contains rectangle information. */ loadSprite(); /** Texture contains png image. */ loadTexture(); + + /** Set the texture filtering. */ + textureFilter = TyraNearest; + + engine->renderer.core.renderer2D.setTextureMappingType(textureFilter); + } void Tutorial10::loop() { auto& renderer = engine->renderer; + /** Change the texture filtering of the texture. + * + * The options are: + * TyraNearest (Point Sampling): + * uses the color of the texel closest to the pixel center for the pixel color. + * + * TyraLinear (Bilinear Sampling): + * the four nearest texels to the pixel center are sampled + * and their colors are combined by weighted average according to distance. + * + * By default it is linear. + */ + + renderer.core.renderer2D.setTextureMappingType(textureFilter); + + if(engine->pad.getClicked().Cross){ + textureFilter = TyraNearest; + }else if(engine->pad.getClicked().Circle){ + textureFilter = TyraLinear; + } + + static int timer = 0; + + /** Move the offset of the sprites. + * + * The offset of the sprite need to be equal or greater to zero + * or a graphical error occurs. + * + * And the sum of the sprite size and the offset must be less than 1024 + * or a graphical error occurs. + * + * Sum with repeat mode: + * Max = sprite.size + sprite.offset. + * + * Sum with strecth mode: + * Max = original size of the image + sprite.offset. + * + * If it continues moving past these values + * in a moment it will return to normal but + * the error will be repeated with another value. + */ + + if(timer <= 0){ + if(pad->getLeftJoyPad().v <= 100){ //up + sprite.offset -= Vec2(0,16); + spriteFlip.offset -= Vec2(0,16); + spriteScale.offset -= Vec2(0,16); + spriteStrech.offset -= Vec2(0,16); timer = 20; + printf("offset: %f,%f\n",sprite.offset.x,sprite.offset.y); + }else if(pad->getLeftJoyPad().v >= 200 ){ //down + sprite.offset += Vec2(0,16); + spriteFlip.offset += Vec2(0,16); + spriteScale.offset += Vec2(0,16); + spriteStrech.offset += Vec2(0,16); timer = 20; + printf("offset: %f,%f\n",sprite.offset.x,sprite.offset.y); + } + + if (pad->getLeftJoyPad().h <= 100) { // left + sprite.offset -= Vec2(16,0); + spriteFlip.offset -= Vec2(16,0); + spriteScale.offset -= Vec2(16,0); + spriteStrech.offset -= Vec2(16,0); timer = 20; + printf("offset: %f,%f\n",sprite.offset.x,sprite.offset.y); + } else if (pad->getLeftJoyPad().h >= 200 ) { //right + sprite.offset += Vec2(16,0); + spriteFlip.offset += Vec2(16,0); + spriteScale.offset += Vec2(16,0); + spriteStrech.offset += Vec2(16,0); timer = 20; + printf("offset: %f,%f\n",sprite.offset.x,sprite.offset.y); + } + + }else{ + timer--; + } + /** Begin frame will clear our screen. */ renderer.beginFrame(); /** Render sprite. */ renderer.renderer2D.render(sprite); + renderer.core.renderer2D.setTextureMappingType(TyraLinear); + renderer.renderer2D.render(spriteFlip); + renderer.core.renderer2D.setTextureMappingType(textureFilter); + renderer.renderer2D.render(spriteScale); + renderer.renderer2D.render(spriteStrech); + + font.DrawText("tests with sprite sheet",(512/2)-60,10,Color(255,255,255)); + font.DrawText("Press Cross for nearest filter",(512/2)-70,50,Color(255,255,255)); + font.DrawText("Press Circle for linear filter",(512/2)-65,70,Color(255,255,255)); + font.DrawText("Use left stick for move the offsets of the sprites",(512/2)-110,90,Color(255,255,255)); + font.DrawText("sprite 32x32\n scaled x3",sprite.position.x+16,sprite.position.y-40,Color(255,255,255)); + font.DrawText("sprite 32x32\nscaled x3 with flip",spriteFlip.position.x+4,spriteFlip.position.y-40,Color(255,255,255)); + font.DrawText("sprite 96x96 with\n repeat mode",spriteScale.position.x+6,spriteScale.position.y-40,Color(255,255,255)); + font.DrawText("sprite 96x96 with\n stretch mode",spriteStrech.position.x+6,spriteStrech.position.y-40,Color(255,255,255)); + + if(textureFilter == TyraLinear){ str_filter = "Filter: Linear";} + else{ str_filter = "Filter: Nearest";} + + static int posX1 = sprite.position.x + 16; + static int posX2 = spriteFlip.position.x + 16; + static int posX3 = spriteStrech.position.x + 16; + static int posX4 = spriteScale.position.x + 16; + + static int posY1 = sprite.position.y + (sprite.size.y * sprite.scale) + 16; + static int posY2 = spriteFlip.position.y + (spriteFlip.size.y * spriteFlip.scale) + 16; + static int posY3 = spriteStrech.position.y + (spriteStrech.size.y * spriteStrech.scale) + 16; + static int posY4 = spriteScale.position.y + (spriteScale.size.y * spriteScale.scale) + 16; + + font.DrawText(str_filter,posX1,posY1,Color(255,255,255)); + font.DrawText("Filter: Linear",posX2,posY2,Color(255,255,255)); + font.DrawText(str_filter,posX3,posY3,Color(255,255,255)); + font.DrawText(str_filter,posX4,posY4,Color(255,255,255)); /** End frame will perform vsync. */ renderer.endFrame(); @@ -45,15 +165,39 @@ void Tutorial10::loop() { void Tutorial10::loadSprite() { const auto& screenSettings = engine->renderer.core.getSettings(); - sprite.mode = SpriteMode::MODE_STRETCH; + int offset = 25; + + sprite.mode = SpriteMode::MODE_REPEAT; /** Let's scale it down */ - sprite.size = Vec2(256.0F, 256.0F); + sprite.size = Vec2(32.0F, 32.0F); + sprite.scale = 3; + + /** Set the position */ + sprite.position = Vec2(offset,screenSettings.getHeight() / 2.0F - sprite.size.y / 2.0F); - /** Set it in screen center */ - sprite.position = - Vec2(screenSettings.getWidth() / 2.0F - sprite.size.x / 2.0F, - screenSettings.getHeight() / 2.0F - sprite.size.y / 2.0F); + spriteFlip.mode = SpriteMode::MODE_REPEAT; + + spriteFlip.flipHorizontal = true; + spriteFlip.flipVertical = true; + + spriteFlip.size = Vec2(32.0F, 32.0F); + + spriteFlip.scale = 3; + + spriteFlip.position = Vec2(((offset*2) + (32.0F*3)),screenSettings.getHeight() / 2.0F - sprite.size.y / 2.0F); + + spriteScale.mode = SpriteMode::MODE_REPEAT; + + spriteScale.size = Vec2(96.0F,96.0F); + + spriteScale.position = Vec2(((offset*3) + ((32.0F*3)*2)),screenSettings.getHeight() / 2.0F - sprite.size.y / 2.0F); + + spriteStrech.mode = SpriteMode::MODE_STRETCH; + + spriteStrech.size = Vec2(96.0F,96.0F); + + spriteStrech.position = Vec2(((offset*4) + ((32.0F*3)*3)),screenSettings.getHeight() / 2.0F - sprite.size.y / 2.0F); TYRA_LOG("Sprite created!"); } @@ -106,6 +250,10 @@ void Tutorial10::loadTexture() { /** Let's assign this texture to sprite. */ texture->addLink(sprite.id); + spriteFlip.id = sprite.id; + spriteScale.id = sprite.id; + spriteStrech.id = sprite.id; + TYRA_LOG("Texture loaded!"); }