Started on tilemaps

This commit is contained in:
OusmBlueNinja 2025-04-13 16:04:03 -05:00
parent 458891b609
commit de16b0acbb
18 changed files with 680 additions and 122 deletions

View File

@ -1,3 +1,4 @@
[LINK] g++ src\build\src\Engine.o src\build\src\main.o src\build\src\Renderer.o src\build\src\Components\CameraComponent.o src\build\src\Components\LightComponent.o src\build\src\Components\SpriteComponent.o src\build\src\Entitys\Object.o src\build\src\utils\FileDialog.o src\build\src\utils\Logging.o src\build\src\utils\Shader.o src\build\src\utils\utils.o src\build\vendor\imgui\imgui.o src\build\vendor\imgui\imgui_demo.o src\build\vendor\imgui\imgui_draw.o src\build\vendor\imgui\imgui_impl_glfw.o src\build\vendor\imgui\imgui_impl_opengl3.o src\build\vendor\imgui\imgui_tables.o src\build\vendor\imgui\imgui_widgets.o -o src\build\app.exe -LC:\msys64\mingw64\lib -lglfw3 -lglew32 -lopengl32 -lgdi32 -lyaml-cpp -lcomdlg32 -lssl -lcrypto
[ERROR] Runtime crash
Command 'src\build\app.exe' returned non-zero exit status 3221225477.
[COMPILE] g++ -std=c++20 -Wall -Isrc/include -Isrc/vendor -Isrc/vendor/imgui -IC:/msys64/mingw64/include -Isrc/include -Isrc/vendor -Isrc/vendor/imgui -IC:/msys64/mingw64/include -Isrc\vendor\imgui -IC:\msys64\mingw64\lib\libyaml-cpp.a -MMD -MP -c src\src\Components\TilemapComponent.cpp -o src\build\src\Components\TilemapComponent.o
[COMPILE] g++ -std=c++20 -Wall -Isrc/include -Isrc/vendor -Isrc/vendor/imgui -IC:/msys64/mingw64/include -Isrc/include -Isrc/vendor -Isrc/vendor/imgui -IC:/msys64/mingw64/include -Isrc\vendor\imgui -IC:\msys64\mingw64\lib\libyaml-cpp.a -MMD -MP -c src\src\utils\utils.cpp -o src\build\src\utils\utils.o
[LINK] g++ src\build\src\Engine.o src\build\src\main.o src\build\src\Renderer.o src\build\src\Components\CameraComponent.o src\build\src\Components\LightComponent.o src\build\src\Components\SpriteComponent.o src\build\src\Components\TilemapComponent.o src\build\src\Entitys\Object.o src\build\src\utils\EngineConfig.o src\build\src\utils\FileDialog.o src\build\src\utils\Logging.o src\build\src\utils\Shader.o src\build\src\utils\utils.o src\build\vendor\imgui\imgui.o src\build\vendor\imgui\imgui_demo.o src\build\vendor\imgui\imgui_draw.o src\build\vendor\imgui\imgui_impl_glfw.o src\build\vendor\imgui\imgui_impl_opengl3.o src\build\vendor\imgui\imgui_tables.o src\build\vendor\imgui\imgui_widgets.o -o src\build\app.exe -LC:\msys64\mingw64\lib -lglfw3 -lglew32 -lopengl32 -lgdi32 -lyaml-cpp -lcomdlg32 -lssl -lcrypto
[RUN] Executed app.exe successfully.

View File

@ -47,12 +47,18 @@ Size=1141,287
Collapsed=0
DockId=0x00000008,0
[Window][Tilemap Editor]
Pos=265,19
Size=1141,869
Collapsed=0
DockId=0x00000007,1
[Docking][Data]
DockSpace ID=0x11111111 Window=0x1BBC0F80 Pos=0,19 Size=1920,1158 Split=X
DockNode ID=0x00000003 Parent=0x11111111 SizeRef=1406,1158 Split=X
DockNode ID=0x00000001 Parent=0x00000003 SizeRef=263,701 HiddenTabBar=1 Selected=0x12EF0F59
DockNode ID=0x00000002 Parent=0x00000003 SizeRef=1141,701 Split=Y Selected=0xC450F867
DockNode ID=0x00000007 Parent=0x00000002 SizeRef=606,869 CentralNode=1 HiddenTabBar=1 Selected=0xC450F867
DockNode ID=0x00000007 Parent=0x00000002 SizeRef=606,869 CentralNode=1 Selected=0xC450F867
DockNode ID=0x00000008 Parent=0x00000002 SizeRef=606,287 HiddenTabBar=1 Selected=0xEA83D666
DockNode ID=0x00000004 Parent=0x11111111 SizeRef=512,1158 Split=Y Selected=0x36DC96AB
DockNode ID=0x00000005 Parent=0x00000004 SizeRef=407,835 HiddenTabBar=1 Selected=0x36DC96AB

