// Copyright (c) 2026 Dominic Masters // // This software is released under the MIT License. // https://opensource.org/licenses/MIT // 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. var CAMERA_ORBIT_RADIUS = 18.0; var CAMERA_ORBIT_HEIGHT = 10.0; var CAMERA_ORBIT_SPEED = 0.5; var cameraOrbitAngle = 0.0; var cameraPosition = null; // Orbits the camera around the world origin at a fixed radius/height/ // speed, always looking back at the origin. Called once up front (so the // very first rendered frame is already positioned correctly) and then // once per frame via update() below. function updateCameraOrbit() { cameraOrbitAngle += Time.delta * CAMERA_ORBIT_SPEED; var eyeX = Math.cos(cameraOrbitAngle) * CAMERA_ORBIT_RADIUS; var eyeY = CAMERA_ORBIT_HEIGHT; var eyeZ = Math.sin(cameraOrbitAngle) * CAMERA_ORBIT_RADIUS; cameraPosition.lookAt(eyeX, eyeY, eyeZ, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0); } module.exports = { // Called once by Scene.set(), right after it creates and activates the // scene this module owns. init: function() { // Camera, orbiting the origin (see updateCameraOrbit above). var camera = new Entity(); cameraPosition = camera.add(POSITION); camera.add(CAMERA); updateCameraOrbit(); // Static ground plane. Physics ignores the entity's position // component -- the shape's own normal/distance fully define the // plane in world space. var plane = new Entity(); var planePosition = plane.add(POSITION); planePosition.setLocalPosition(-10.0, 0.0, -10.0); planePosition.setLocalScale(20.0, 1.0, 20.0); var planePhysics = plane.add(PHYSICS); planePhysics.setBodyType(PHYSICS_BODY_STATIC); planePhysics.setShape(PHYSICS_SHAPE_PLANE, 0.0, 1.0, 0.0, 0.0); var planeRenderable = plane.add(RENDERABLE); planeRenderable.setMesh(0, MESH_PLANE); planeRenderable.setColor(128, 128, 128, 255); // Player: dynamic capsule body, moved relative to the camera by // PLAYER's own update callback (see entityplayer.c). var player = new Entity(); var playerPosition = player.add(POSITION); playerPosition.setLocalPosition(0.0, 2.0, 0.0); var playerPhysics = player.add(PHYSICS); playerPhysics.setShape(PHYSICS_SHAPE_CAPSULE, 0.5, 0.5); var playerRenderable = player.add(RENDERABLE); playerRenderable.setMesh(0, MESH_CAPSULE); playerRenderable.setColor(0, 0, 255, 255); player.add(PLAYER); }, // Called once per engine frame while this module is the active scene // (see Scene.set(), engineUpdate() -> moduleSceneUpdateCurrent()). update: function() { if(cameraPosition) updateCameraOrbit(); }, // Called once by Scene.set() when this module is replaced by another, // right before the scene it owns is destroyed. dispose: function() { cameraPosition = null; } };