/** * Copyright (c) 2025 Dominic Masters * * This software is released under the MIT License. * https://opensource.org/licenses/MIT */ #include "sceneoverworld.h" #include "rpg/entity/entity.h" #include "display/spritebatch.h" #include "display/framebuffer.h" #include "display/scene/scenemanager.h" #include "display/mesh/quad.h" #include "asset/assetmanager.h" #include "assert/assert.h" #include "display/tileset/tileset_entities.h" sceneoverworld_t SCENE_OVERWORLD; asset_t *testAsset; ref_t testAssetRef; errorret_t sceneOverworldInit(void) { cameraInit(&SCENE_OVERWORLD.camera); glm_vec3_copy((vec3){ 0.0f, 1.0f, 0.0f }, SCENE_OVERWORLD.camera.lookat.up); SCENE_OVERWORLD.camera.perspective.fov = 45; SCENE_OVERWORLD.camera.farClip = 10000.0f; scene_t *scene = &SCENE_MANAGER_SCENES[SCENE_TYPE_OVERWORLD]; scene->flags |= SCENE_FLAG_ACTIVE | SCENE_FLAG_VISIBLE; errorChain(assetManagerLoadAsset( TILESET_ENTITIES.image, &testAsset, &testAssetRef )); errorOk(); } void sceneOverworldUpdate(void) { if(RPG.map == NULL) return; // Move camera to player. const entity_t *start = &RPG.map->entities[0]; const entity_t *end = &RPG.map->entities[RPG.map->entityCount]; while(start < end) { if(start->type == ENTITY_TYPE_PLAYER) { SCENE_OVERWORLD.camera.lookat.target[0] = start->position[0]; SCENE_OVERWORLD.camera.lookat.target[1] = start->position[1]; break; } start++; } } void sceneOverworldRender(void) { const float_t camOffset = 12.0f; const float_t fbWidth = frameBufferGetWidth(FRAMEBUFFER_BOUND); const float_t fbHeight = frameBufferGetHeight(FRAMEBUFFER_BOUND); const float_t aspect = fbWidth / fbHeight; const float_t pixelPerfectOffset = tanf( (glm_rad(180) - SCENE_OVERWORLD.camera.perspective.fov) / 2.0f ) * (fbHeight/ 2.0f); // glm_vec3_copy((vec3){ // -100.0f, -100.0f, 0.0f // }, SCENE_OVERWORLD.camera.lookat.target); glm_vec3_copy((vec3){ SCENE_OVERWORLD.camera.lookat.target[0], SCENE_OVERWORLD.camera.lookat.target[1] + camOffset, SCENE_OVERWORLD.camera.lookat.target[2] + pixelPerfectOffset }, SCENE_OVERWORLD.camera.lookat.position); cameraPushMatrix(&SCENE_OVERWORLD.camera); if(RPG.map != NULL) sceneOverworldRenderMap(RPG.map); spriteBatchFlush(); cameraPopMatrix(); } void sceneOverworldRenderMap(const map_t *map) { assertNotNull(map, "Map pointer cannot be NULL"); // Draw base layer sceneOverworldRenderMapLayer(map, &map->base); // Draw entities const entity_t *start = &map->entities[0]; const entity_t *end = &map->entities[map->entityCount]; while(start < end) { // Render entity here. sceneOverworldRenderEntity(start); start++; } spriteBatchFlush(); // Draw overlay layer. sceneOverworldRenderMapLayer(map, &map->overlay); } void sceneOverworldRenderEntity(const entity_t *entity) { assertNotNull(entity, "Entity pointer cannot be NULL"); assertTrue(entity->type < ENTITY_TYPE_COUNT, "Invalid entity type"); assertTrue(entity->type != ENTITY_TYPE_NULL, "Cannot have NULL entity type"); vec4 uv; tilesetPositionGetUV(&TILESET_ENTITIES, entity->direction, 0, uv); // For now, just draw a placeholder quad. spriteBatchPush( &testAsset->paletteImage.texture, entity->position[0], entity->position[1], entity->position[0] + TILESET_ENTITIES.tileWidth, entity->position[1] + TILESET_ENTITIES.tileHeight, COLOR_WHITE, uv[0], uv[1], uv[2], uv[3] ); } void sceneOverworldRenderMapLayer(const map_t *map, const maplayer_t *layer) { assertNotNull(layer, "Map layer pointer cannot be NULL"); for(uint32_t y = 0; y < map->height; y++) { for(uint32_t x = 0; x < map->width; x++) { const tile_t *tile = &layer->tiles[y * map->width + x]; if(tile->id == 0) continue; spriteBatchPush( NULL, x * TILESET_ENTITIES.tileWidth, y * TILESET_ENTITIES.tileHeight, (x + 1) * TILESET_ENTITIES.tileWidth, (y + 1) * TILESET_ENTITIES.tileHeight, COLOR_RED, 0, 0, 1, 1 ); } } spriteBatchFlush(); } void sceneOverworldDispose(void) { // Dispose of the overworld scene. if(testAsset) assetUnlock(testAsset, testAssetRef); }