View File

@ -32,36 +32,6 @@ objects:
texture: C:\Users\spenc\OneDrive\Pictures\49555.jpg
normalMap: ""
children: []
- name: Bark
position: [1024, 0]
layer: -1
components:
- type: SpriteComponent
texture: C:\Users\spenc\OneDrive\Pictures\textures\bark_willow_02_diff_1k.png
normalMap: C:\Users\spenc\OneDrive\Pictures\textures\bark_willow_02_nor_gl_1k.png
children: []
- name: Sun
position: [-5000, -5000]
layer: 1
components:
- type: LightComponent
color:
- 0.990196049
- 0.943370163
- 0.791186035
intensity: 2.0999999
radius: 100000000
falloff: 0.100000001
type: 0
children: []
- name: Rocks
position: [0, 0]
layer: -1
components:
- type: SpriteComponent
texture: C:\Users\spenc\OneDrive\Pictures\ganges_river_pebbles_diff_1k.png
normalMap: C:\Users\spenc\OneDrive\Pictures\ganges_river_pebbles_nor_gl_1k.png
children: []
- name: World
position: [-436, 248]
layer: 0

View File

@ -0,0 +1,122 @@
engine_version: 0.1.0
scene_name: tilemap
scene_hash: 0ffd3035689b66c87af235dc22993fa7dac416ee447c93a1c4594a6b8aafc289
format_version: 1
objects:
- name: Hello, Create
position: [0, 0]
layer: 0
components:
- type: TilemapComponent
gridSize:
- 10
- 10
tileSize:
- 32
- 32
tiles:
- 961
- 961
- 961
- -1
- -1
- -1
- -1
- -1
- -1
- -1
- -1
- -1
- -1
- -1
- -1
- -1
- -1
- -1
- -1
- -1
- -1
- -1
- -1
- 816
- -1
- 945
- 970
- 970
- -1
- -1
- -1
- -1
- 813
- -1
- -1
- -1
- 970
- -1
- -1
- -1
- -1
- -1
- 748
- 840
- -1
- 970
- -1
- 840
- -1
- -1
- -1
- 812
- -1
- 805
- -1
- 845
- 877
- -1
- 904
- -1
- -1
- -1
- 970
- -1
- -1
- -1
- -1
- 970
- -1
- -1
- -1
- -1
- -1
- 875
- -1
- -1
- -1
- -1
- -1
- -1
- -1
- -1
- -1
- -1
- -1
- -1
- -1
- -1
- -1
- -1
- -1
- -1
- -1
- -1
- -1
- -1
- -1
- -1
- -1
- -1
atlasPath: C:\Users\spenc\OneDrive\Pictures\6656e7221e49a1774d2fb280357e56f8d25d9d95.png
atlasTileSize:
- 32
- 32
children: []

View File

@ -40,36 +40,6 @@ objects:
texture: C:\Users\spenc\OneDrive\Pictures\blue_logo.png
normalMap: C:\Users\spenc\OneDrive\Pictures\images.jpg
children: []
- name: Bark
position: [1024, 0]
layer: -1
components:
- type: SpriteComponent
texture: C:\Users\spenc\OneDrive\Pictures\textures\bark_willow_02_diff_1k.png
normalMap: C:\Users\spenc\OneDrive\Pictures\textures\bark_willow_02_nor_gl_1k.png
children: []
- name: Sun
position: [-5000, -5000]
layer: 1
components:
- type: LightComponent
color:
- 0.992156863
- 0.984313726
- 0.827450991
intensity: 1.25
radius: 100000000
falloff: 0.100000001
type: 0
children: []
- name: Rocks
position: [0, 0]
layer: -1
components:
- type: SpriteComponent
texture: C:\Users\spenc\OneDrive\Pictures\ganges_river_pebbles_diff_1k.png
normalMap: C:\Users\spenc\OneDrive\Pictures\ganges_river_pebbles_nor_gl_1k.png
children: []
- name: World
position: [-436, 248]
layer: 0

