All checks were successful
Build Dusk / run-tests (push) Successful in 2m12s
Build Dusk / build-linux (push) Successful in 1m49s
Build Dusk / build-psp (push) Successful in 1m52s
213 lines
5.4 KiB
C
213 lines
5.4 KiB
C
/**
|
|
* Copyright (c) 2025 Dominic Masters
|
|
*
|
|
* This software is released under the MIT License.
|
|
* https://opensource.org/licenses/MIT
|
|
*/
|
|
|
|
#include "scenemap.h"
|
|
#include "scene/scenedata.h"
|
|
#include "display/spritebatch.h"
|
|
#include "assert/assert.h"
|
|
#include "asset/asset.h"
|
|
#include "rpg/entity/entity.h"
|
|
#include "rpg/overworld/map.h"
|
|
#include "display/screen.h"
|
|
#include "rpg/rpgcamera.h"
|
|
#include "util/memory.h"
|
|
#include "duskdefs.h"
|
|
|
|
errorret_t sceneMapInit(scenedata_t *data) {
|
|
// Init the camera.
|
|
cameraInitPerspective(&data->sceneMap.camera);
|
|
data->sceneMap.camera.projType = CAMERA_PROJECTION_TYPE_PERSPECTIVE_FLIPPED;
|
|
data->sceneMap.camera.viewType = CAMERA_VIEW_TYPE_LOOKAT_PIXEL_PERFECT;
|
|
glm_vec3_zero(data->sceneMap.camera.lookatPixelPerfect.offset);
|
|
data->sceneMap.camera.lookatPixelPerfect.offset[1] = RPG_CAMERA_Z_OFFSET;
|
|
glm_vec3_copy(
|
|
(vec3){ 0.0f, 0.0f, 0.0f },
|
|
data->sceneMap.camera.lookatPixelPerfect.target
|
|
);
|
|
glm_vec3_copy(
|
|
(vec3){ 0.0f, 1.0f, 0.0f },
|
|
data->sceneMap.camera.lookatPixelPerfect.up
|
|
);
|
|
data->sceneMap.camera.lookatPixelPerfect.pixelsPerUnit = (
|
|
RPG_CAMERA_PIXELS_PER_UNIT
|
|
);
|
|
data->sceneMap.camera.perspective.fov = glm_rad(RPG_CAMERA_FOV);
|
|
|
|
errorOk();
|
|
}
|
|
|
|
void sceneMapUpdate(scenedata_t *data) {
|
|
|
|
}
|
|
|
|
void sceneMapGetWorldPosition(const worldpos_t pos, vec3 outPosition) {
|
|
assertNotNull(outPosition, "Output position cannot be NULL");
|
|
|
|
outPosition[0] = pos.x * TILE_WIDTH;
|
|
outPosition[1] = pos.y * TILE_HEIGHT;
|
|
outPosition[2] = pos.z * TILE_DEPTH;
|
|
|
|
// Handle stair tiles.
|
|
tile_t tile = mapGetTile(pos);
|
|
if(tileIsRamp(tile)) {
|
|
outPosition[2] += TILE_DEPTH / 2.0f;
|
|
}
|
|
}
|
|
|
|
void sceneMapEntityGetPosition(const entity_t *entity, vec3 outPosition) {
|
|
assertNotNull(entity, "Entity cannot be NULL");
|
|
assertNotNull(outPosition, "Output position cannot be NULL");
|
|
|
|
// Get position
|
|
sceneMapGetWorldPosition(entity->position, outPosition);
|
|
|
|
// Add a small offset so we render above the tile
|
|
outPosition[2] += 0.1f;
|
|
|
|
// Add animation offset(s)
|
|
switch(entity->animation) {
|
|
case ENTITY_ANIM_WALK: {
|
|
float_t animPercentage = entity->animTime / ENTITY_ANIM_WALK_DURATION;
|
|
|
|
vec3 lastPosition;
|
|
sceneMapGetWorldPosition(entity->lastPosition, lastPosition);
|
|
|
|
vec3 offset;
|
|
glm_vec3_sub(outPosition, lastPosition, offset);
|
|
glm_vec3_scale(offset, -animPercentage, offset);
|
|
glm_vec3_add(outPosition, offset, outPosition);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void sceneMapRender(scenedata_t *data) {
|
|
if(!mapIsLoaded()) return;
|
|
|
|
// Look at target.
|
|
vec3 cameraTarget;
|
|
switch(RPG_CAMERA.mode) {
|
|
case RPG_CAMERA_MODE_FREE:
|
|
sceneMapGetWorldPosition(RPG_CAMERA.free, cameraTarget);
|
|
break;
|
|
|
|
case RPG_CAMERA_MODE_FOLLOW_ENTITY: {
|
|
const entity_t *ent = &ENTITIES[RPG_CAMERA.followEntity.followEntityId];
|
|
sceneMapEntityGetPosition(ent, cameraTarget);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
glm_vec3_zero(cameraTarget);
|
|
break;
|
|
}
|
|
|
|
glm_vec3_copy(
|
|
cameraTarget,
|
|
data->sceneMap.camera.lookatPixelPerfect.target
|
|
);
|
|
|
|
// Push camera
|
|
cameraPushMatrix(&data->sceneMap.camera);
|
|
|
|
// Render map probably.
|
|
sceneMapRenderMap();
|
|
|
|
// Render ents
|
|
entity_t *ent = ENTITIES;
|
|
do {
|
|
sceneMapRenderEntity(ent);
|
|
} while(++ent, ent < &ENTITIES[ENTITY_COUNT]);
|
|
spriteBatchFlush();
|
|
|
|
// Finished, pop back camera.
|
|
cameraPopMatrix();
|
|
}
|
|
|
|
void sceneMapRenderEntity(entity_t *entity) {
|
|
assertNotNull(entity, "Entity cannot be NULL");
|
|
|
|
if(entity->type == ENTITY_TYPE_NULL) return;
|
|
|
|
vec3 posMin, posMax;
|
|
vec3 size = { TILE_WIDTH, TILE_HEIGHT, TILE_DEPTH };
|
|
sceneMapEntityGetPosition(entity, posMin);
|
|
glm_vec3_add(posMin, size, posMax);
|
|
|
|
// TEST: Change color depending on dir.
|
|
color_t testColor;
|
|
switch(entity->direction) {
|
|
case ENTITY_DIR_NORTH:
|
|
testColor = COLOR_BLUE;
|
|
break;
|
|
case ENTITY_DIR_EAST:
|
|
testColor = COLOR_GREEN;
|
|
break;
|
|
case ENTITY_DIR_SOUTH:
|
|
testColor = COLOR_CYAN;
|
|
break;
|
|
case ENTITY_DIR_WEST:
|
|
testColor = COLOR_YELLOW;
|
|
break;
|
|
default:
|
|
testColor = COLOR_WHITE;
|
|
break;
|
|
}
|
|
|
|
vec2 uv0 = { 0.0f, 0.0f };
|
|
vec2 uv1 = { 1.0f, 1.0f };
|
|
|
|
spriteBatchPush3D(NULL, posMin, posMax, testColor, uv0, uv1);
|
|
}
|
|
|
|
void sceneMapRenderMap() {
|
|
assertTrue(mapIsLoaded(), "No map loaded to render");
|
|
|
|
// For each chunk.
|
|
for(uint32_t i = 0; i < MAP_CHUNK_COUNT; i++) {
|
|
chunk_t *chunk = MAP.chunkOrder[i];
|
|
|
|
for(uint8_t j = 0; j < chunk->meshCount; j++) {
|
|
mesh_t *mesh = &chunk->meshes[j];
|
|
if(mesh->vertexCount == 0) continue;
|
|
textureBind(NULL);
|
|
meshDraw(mesh, -1, -1);
|
|
}
|
|
|
|
// vec3 min, max;
|
|
// min[0] = chunk->position.x * CHUNK_WIDTH * TILE_WIDTH;
|
|
// min[1] = chunk->position.y * CHUNK_HEIGHT * TILE_HEIGHT;
|
|
// min[2] = chunk->position.z * CHUNK_DEPTH * TILE_DEPTH;
|
|
|
|
// max[0] = min[0] + (CHUNK_WIDTH * TILE_WIDTH);
|
|
// max[1] = min[1] + (CHUNK_HEIGHT * TILE_HEIGHT);
|
|
// max[2] = min[2];
|
|
|
|
// color_t color = COLOR_WHITE;
|
|
// if(chunk->position.x % 2 == 0) {
|
|
// color = (chunk->position.y % 2 == 0) ? COLOR_BLACK : COLOR_WHITE;
|
|
// } else {
|
|
// color = (chunk->position.y % 2 == 0) ? COLOR_WHITE : COLOR_BLACK;
|
|
// }
|
|
|
|
// spriteBatchPush3D(
|
|
// NULL,
|
|
// min,
|
|
// max,
|
|
// color,
|
|
// (vec2){ 0.0f, 0.0f },
|
|
// (vec2){ 1.0f, 1.0f }
|
|
// );
|
|
}
|
|
}
|
|
|
|
void sceneMapDispose(scenedata_t *data) {
|
|
}
|