feat(render): add game rendering
This commit is contained in:
parent
34d08212f8
commit
3bb961448d
7
app.c
7
app.c
@ -28,10 +28,13 @@ void App_Init(App_t *app) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Objects_Init(&app->graphics)) {
|
if (!Objects_Init(&app->objects)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Map_LoadSampleData();
|
||||||
|
|
||||||
|
app->isInGame = true;
|
||||||
app->isRunning = true;
|
app->isRunning = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,7 +46,7 @@ void App_Run(App_t *app) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void App_Shutdown(const App_t* app) {
|
void App_Shutdown(const App_t* app) {
|
||||||
Objects_Destroy(&app->graphics);
|
Objects_Destroy(&app->objects);
|
||||||
Map_Destroy(&app->map);
|
Map_Destroy(&app->map);
|
||||||
Bitmap_Destroy(&app->bitmap);
|
Bitmap_Destroy(&app->bitmap);
|
||||||
Gui_Shutdown();
|
Gui_Shutdown();
|
||||||
|
|||||||
3
app.h
3
app.h
@ -12,11 +12,12 @@
|
|||||||
|
|
||||||
typedef struct App {
|
typedef struct App {
|
||||||
bool isRunning;
|
bool isRunning;
|
||||||
|
bool isInGame;
|
||||||
SystemMetrics_t metrics;
|
SystemMetrics_t metrics;
|
||||||
ConfigParams_t configParams;
|
ConfigParams_t configParams;
|
||||||
Map_t map;
|
Map_t map;
|
||||||
Bitmap_t bitmap;
|
Bitmap_t bitmap;
|
||||||
Objects_t graphics;
|
Objects_t objects;
|
||||||
Gui_t gui;
|
Gui_t gui;
|
||||||
} App_t;
|
} App_t;
|
||||||
|
|
||||||
|
|||||||
14
gui.c
14
gui.c
@ -39,8 +39,8 @@ void Gui_StartRender() {
|
|||||||
ImGui_NewFrame();
|
ImGui_NewFrame();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Gui_Render(Gui_t *gui, ConfigParams_t *configParams) {
|
void Gui_Render(Gui_t *gui, ConfigParams_t *configParams, const bool inGame) {
|
||||||
Gui_RenderMainMenu(gui);
|
Gui_RenderMainMenu(gui, inGame);
|
||||||
Gui_RenderPreferences_Dialog(gui, configParams);
|
Gui_RenderPreferences_Dialog(gui, configParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,7 +57,7 @@ void Gui_Shutdown() {
|
|||||||
ImGui_DestroyContext(NULL);
|
ImGui_DestroyContext(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Gui_RenderMainMenu(Gui_t *gui) {
|
void Gui_RenderMainMenu(Gui_t *gui, const bool inGame) {
|
||||||
if (!ImGui_BeginMainMenuBar()) {
|
if (!ImGui_BeginMainMenuBar()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -82,20 +82,20 @@ void Gui_RenderMainMenu(Gui_t *gui) {
|
|||||||
if (ImGui_MenuItem("Journey Onward")) {
|
if (ImGui_MenuItem("Journey Onward")) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ImGui_MenuItem("End Game")) {
|
if (ImGui_MenuItemEx("End Game", NULL, NULL, inGame)) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui_EndMenu();
|
ImGui_EndMenu();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ImGui_BeginMenu("Info")) {
|
if (ImGui_BeginMenu("Info")) {
|
||||||
if (ImGui_MenuItem("Change Data")) {
|
if (ImGui_MenuItemEx("Change Data", NULL, NULL, inGame)) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ImGui_MenuItem("Userlist")) {
|
if (ImGui_MenuItemEx("Userlist", NULL, NULL, inGame)) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ImGui_MenuItem("Comments")) {
|
if (ImGui_MenuItemEx("Comments", NULL, NULL, inGame)) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui_EndMenu();
|
ImGui_EndMenu();
|
||||||
|
|||||||
4
gui.h
4
gui.h
@ -16,11 +16,11 @@ typedef struct Gui {
|
|||||||
void Gui_Init(Gui_t* gui);
|
void Gui_Init(Gui_t* gui);
|
||||||
void Gui_ProcessEvent(const SDL_Event* event);
|
void Gui_ProcessEvent(const SDL_Event* event);
|
||||||
void Gui_StartRender();
|
void Gui_StartRender();
|
||||||
void Gui_Render(Gui_t* gui, ConfigParams_t* configParams);
|
void Gui_Render(Gui_t* gui, ConfigParams_t* configParams, bool inGame);
|
||||||
void Gui_FinishRender();
|
void Gui_FinishRender();
|
||||||
void Gui_Shutdown();
|
void Gui_Shutdown();
|
||||||
|
|
||||||
void Gui_RenderMainMenu(Gui_t* gui);
|
void Gui_RenderMainMenu(Gui_t* gui, bool inGame);
|
||||||
void Gui_RenderPreferences_Dialog(Gui_t* gui, ConfigParams_t* configParams);
|
void Gui_RenderPreferences_Dialog(Gui_t* gui, ConfigParams_t* configParams);
|
||||||
|
|
||||||
#endif //TIBIA_GUI_H
|
#endif //TIBIA_GUI_H
|
||||||
78
objects.c
78
objects.c
@ -6,9 +6,8 @@
|
|||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <SDL3/SDL_iostream.h>
|
#include <SDL3/SDL.h>
|
||||||
#include <SDL3/SDL_log.h>
|
#include "window.h"
|
||||||
#include <SDL3/SDL_messagebox.h>
|
|
||||||
|
|
||||||
bool Objects_Init(Objects_t *objects) {
|
bool Objects_Init(Objects_t *objects) {
|
||||||
memset(objects->spriteTable, 0, sizeof(objects->spriteTable));
|
memset(objects->spriteTable, 0, sizeof(objects->spriteTable));
|
||||||
@ -22,6 +21,7 @@ bool Objects_Init(Objects_t *objects) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
memset(objects->activeObjectList, 0, sizeof(objects->activeObjectList));
|
memset(objects->activeObjectList, 0, sizeof(objects->activeObjectList));
|
||||||
|
memset(objects->activeTextures, 0, sizeof(objects->activeTextures));
|
||||||
|
|
||||||
SDL_LogInfo(SDL_LOG_CATEGORY_CUSTOM, "Objects_Init: memory allocated");
|
SDL_LogInfo(SDL_LOG_CATEGORY_CUSTOM, "Objects_Init: memory allocated");
|
||||||
|
|
||||||
@ -115,6 +115,7 @@ bool Objects_LoadData(Objects_t *objects) {
|
|||||||
|
|
||||||
bool Objects_LoadSprites(Objects_t *objects) {
|
bool Objects_LoadSprites(Objects_t *objects) {
|
||||||
SDL_LogInfo(SDL_LOG_CATEGORY_CUSTOM, "Objects_LoadSprites: loading object sprites.");
|
SDL_LogInfo(SDL_LOG_CATEGORY_CUSTOM, "Objects_LoadSprites: loading object sprites.");
|
||||||
|
SDL_Renderer* renderer = Window_GetRenderer();
|
||||||
|
|
||||||
// Free existing sprites to prevent memory leaks
|
// Free existing sprites to prevent memory leaks
|
||||||
for (int i = 0; i < SPRITE_TABLE_SIZE; i++) {
|
for (int i = 0; i < SPRITE_TABLE_SIZE; i++) {
|
||||||
@ -186,6 +187,9 @@ bool Objects_LoadSprites(Objects_t *objects) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
objects->spriteTable[spriteID] = spriteBuffer;
|
||||||
|
objects->activeTextures[spriteID] = Objects_TextureFromRaw(renderer, spriteBuffer);
|
||||||
|
|
||||||
spritesLoaded++;
|
spritesLoaded++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -199,4 +203,72 @@ void Objects_Destroy(const Objects_t *objects) {
|
|||||||
for (int i = 0; i < THING_DATA_POOL_SIZE; i++) {
|
for (int i = 0; i < THING_DATA_POOL_SIZE; i++) {
|
||||||
free(objects->thingDataPool[i]);
|
free(objects->thingDataPool[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < ACTIVE_OBJECT_LIST_SIZE; i++) {
|
||||||
|
if (objects->activeTextures[i]) {
|
||||||
|
SDL_DestroyTexture(objects->activeTextures[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_Texture* Objects_GetSpriteTexture(const Objects_t *objects, const uint16_t spriteID) {
|
||||||
|
if (spriteID >= SPRITE_TABLE_SIZE) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return objects->activeTextures[spriteID];
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_Texture* Objects_TextureFromRaw(SDL_Renderer* renderer, uint8_t* rawData) {
|
||||||
|
if (!rawData) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint16_t totalSize = *(uint16_t*) rawData;
|
||||||
|
uint8_t* data = rawData + 2;
|
||||||
|
int readOffset = 0;
|
||||||
|
|
||||||
|
SDL_Surface* surface = SDL_CreateSurface(32, 32, SDL_PIXELFORMAT_ARGB8888);
|
||||||
|
if (!surface) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_LockSurface(surface);
|
||||||
|
uint32_t* pixels = surface->pixels;
|
||||||
|
|
||||||
|
memset(pixels, 0, 32 * 32 * 4);
|
||||||
|
|
||||||
|
int currentPixel = 0;
|
||||||
|
while (readOffset < totalSize - 2 && currentPixel < 1024) {
|
||||||
|
const uint16_t transparentRaw = *(uint16_t*)(data + readOffset);
|
||||||
|
readOffset += 2;
|
||||||
|
|
||||||
|
const uint16_t transparentBytes = transparentRaw % 0x5A0;
|
||||||
|
const int skipPixels = transparentBytes / 3;
|
||||||
|
|
||||||
|
currentPixel += skipPixels;
|
||||||
|
|
||||||
|
const uint16_t colorBytes = *(uint16_t*)(data + readOffset);
|
||||||
|
readOffset += 2;
|
||||||
|
|
||||||
|
const int drawPixels = colorBytes / 3;
|
||||||
|
for (int i = 0; i < drawPixels; i++) {
|
||||||
|
if (currentPixel >= 1024) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint8_t r = data[readOffset++];
|
||||||
|
const uint8_t g = data[readOffset++];
|
||||||
|
const uint8_t b = data[readOffset++];
|
||||||
|
|
||||||
|
pixels[currentPixel++] = SDL_MapRGBA(SDL_GetPixelFormatDetails(surface->format), NULL, r, g, b, 255);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_UnlockSurface(surface);
|
||||||
|
|
||||||
|
SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, surface);
|
||||||
|
SDL_DestroySurface(surface);
|
||||||
|
|
||||||
|
return texture;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <SDL3/SDL_render.h>
|
||||||
|
|
||||||
#pragma pack(push, 1)
|
#pragma pack(push, 1)
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@ -25,11 +26,16 @@ typedef struct Objects {
|
|||||||
void* spriteTable[SPRITE_TABLE_SIZE];
|
void* spriteTable[SPRITE_TABLE_SIZE];
|
||||||
void* thingDataPool[THING_DATA_POOL_SIZE];
|
void* thingDataPool[THING_DATA_POOL_SIZE];
|
||||||
void* activeObjectList[ACTIVE_OBJECT_LIST_SIZE];
|
void* activeObjectList[ACTIVE_OBJECT_LIST_SIZE];
|
||||||
|
SDL_Texture* activeTextures[SPRITE_TABLE_SIZE];
|
||||||
} Objects_t;
|
} Objects_t;
|
||||||
|
|
||||||
bool Objects_Init(Objects_t* objects);
|
bool Objects_Init(Objects_t* objects);
|
||||||
bool Objects_LoadData(Objects_t* objects);
|
bool Objects_LoadData(Objects_t* objects);
|
||||||
bool Objects_LoadSprites(Objects_t* objects);
|
bool Objects_LoadSprites(Objects_t* objects);
|
||||||
void Objects_Destroy(const Objects_t* objects);
|
void Objects_Destroy(const Objects_t* objects);
|
||||||
|
SDL_Texture* Objects_GetSpriteTexture(const Objects_t* objects, uint16_t spriteID);
|
||||||
|
|
||||||
|
// TODO: move this somewhere else
|
||||||
|
SDL_Texture* Objects_TextureFromRaw(SDL_Renderer* renderer, uint8_t* rawData);
|
||||||
|
|
||||||
#endif //TIBIA_OBJECTS_H
|
#endif //TIBIA_OBJECTS_H
|
||||||
104
render.c
104
render.c
@ -6,6 +6,48 @@
|
|||||||
#include "window.h"
|
#include "window.h"
|
||||||
#include "gui.h"
|
#include "gui.h"
|
||||||
|
|
||||||
|
#define MAP_WIDTH_TILES 15
|
||||||
|
#define MAP_HEIGHT_TILES 11
|
||||||
|
#define TILE_SIZE 32
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint16_t groundSpriteID;
|
||||||
|
} MapTile;
|
||||||
|
|
||||||
|
MapTile g_ClientMapData[MAP_WIDTH_TILES][MAP_HEIGHT_TILES];
|
||||||
|
|
||||||
|
void Map_LoadSampleData() {
|
||||||
|
SDL_Log("Map_LoadSampleData: Populating dummy map...");
|
||||||
|
|
||||||
|
// These IDs are guesses for Tibia 1.03 based on standard asset order.
|
||||||
|
// If these show up as items (like swords/apples), try swapping them.
|
||||||
|
// Usually: 0-100 contains basic ground tiles.
|
||||||
|
uint16_t ID_GRASS = 45;
|
||||||
|
uint16_t ID_DIRT = 40;
|
||||||
|
uint16_t ID_STONE = 50;
|
||||||
|
|
||||||
|
for (int x = 0; x < MAP_WIDTH_TILES; x++) {
|
||||||
|
for (int y = 0; y < MAP_HEIGHT_TILES; y++) {
|
||||||
|
|
||||||
|
// 1. Default to Grass
|
||||||
|
uint16_t tileID = ID_GRASS;
|
||||||
|
|
||||||
|
// 2. Create a Checkerboard pattern
|
||||||
|
if ((x + y) % 2 == 0) {
|
||||||
|
tileID = ID_DIRT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Create a Stone path in the middle row
|
||||||
|
if (y == 5) {
|
||||||
|
tileID = ID_STONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Assign to the map
|
||||||
|
g_ClientMapData[x][y].groundSpriteID = x * MAP_HEIGHT_TILES + y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Render_Frame(App_t* app) {
|
void Render_Frame(App_t* app) {
|
||||||
Gui_StartRender();
|
Gui_StartRender();
|
||||||
|
|
||||||
@ -14,7 +56,9 @@ void Render_Frame(App_t* app) {
|
|||||||
SDL_RenderClear(renderer);
|
SDL_RenderClear(renderer);
|
||||||
|
|
||||||
Render_MainWindowBackground(app);
|
Render_MainWindowBackground(app);
|
||||||
Gui_Render(&app->gui, &app->configParams);
|
Render_GameView(app);
|
||||||
|
|
||||||
|
Gui_Render(&app->gui, &app->configParams, app->isInGame);
|
||||||
|
|
||||||
Gui_FinishRender();
|
Gui_FinishRender();
|
||||||
SDL_RenderPresent(renderer);
|
SDL_RenderPresent(renderer);
|
||||||
@ -29,7 +73,7 @@ void Render_MainWindowBackground(const App_t *app) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const int TILE_SIZE = 128;
|
// const int TILE_SIZE = 128;
|
||||||
|
|
||||||
// Calculate how many tiles we need to cover the screen
|
// Calculate how many tiles we need to cover the screen
|
||||||
const int tilesX = app->metrics.screenWidth / TILE_SIZE + 1;
|
const int tilesX = app->metrics.screenWidth / TILE_SIZE + 1;
|
||||||
@ -48,3 +92,59 @@ void Render_MainWindowBackground(const App_t *app) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Render_GameView(const App_t *app) {
|
||||||
|
if (!app->isInGame) {
|
||||||
|
Render_TitleScreen(app);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Render_Game(app);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Render_TitleScreen(const App_t *app) {
|
||||||
|
// TODO: properly calculate the window sizes and spacings
|
||||||
|
SDL_Renderer *renderer = Window_GetRenderer();
|
||||||
|
int winW, winH;
|
||||||
|
SDL_GetRenderOutputSize(renderer, &winW, &winH);
|
||||||
|
|
||||||
|
const SDL_FRect destRect = {
|
||||||
|
0.0f, 30.0f,
|
||||||
|
(float)winW, (float)winH - 150.0f
|
||||||
|
};
|
||||||
|
|
||||||
|
SDL_RenderTexture(renderer, app->bitmap.tibiaTexture, NULL, &destRect);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Render_Game(const App_t *app) {
|
||||||
|
SDL_Renderer *renderer = Window_GetRenderer();
|
||||||
|
|
||||||
|
for (int x = 0; x < MAP_WIDTH_TILES; x++) {
|
||||||
|
for (int y = 0; y < MAP_HEIGHT_TILES; y++) {
|
||||||
|
const MapTile* tile = &g_ClientMapData[x][y];
|
||||||
|
|
||||||
|
if (tile->groundSpriteID == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int screenX = x * TILE_SIZE;
|
||||||
|
const int screenY = y * TILE_SIZE;
|
||||||
|
|
||||||
|
SDL_Texture* texture = Objects_GetSpriteTexture(&app->objects, tile->groundSpriteID);
|
||||||
|
if (texture) {
|
||||||
|
SDL_FRect destRect = {
|
||||||
|
(float)screenX,
|
||||||
|
(float)screenY,
|
||||||
|
(float)TILE_SIZE,
|
||||||
|
(float)TILE_SIZE
|
||||||
|
};
|
||||||
|
|
||||||
|
SDL_RenderTexture(renderer, texture, NULL, &destRect);
|
||||||
|
} else {
|
||||||
|
SDL_FRect destRect = { (float)screenX, (float)screenY, 32.0f, 32.0f };
|
||||||
|
SDL_SetRenderDrawColor(renderer, 0, 255, 0, 255); // Green for ground
|
||||||
|
SDL_RenderFillRect(renderer, &destRect);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
6
render.h
6
render.h
@ -8,8 +8,14 @@
|
|||||||
|
|
||||||
#include "app.h"
|
#include "app.h"
|
||||||
|
|
||||||
|
void Map_LoadSampleData();
|
||||||
|
|
||||||
void Render_Frame(App_t* app);
|
void Render_Frame(App_t* app);
|
||||||
void Render_MainWindowBackground(const App_t* app);
|
void Render_MainWindowBackground(const App_t* app);
|
||||||
|
void Render_GameView(const App_t* app);
|
||||||
|
|
||||||
|
void Render_TitleScreen(const App_t* app);
|
||||||
|
void Render_Game(const App_t* app);
|
||||||
|
|
||||||
void Render_DrawWindowFrame(SDL_Renderer* renderer, int x, int y, int w, int h);
|
void Render_DrawWindowFrame(SDL_Renderer* renderer, int x, int y, int w, int h);
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user