View File

@ -0,0 +1,12 @@
#version 330 core
in vec2 vUV;
out vec4 FragColor;
uniform sampler2D uTex;
void main()
{
vec4 tex = texture(uTex, vUV);
if (tex.a < 0.1) discard;
FragColor = tex;
}

View File

@ -0,0 +1,20 @@
#version 330 core
layout(location = 0) in vec2 aPos;
layout(location = 1) in vec2 aUV;
out vec2 vUV;
uniform vec2 uPos;
uniform vec2 uSize;
uniform vec2 uScreen;
uniform vec2 uUVMin;
uniform vec2 uUVMax;
void main()
{
vec2 pos = uPos + aPos * uSize;
gl_Position = vec4((pos / uScreen) * 2.0 - 1.0, 0.0, 1.0);
// Remap UV to tile slice
vUV = mix(uUVMin, uUVMax, aUV);
}

View File

@ -0,0 +1,12 @@
#version 330 core
in vec2 vUV;
out vec4 FragColor;
uniform sampler2D uTex;
void main() {
vec4 color = texture(uTex, vUV);
if (color.a < 0.01)
discard;
FragColor = color;
}

View File

@ -0,0 +1,13 @@
#version 330 core
layout (location = 0) in vec2 aPos;
layout (location = 1) in vec2 aUV;
out vec2 vUV;
uniform vec2 uPos;
uniform vec2 uSize;
uniform vec2 uScreen;
void main() {
vec2 worldPos = aPos * uSize + uPos;
vUV = aUV;
vec2 ndc = (worldPos / uScreen) * 2.0 - 1.0;
gl_Position = vec4(ndc.x, -ndc.y, 0.0, 1.0);
}

View File

