139 lines
4.9 KiB
C
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 }
|
|
);
|
|
}
|