Files
librw/skeleton/camera.cpp
T
aap da98dab4a5 skeleton: tracked input state, analog pad api, camera manipulator, file paths
EventHandler now keeps keys/mouse/pad state so apps don't have to;
sk::Camera is an orbit/dolly/pan manipulator over an rw::Camera;
GetFilePath mangles paths for cdrom0:/host:/host0: on ps2.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-26 17:52:52 +02:00

269 lines
5.6 KiB
C++

#include <math.h>
#include <rw.h>
#include "skeleton.h"
#define PI 3.14159265359f
namespace sk {
using rw::Quat;
using rw::V3d;
void
Camera::init(void)
{
m_position.set(0.0f, 6.0f, 0.0f);
m_target.set(0.0f, 0.0f, 0.0f);
m_up.set(0.0f, 0.0f, 1.0f);
m_localup = m_up;
m_fov = 70.0f;
m_aspectRatio = 1.0f;
m_near = 0.1f;
m_far = 100.0f;
m_rwcam = nil;
}
void
Camera::attach(rw::Camera *cam)
{
m_rwcam = cam;
}
void
Camera::update(void)
{
rw::Frame *f;
V3d forward, left, nup;
if(m_rwcam == nil)
return;
m_rwcam->setNearPlane(m_near);
m_rwcam->setFarPlane(m_far);
m_rwcam->setFOV(m_fov, m_aspectRatio);
f = m_rwcam->getFrame();
if(f == nil)
return;
forward = normalize(sub(m_target, m_position));
left = normalize(cross(m_up, forward));
nup = cross(forward, left);
f->matrix.right = left; // lol
f->matrix.up = nup;
f->matrix.at = forward;
f->matrix.pos = m_position;
f->matrix.optimize();
f->updateObjects();
}
void
Camera::setTarget(V3d target)
{
m_position = sub(m_position, sub(m_target, target));
m_target = target;
}
float
Camera::getHeading(void)
{
V3d dir = sub(m_target, m_position);
float a = atan2(dir.y, dir.x) - PI/2.0f;
return m_localup.z < 0.0f ? a-PI : a;
}
void
Camera::turn(float yaw, float pitch)
{
V3d dir, right;
Quat r;
dir = sub(m_target, m_position);
r = Quat::rotation(yaw, rw::makeV3d(0.0f, 0.0f, 1.0f));
dir = rotate(dir, r);
m_localup = rotate(m_localup, r);
right = normalize(cross(dir, m_localup));
r = Quat::rotation(pitch, right);
dir = rotate(dir, r);
m_localup = normalize(cross(right, dir));
if(m_localup.z >= 0.0f) m_up.z = 1.0f;
else m_up.z = -1.0f;
m_target = add(m_position, dir);
}
void
Camera::orbit(float yaw, float pitch)
{
V3d dir, right;
Quat r;
dir = sub(m_target, m_position);
r = Quat::rotation(yaw, rw::makeV3d(0.0f, 0.0f, 1.0f));
dir = rotate(dir, r);
m_localup = rotate(m_localup, r);
right = normalize(cross(dir, m_localup));
r = Quat::rotation(-pitch, right);
dir = rotate(dir, r);
m_localup = normalize(cross(right, dir));
if(m_localup.z >= 0.0f) m_up.z = 1.0f;
else m_up.z = -1.0f;
m_position = sub(m_target, dir);
}
void
Camera::dolly(float dist)
{
V3d dir = setlength(sub(m_target, m_position), dist);
m_position = add(m_position, dir);
m_target = add(m_target, dir);
}
void
Camera::zoom(float dist)
{
V3d dir = sub(m_target, m_position);
float curdist = length(dir);
if(dist >= curdist)
dist = curdist - 0.01f;
dir = setlength(dir, dist);
m_position = add(m_position, dir);
}
void
Camera::pan(float x, float y)
{
V3d dir = normalize(sub(m_target, m_position));
V3d right = normalize(cross(dir, m_up));
V3d localup = normalize(cross(right, dir));
dir = add(scale(right, x), scale(localup, y));
m_position = add(m_position, dir);
m_target = add(m_target, dir);
}
float
Camera::distanceTo(V3d v)
{
return length(sub(m_position, v));
}
float
Camera::distanceToTarget(void)
{
return length(sub(m_position, m_target));
}
rw::bool32
Camera::isSphereVisible(rw::Sphere *sph, rw::Matrix *xform)
{
rw::Sphere sphere = *sph;
if(xform)
rw::V3d::transformPoints(&sphere.center, &sphere.center, 1, xform);
return m_rwcam->frustumTestSphere(&sphere) != rw::Camera::SPHEREOUTSIDE;
}
/*
* Controls
*/
CameraControls cameraControls;
void
CameraControlsInit(void)
{
cameraControls.mouseSens = 4.5f;
cameraControls.wheelSens = 0.2f;
cameraControls.stickSens = 2.0f;
cameraControls.moveSpeed = 30.0f;
cameraControls.speedUpMult = 4.0f;
}
// Mouse motion since the last frame, normalized to the viewport.
// This is a displacement, so it must NOT be scaled by dt.
static void
MouseDelta(float *dx, float *dy)
{
*dx = (float)(mouse.posx - prevmouse.posx) / (float)globals.width;
*dy = (float)(mouse.posy - prevmouse.posy) / (float)globals.height;
}
// Move along world up, keeping the view direction.
static void
MoveUp(Camera *cam, float dist)
{
V3d d = scale(cam->m_up, dist);
cam->m_position = add(cam->m_position, d);
cam->m_target = add(cam->m_target, d);
}
void
CameraEditorControls(Camera *cam, float dt)
{
float dx, dy, s, d;
MouseDelta(&dx, &dy);
s = cameraControls.mouseSens;
d = cam->distanceToTarget();
if(mouse.buttons & 1)
cam->orbit(-dx*s, dy*s);
else if(mouse.buttons & 2)
cam->pan(-dx*d, dy*d);
else if(mouse.buttons & 4)
cam->zoom(-dy*s*d);
// zoom proportional to distance so it approaches the target smoothly
if(mouse.wheelDelta != 0.0f)
cam->zoom(mouse.wheelDelta * cameraControls.wheelSens * d);
// stick deflection is a rate
if(pad.connected){
cam->orbit(-pad.leftx*cameraControls.stickSens*dt,
-pad.lefty*cameraControls.stickSens*dt);
cam->pan(-pad.rightx*d*dt, pad.righty*d*dt);
if(pad.buttons & PADL1)
cam->zoom(-d*dt);
if(pad.buttons & PADR1)
cam->zoom(d*dt);
}
}
void
CameraFlyControls(Camera *cam, float dt)
{
float dx, dy, s, speed;
MouseDelta(&dx, &dy);
s = cameraControls.mouseSens;
if(mouse.buttons & 1)
cam->turn(-dx*s, -dy*s);
// stick look is a rate
if(pad.connected)
cam->turn(-pad.rightx*cameraControls.stickSens*dt,
-pad.righty*cameraControls.stickSens*dt);
speed = cameraControls.moveSpeed;
if(keys[KEY_LSHIFT] || keys[KEY_RSHIFT] || (pad.buttons & PADR1))
speed *= cameraControls.speedUpMult;
speed *= dt;
if(keys['W']) cam->dolly(speed);
if(keys['S']) cam->dolly(-speed);
if(keys['A']) cam->pan(-speed, 0.0f);
if(keys['D']) cam->pan(speed, 0.0f);
if(keys['Q']) MoveUp(cam, -speed);
if(keys['E']) MoveUp(cam, speed);
if(pad.connected){
cam->dolly(-pad.lefty*speed);
cam->pan(pad.leftx*speed, 0.0f);
if(pad.buttons & PADL2) MoveUp(cam, -speed);
if(pad.buttons & PADR2) MoveUp(cam, speed);
}
}
}