@ -0,0 +1,258 @@
#include "TilemapComponent.h"
#include "../utils/Logging.h"
#include "../utils/FileDialog.h"
#include "../utils/utils.h"
#include <stb_image.h>
#include <cstdio>
#include <cstring>
#include <GLFW/glfw3.h>
static int g_selectedTileIndex = 0;
// Constructor: initialize with a default grid (optional)
TilemapComponent::TilemapComponent(Object *owner)
: Component(owner)
{
// For example, create an empty 10x10 tilemap
SetGridSize(glm::ivec2(10, 10));
// Optionally set a default tile size
m_TileSize = glm::ivec2(32, 32);
}
// Set grid dimensions and resize tile array; initially all -1 (empty)
void TilemapComponent::SetGridSize(const glm::ivec2 &size)
{
m_GridSize = size;
m_Tiles.assign(size.x * size.y, -1);
}
// Set individual tile at (x, y)
void TilemapComponent::SetTile(int x, int y, int tileIndex)
{
if (x < 0 || y < 0 || x >= m_GridSize.x || y >= m_GridSize.y)
return;
m_Tiles[y * m_GridSize.x + x] = tileIndex;
}
int TilemapComponent::GetTile(int x, int y) const
{
if (x < 0 || y < 0 || x >= m_GridSize.x || y >= m_GridSize.y)
return -1;
return m_Tiles[y * m_GridSize.x + x];
}
void TilemapComponent::SetTileSize(const glm::ivec2 &size)
{
m_TileSize = size;
}
// Set atlas (tileset) settings; also update dimensions
void TilemapComponent::SetAtlas(const std::string &path, int atlasTileWidth, int atlasTileHeight)
{
m_AtlasPath = path;
m_AtlasTileWidth = atlasTileWidth;
m_AtlasTileHeight = atlasTileHeight;
UpdateAtlasDimensions();
}
// Helper: load atlas image info using stbi_info to compute columns and rows
void TilemapComponent::UpdateAtlasDimensions()
{
if (m_AtlasPath.empty())
return;
int texW, texH, comp;
if (stbi_info(m_AtlasPath.c_str(), &texW, &texH, &comp))
{
m_AtlasCols = texW / m_AtlasTileWidth;
m_AtlasRows = texH / m_AtlasTileHeight;
}
else
{
m_AtlasCols = m_AtlasRows = 0;
Logger::LogError("Failed to get atlas info: %s", m_AtlasPath.c_str());
}
}
// Serialization: store grid size, tile size, tile data, and atlas settings.
void TilemapComponent::Save(YAML::Emitter &out) const
{
out << YAML::BeginMap;
out << YAML::Key << "type" << YAML::Value << "TilemapComponent";
out << YAML::Key << "gridSize" << YAML::Value << std::vector<int>{m_GridSize.x, m_GridSize.y};
out << YAML::Key << "tileSize" << YAML::Value << std::vector<int>{m_TileSize.x, m_TileSize.y};
out << YAML::Key << "tiles" << YAML::Value << m_Tiles;
out << YAML::Key << "atlasPath" << YAML::Value << m_AtlasPath;
out << YAML::Key << "atlasTileSize" << YAML::Value << std::vector<int>{m_AtlasTileWidth, m_AtlasTileHeight};
out << YAML::EndMap;
}
// Deserialization: load saved data
void TilemapComponent::Load(const YAML::Node &node)
{
if (node["gridSize"])
{
auto vec = node["gridSize"].as<std::vector<int>>();
if (vec.size() == 2)
m_GridSize = glm::ivec2(vec[0], vec[1]);
}
if (node["tileSize"])
{
auto vec = node["tileSize"].as<std::vector<int>>();
if (vec.size() == 2)
m_TileSize = glm::ivec2(vec[0], vec[1]);
}
if (node["tiles"])
m_Tiles = node["tiles"].as<std::vector<int>>();
if (node["atlasPath"])
m_AtlasPath = node["atlasPath"].as<std::string>();
if (node["atlasTileSize"])
{
auto vec = node["atlasTileSize"].as<std::vector<int>>();
if (vec.size() == 2)
{
m_AtlasTileWidth = vec[0];
m_AtlasTileHeight = vec[1];
}
}
// Update atlas dimensions after loading settings
UpdateAtlasDimensions();
}
// -----------------------------
// Editor UI
// -----------------------------
void TilemapComponent::DrawEditorUI()
{
ImGui::Begin("Tilemap Editor");
// --- Atlas Settings ---
if (ImGui::CollapsingHeader("Tileset Atlas Settings"))
{
// Display current atlas path
ImGui::Text("Current Atlas: %s", m_AtlasPath.empty() ? "None" : m_AtlasPath.c_str());
if (ImGui::Button("Import Atlas"))
{
std::string path = OpenFileDialog(FileDialogType::Images);
if (!path.empty())
{
// For simplicity, default atlas tile dimensions are set via input later.
m_AtlasPath = path;
UpdateAtlasDimensions();
}
}
// Configure atlas tile size
int atlasW = m_AtlasTileWidth, atlasH = m_AtlasTileHeight;
if (ImGui::InputInt("Atlas Tile Width", &atlasW))
{
m_AtlasTileWidth = atlasW;
UpdateAtlasDimensions();
}
if (ImGui::InputInt("Atlas Tile Height", &atlasH))
{
m_AtlasTileHeight = atlasH;
UpdateAtlasDimensions();
}
ImGui::Text("Atlas Grid: %d columns x %d rows", m_AtlasCols, m_AtlasRows);
}
// --- Map Grid Settings ---
if (ImGui::CollapsingHeader("Map Settings"))
{
glm::ivec2 grid = m_GridSize;
int gridW = grid.x, gridH = grid.y;
if (ImGui::InputInt("Map Grid Width", &gridW) || ImGui::InputInt("Map Grid Height", &gridH))
{
SetGridSize(glm::ivec2(gridW, gridH));
}
glm::ivec2 ts = m_TileSize;
int tileW = ts.x, tileH = ts.y;
if (ImGui::InputInt("Tile Width", &tileW) || ImGui::InputInt("Tile Height", &tileH))
{
SetTileSize(glm::ivec2(tileW, tileH));
}
}
// --- Tileset Palette Editor ---
if (ImGui::CollapsingHeader("Tileset Palette"))
{
if (m_AtlasPath.empty())
{
ImGui::Text("No atlas loaded.");
}
else
{
// Retrieve texture dimensions via stbi_info (for preview UV calculation)
int texW, texH, comp;
if (stbi_info(m_AtlasPath.c_str(), &texW, &texH, &comp))
{
float uvTileW = float(m_AtlasTileWidth) / float(texW);
float uvTileH = float(m_AtlasTileHeight) / float(texH);
// Display each tile as an image button
ImGui::Text("Select a tile:");
for (int row = 0; row < m_AtlasRows; row++)
{
for (int col = 0; col < m_AtlasCols; col++)
{
int tileIndex = row * m_AtlasCols + col;
// UV coordinates for this tile
float uvX = float(col) * uvTileW;
float uvY = float(row) * uvTileH;
ImVec2 uv0(uvX, uvY);
ImVec2 uv1(uvX + uvTileW, uvY + uvTileH);
char buf[32];
sprintf(buf, "##tile_%d", tileIndex);
// Display a button. Here, 32x32 pixels is used for preview size.
GLuint textureID = LoadTextureIfNeeded(m_AtlasPath);
if (ImGui::ImageButton(buf, (ImTextureID)(uintptr_t)textureID, ImVec2(32, 32), uv0, uv1))
{
g_selectedTileIndex = tileIndex;
}
ImGui::SameLine();
}
ImGui::NewLine();
}
ImGui::Text("Selected Tile: %d", g_selectedTileIndex);
}
else
{
ImGui::Text("Failed to load atlas info.");
}
}
}
// --- Map Editor ---
if (ImGui::CollapsingHeader("Map Editor"))
{
if (m_GridSize.x == 0 || m_GridSize.y == 0)
{
ImGui::Text("No map created.");
}
else
{
ImGui::Text("Click a cell to paint with the selected tile.");
ImGui::BeginChild("TilemapGrid", ImVec2(0, 300), true);
for (int y = 0; y < m_GridSize.y; y++)
{
for (int x = 0; x < m_GridSize.x; x++)
{
int tile = GetTile(x, y);
char label[32];
sprintf(label, "%d##%d_%d", tile, x, y);
if (ImGui::Button(label, ImVec2(30, 30)))
{
SetTile(x, y, g_selectedTileIndex);
}
ImGui::SameLine();
}
ImGui::NewLine();
}
ImGui::EndChild();
}
}
ImGui::End();
}

