/** * 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 } ); }