Files
dusk/src/duskrpg/scene/overworldscene.c
T
2026-07-19 14:51:57 -05:00

139 lines
4.9 KiB
C

/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "overworldscene.h"
#include "scene/scene.h"
#include "entity/entitymanager.h"
#include "entity/component/display/entityposition.h"
#include "entity/component/display/entitycamera.h"
#include "entity/component/display/entityrenderable.h"
#include "entity/component/physics/entityphysics.h"
#include "display/mesh/plane.h"
#include "display/mesh/capsule.h"
#include "display/color.h"
#include "time/time.h"
// Radius must stay well outside the floor's footprint (a 20x20 plane has a
// corner-to-center distance of 10*sqrt(2) =~ 14.1) -- orbiting inside that
// puts parts of the floor's own geometry near/behind the camera's view
// direction, which the PSP's legacy GU pipeline can't clip properly and
// drops the whole triangle instead of clipping it.
#define OVERWORLD_SCENE_CAMERA_ORBIT_RADIUS 18.0f
#define OVERWORLD_SCENE_CAMERA_ORBIT_HEIGHT 10.0f
#define OVERWORLD_SCENE_CAMERA_ORBIT_SPEED 0.5f
static overworldcameraorbit_t OVERWORLD_SCENE_CAMERA_ORBIT;
sceneid_t overworldSceneCreate(void) {
sceneid_t sceneId = sceneCreate();
entitymanager_t *mgr = sceneGetEntities(sceneId);
// Camera, orbiting the origin (see overworldSceneCameraOrbitUpdate).
entityid_t camEntity = entityManagerAdd(mgr);
componentid_t camPosition = entityAddComponent(
mgr, camEntity, COMPONENT_TYPE_POSITION
);
entityAddComponent(mgr, camEntity, COMPONENT_TYPE_CAMERA);
OVERWORLD_SCENE_CAMERA_ORBIT = (overworldcameraorbit_t){
.angle = 0.0f,
.radius = OVERWORLD_SCENE_CAMERA_ORBIT_RADIUS,
.height = OVERWORLD_SCENE_CAMERA_ORBIT_HEIGHT,
.speed = OVERWORLD_SCENE_CAMERA_ORBIT_SPEED
};
entityUpdateAdd(
mgr, camEntity, overworldSceneCameraOrbitUpdate, camPosition,
&OVERWORLD_SCENE_CAMERA_ORBIT
);
// Position it correctly for the very first rendered frame, rather than
// waiting for the first fixed tick to run.
overworldSceneCameraOrbitUpdate(
mgr, camEntity, camPosition, &OVERWORLD_SCENE_CAMERA_ORBIT
);
// Static ground plane. Physics ignores the entity's position component --
// the shape's own normal/distance fully define the plane in world space.
entityid_t planeEntity = entityManagerAdd(mgr);
componentid_t planePosition = entityAddComponent(
mgr, planeEntity, COMPONENT_TYPE_POSITION
);
entityPositionSetLocalPosition(
mgr, planeEntity, planePosition, (vec3){ -10.0f, 0.0f, -10.0f }
);
entityPositionSetLocalScale(
mgr, planeEntity, planePosition, (vec3){ 20.0f, 1.0f, 20.0f }
);
componentid_t planePhysics = entityAddComponent(
mgr, planeEntity, COMPONENT_TYPE_PHYSICS
);
entityPhysicsSetBodyType(
mgr, planeEntity, planePhysics, PHYSICS_BODY_STATIC
);
entityPhysicsSetShape(mgr, planeEntity, planePhysics, (physicsshape_t){
.type = PHYSICS_SHAPE_PLANE,
.data.plane = { .normal = { 0.0f, 1.0f, 0.0f }, .distance = 0.0f }
});
componentid_t planeRenderable = entityAddComponent(
mgr, planeEntity, COMPONENT_TYPE_RENDERABLE
);
entityrenderable_t *planeR = componentGetData(
mgr, planeEntity, planeRenderable, COMPONENT_TYPE_RENDERABLE
);
planeR->data.material.meshes[0] = &PLANE_MESH_SIMPLE;
entityRenderableSetColor(mgr, planeEntity, planeRenderable, COLOR_GRAY);
// Player: dynamic capsule body, moved relative to the camera by
// COMPONENT_TYPE_PLAYER's own update callback (see entityplayer.c).
entityid_t playerEntity = entityManagerAdd(mgr);
componentid_t playerPosition = entityAddComponent(
mgr, playerEntity, COMPONENT_TYPE_POSITION
);
entityPositionSetLocalPosition(
mgr, playerEntity, playerPosition, (vec3){ 0.0f, 2.0f, 0.0f }
);
componentid_t playerPhysics = entityAddComponent(
mgr, playerEntity, COMPONENT_TYPE_PHYSICS
);
entityPhysicsSetShape(mgr, playerEntity, playerPhysics, (physicsshape_t){
.type = PHYSICS_SHAPE_CAPSULE,
.data.capsule = { .radius = 0.5f, .halfHeight = 0.5f }
});
componentid_t playerRenderable = entityAddComponent(
mgr, playerEntity, COMPONENT_TYPE_RENDERABLE
);
entityrenderable_t *playerR = componentGetData(
mgr, playerEntity, playerRenderable, COMPONENT_TYPE_RENDERABLE
);
playerR->data.material.meshes[0] = &CAPSULE_MESH_SIMPLE;
entityRenderableSetColor(mgr, playerEntity, playerRenderable, COLOR_BLUE);
entityAddComponent(mgr, playerEntity, COMPONENT_TYPE_PLAYER);
return sceneId;
}
void overworldSceneCameraOrbitUpdate(
entitymanager_t *mgr,
const entityid_t entityId,
const componentid_t componentId,
void *user
) {
overworldcameraorbit_t *orbit = (overworldcameraorbit_t *)user;
orbit->angle += TIME.delta * orbit->speed;
vec3 eye = {
cosf(orbit->angle) * orbit->radius,
orbit->height,
sinf(orbit->angle) * orbit->radius
};
entityPositionLookAt(
mgr, entityId, componentId,
eye,
(vec3){ 0.0f, 0.0f, 0.0f },
(vec3){ 0.0f, 1.0f, 0.0f }
);
}