View File

@ -0,0 +1,56 @@
#pragma once
#include "Component.h"
#include <glm/glm.hpp>
#include <yaml-cpp/yaml.h>
#include <string>
#include <vector>
#include <imgui.h>
class TilemapComponent : public Component
{
public:
TilemapComponent(Object* owner);
// Editor UI for tilemap
void DrawEditorUI();
// Set up grid and tile dimensions
void SetGridSize(const glm::ivec2& size);
void SetTileSize(const glm::ivec2& size);
void SetTile(int x, int y, int tileIndex);
int GetTile(int x, int y) const;
// Getters
const glm::ivec2& GetGridSize() const { return m_GridSize; }
const glm::ivec2& GetTileSize() const { return m_TileSize; }
const std::vector<int>& GetTiles() const { return m_Tiles; }
const std::string& GetAtlasPath() const { return m_AtlasPath; }
int GetAtlasTileWidth() const { return m_AtlasTileWidth; }
int GetAtlasTileHeight() const { return m_AtlasTileHeight; }
int GetAtlasCols() const { return m_AtlasCols; }
int GetAtlasRows() const { return m_AtlasRows; }
// Atlas setup: import an atlas image and configure its tile dimensions.
void SetAtlas(const std::string& path, int atlasTileWidth, int atlasTileHeight);
// Serialization
void Save(YAML::Emitter& out) const override;
void Load(const YAML::Node& node) override;
std::string GetName() const override { return "TilemapComponent"; }
private:
// Grid and tile data
glm::ivec2 m_GridSize {0, 0};
glm::ivec2 m_TileSize {32, 32};
std::vector<int> m_Tiles;
// Atlas (tileset) settings
std::string m_AtlasPath;
int m_AtlasTileWidth = 32;
int m_AtlasTileHeight = 32;
int m_AtlasCols = 0;
int m_AtlasRows = 0;
void UpdateAtlasDimensions();
};

View File

@ -4,6 +4,8 @@
#include "components/SpriteComponent.h"
#include "components/CameraComponent.h"
#include "components/LightComponent.h"
#include "components/TilemapComponent.h"
#include "utils/FileDialog.h"
#include "utils/Logging.h"
@ -162,6 +164,11 @@ void DrawInspectorUI(std::shared_ptr<Object> selected)
if (!selected->GetComponent<LightComponent>())
selected->AddComponent<LightComponent>();
}
if (ImGui::Button("Add TilemapComponent"))
{
if (!selected->GetComponent<TilemapComponent>())
selected->AddComponent<TilemapComponent>();
}
// Sprite UI...
if (auto sprite = selected->GetComponent<SpriteComponent>())
@ -230,7 +237,23 @@ void DrawInspectorUI(std::shared_ptr<Object> selected)
selected->RemoveComponent<LightComponent>();
}
if (auto tilemap = selected->GetComponent<TilemapComponent>())
{
ImGui::SeparatorText("Tilemap Component");
ImGui::Text("Refer to Tilemap Editor Window");
if (ImGui::Button("Remove TilemapComponent"))
selected->RemoveComponent<TilemapComponent>();
}
ImGui::End();
if (auto tilemap = selected->GetComponent<TilemapComponent>())
{
tilemap->DrawEditorUI();
}
}

View File

@ -2,18 +2,22 @@
#include "Components/SpriteComponent.h"
#include "utils/Shader.h"
#include "utils/Logging.h"
#include "utils/EngineConfig.h"
#include "utils/utils.h"
#include "stb_image.h"
#include <GL/glew.h>
#include <glm/glm.hpp>
#include <iostream>
#include <map>
static Shader spriteShader;
static Shader unlitShader;
GLuint Renderer::fbo = 0;
GLuint Renderer::textureColorBuffer = 0;
GLuint Renderer::defaultNormalMap = 0;
GLuint Renderer::rbo = 0;
GLuint Renderer::quadVAO = 0;
GLuint Renderer::quadVBO = 0;
@ -22,9 +26,7 @@ int Renderer::height = 720;
int Renderer::s_DrawCalls = 0;
std::vector<Light> Renderer::s_Lights;
static Shader tilemapShader;
void Renderer::InitQuad() {
@ -74,7 +76,10 @@ void Renderer::Init() {
InitQuad();
// Load lit shader
spriteShader.LoadFromFile("src/assets/shaders/sprite.vert", "src/assets/shaders/sprite.frag");
// Load unlit shader
unlitShader.LoadFromFile("src/assets/shaders/unlit.vert", "src/assets/shaders/unlit.frag");
// Create a 1x1 flat normal map (RGB: 128,128,255)
unsigned char flatNormal[3] = { 128, 128, 255 };
@ -115,33 +120,64 @@ void Renderer::End() {
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
//void Renderer::DrawSprite(SpriteComponent* sprite, const glm::vec2& pos, float scale) {
// GLuint tex = sprite->GetTextureID();
// if (!tex) return;
//
// glBindTexture(GL_TEXTURE_2D, tex);
// glBegin(GL_QUADS);
// float size = 100.0f * scale;
//
// glTexCoord2f(0, 0); glVertex2f(pos.x, pos.y);
// glTexCoord2f(1, 0); glVertex2f(pos.x + size, pos.y);
// glTexCoord2f(1, 1); glVertex2f(pos.x + size, pos.y + size);
// glTexCoord2f(0, 1); glVertex2f(pos.x, pos.y + size);
// glEnd();
//}
void Renderer::ClearLights() {
s_Lights.clear();
}
void Renderer::AddLight(const glm::vec2& screenPos, const glm::vec3& color, float intensity, float radius) {
if (s_Lights.size() >= 8) return;
s_Lights.push_back({screenPos, color, intensity, radius});
}
void Renderer::DrawTilemap(TilemapComponent* tilemap, const glm::vec2& worldPos, float zoom, const glm::vec2& cameraPos) {
if (!tilemap || tilemap->GetAtlasPath().empty()) return;
glm::ivec2 grid = tilemap->GetGridSize();
glm::ivec2 tileSize = tilemap->GetTileSize();
int cols = tilemap->GetAtlasCols();
int rows = tilemap->GetAtlasRows();
const std::string& atlasPath = tilemap->GetAtlasPath();
GLuint atlasTex = LoadTextureIfNeeded(atlasPath);
if (atlasTex == 0) return;
tilemapShader.Use();
tilemapShader.SetVec2("uScreen", glm::vec2(width, height));
tilemapShader.SetInt("uTex", 0);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, atlasTex);
glBindVertexArray(quadVAO);
for (int y = 0; y < grid.y; ++y) {
for (int x = 0; x < grid.x; ++x) {
int index = tilemap->GetTile(x, y);
if (index < 0 || index >= cols * rows) continue;
int atlasX = index % cols;
int atlasY = index / cols;
glm::vec2 uvMin = glm::vec2(atlasX, atlasY) / glm::vec2(cols, rows);
glm::vec2 uvMax = (glm::vec2(atlasX + 1, atlasY + 1)) / glm::vec2(cols, rows);
tilemapShader.SetVec2("uUVMin", uvMin);
tilemapShader.SetVec2("uUVMax", uvMax);
glm::vec2 tileWorld = worldPos + glm::vec2(x * tileSize.x, y * tileSize.y);
glm::vec2 screenPos = (tileWorld - cameraPos) * zoom + glm::vec2(width, height) * 0.5f;
tilemapShader.SetVec2("uPos", screenPos);
tilemapShader.SetVec2("uSize", glm::vec2(tileSize) * zoom);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
s_DrawCalls++;
}
}
glBindVertexArray(0);
}
void Renderer::DrawSprite(SpriteComponent* sprite, const glm::vec2& pos, float zoom, glm::vec2& CameraPos) {
if (!sprite->HasTexture()) {
@ -149,54 +185,65 @@ void Renderer::DrawSprite(SpriteComponent* sprite, const glm::vec2& pos, float z
return;
}
spriteShader.Use();
// Choose the shader based on engine configuration
if (g_engineConfig.lighting_enabled) {
spriteShader.Use();
} else {
unlitShader.Use();
}
glm::vec2 size = sprite->GetSize();
glm::vec2 screenPos = (pos - CameraPos) * zoom + glm::vec2(width, height) * 0.5f - (size * zoom * 0.5f);
spriteShader.SetVec2("uPos", screenPos);
spriteShader.SetVec2("uSize", size * zoom);
spriteShader.SetVec2("uScreen", glm::vec2(width, height));
// Set common uniforms
if (g_engineConfig.lighting_enabled) {
spriteShader.SetVec2("uPos", screenPos);
spriteShader.SetVec2("uSize", size * zoom);
spriteShader.SetVec2("uScreen", glm::vec2(width, height));
spriteShader.SetInt("uLightCount", static_cast<int>(s_Lights.size()));
for (size_t i = 0; i < s_Lights.size(); ++i) {
spriteShader.SetVec2(("uLightPos[" + std::to_string(i) + "]").c_str(), s_Lights[i].screenPos);
spriteShader.SetVec3(("uLightColor[" + std::to_string(i) + "]").c_str(), s_Lights[i].color);
spriteShader.SetFloat(("uLightIntensity[" + std::to_string(i) + "]").c_str(), s_Lights[i].intensity);
spriteShader.SetFloat(("uLightRadius[" + std::to_string(i) + "]").c_str(), s_Lights[i].radius);
spriteShader.SetInt("uLightCount", static_cast<int>(s_Lights.size()));
for (size_t i = 0; i < s_Lights.size(); ++i) {
spriteShader.SetVec2(("uLightPos[" + std::to_string(i) + "]").c_str(), s_Lights[i].screenPos);
spriteShader.SetVec3(("uLightColor[" + std::to_string(i) + "]").c_str(), s_Lights[i].color);
spriteShader.SetFloat(("uLightIntensity[" + std::to_string(i) + "]").c_str(), s_Lights[i].intensity);
spriteShader.SetFloat(("uLightRadius[" + std::to_string(i) + "]").c_str(), s_Lights[i].radius);
}
} else {
// Unlit shader uniforms
unlitShader.SetVec2("uPos", screenPos);
unlitShader.SetVec2("uSize", size * zoom);
unlitShader.SetVec2("uScreen", glm::vec2(width, height));
}
// Bind the diffuse texture (common to both shaders)
if (g_engineConfig.lighting_enabled) {
spriteShader.SetInt("uTex", 0);
} else {
unlitShader.SetInt("uTex", 0);
}
spriteShader.SetInt("uTex", 0);
spriteShader.SetInt("uNormalMap", 1);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, sprite->GetTextureID());
glActiveTexture(GL_TEXTURE1);
if (sprite->GetNormalMapID()) {
glBindTexture(GL_TEXTURE_2D, sprite->GetNormalMapID());
} else {
glBindTexture(GL_TEXTURE_2D, defaultNormalMap);
if (g_engineConfig.lighting_enabled) {
spriteShader.SetInt("uNormalMap", 1);
glActiveTexture(GL_TEXTURE1);
if (sprite->GetNormalMapID()) {
glBindTexture(GL_TEXTURE_2D, sprite->GetNormalMapID());
} else {
glBindTexture(GL_TEXTURE_2D, defaultNormalMap);
}
}
glBindVertexArray(quadVAO);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
glBindVertexArray(0);
s_DrawCalls++;
}
int Renderer::GetDrawCallCount()
{
int Renderer::GetDrawCallCount() {
return s_DrawCalls;
}
void Renderer::DrawEditorGrid(const glm::vec2& cameraPos, float zoom) {
glUseProgram(0);
glColor4f(0.5f, 0.5f, 0.5f, 0.25f);
@ -226,11 +273,9 @@ void Renderer::DrawEditorGrid(const glm::vec2& cameraPos, float zoom) {
glVertex2f(left, (float)y);
glVertex2f(right, (float)y);
}
glEnd();
}
GLuint Renderer::GetRenderTexture() {
return textureColorBuffer;
}

View File

@ -3,8 +3,11 @@
#include <glm/glm.hpp>
#include <vector>
#include "Components/TilemapComponent.h"
#include "Components/SpriteComponent.h"
class SpriteComponent;
struct Light {
glm::vec2 screenPos;
@ -21,6 +24,7 @@ public:
static void End();
static void DrawSprite(SpriteComponent* sprite, const glm::vec2& pos, float zoom, glm::vec2& CameraPos);
static void AddLight(const glm::vec2& screenPos, const glm::vec3& color, float intensity, float radius);
static void DrawTilemap(TilemapComponent* tilemap, const glm::vec2& worldPos, float zoom, const glm::vec2& cameraPos);
static void ClearLights();
static void DrawEditorGrid(const glm::vec2& cameraPos, float zoom);
static GLuint GetRenderTexture();

View File

@ -0,0 +1,8 @@
// EngineConfig.cpp
#include "EngineConfig.h"
EngineConfig g_engineConfig {
.lighting_enabled = false,
};

View File

@ -0,0 +1,7 @@
#pragma once
struct EngineConfig {
bool lighting_enabled;
};
extern EngineConfig g_engineConfig;

View File

@ -1,12 +1,37 @@
#include "utils.h"
#include <map>
#include "stb_image.h"
static std::map<std::string, GLuint> textureCache;
GLuint LoadTextureIfNeeded(const std::string& path) {
if (textureCache.count(path))
return textureCache[path];
int w, h, channels;
stbi_set_flip_vertically_on_load(false);
unsigned char* data = stbi_load(path.c_str(), &w, &h, &channels, 4);
if (!data)
return 0;
GLuint id;
glGenTextures(1, &id);
glBindTexture(GL_TEXTURE_2D, id);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
stbi_image_free(data);
textureCache[path] = id;
return id;
}
std::string GetFilenameFromPath(const std::string& path)
{
// Find the last slash or backslash
size_t lastSlash = path.find_last_of("/\\");
std::string filename = (lastSlash == std::string::npos) ? path : path.substr(lastSlash + 1);
// Strip trailing slashes (if any)
// Strip trailing slashes
while (!filename.empty() && (filename.back() == '/' || filename.back() == '\\'))
filename.pop_back();

View File

@ -1,4 +1,10 @@
#include <string>
#include <algorithm>
#pragma once
#include <string>
#include <GL/glew.h>
// Returns filename from a full path (e.g., "C:/foo/bar.png" → "bar.png")
std::string GetFilenameFromPath(const std::string& path);
// Loads a texture (with caching). Returns OpenGL texture ID
GLuint LoadTextureIfNeeded(const std::string& path);