summary refs log tree commit diff
path: root/src/Camera.c
diff options
context:
space:
mode:
authorWlodekM <[email protected]>2024-06-16 10:35:45 +0300
committerWlodekM <[email protected]>2024-06-16 10:35:45 +0300
commitabef6da56913f1c55528103e60a50451a39628b1 (patch)
treeb3c8092471ecbb73e568cd0d336efa0e7871ee8d /src/Camera.c
initial commit
Diffstat (limited to 'src/Camera.c')
-rw-r--r--src/Camera.c355
1 files changed, 355 insertions, 0 deletions
diff --git a/src/Camera.c b/src/Camera.c
new file mode 100644
index 0000000..4956d0e
--- /dev/null
+++ b/src/Camera.c
@@ -0,0 +1,355 @@
+#include "Camera.h"
+#include "ExtMath.h"
+#include "Game.h"
+#include "Window.h"
+#include "Graphics.h"
+#include "Funcs.h"
+#include "Gui.h"
+#include "Entity.h"
+#include "Input.h"
+#include "Event.h"
+#include "Options.h"
+#include "Picking.h"
+#include "Platform.h"
+
+struct _CameraData Camera;
+static struct RayTracer cameraClipPos;
+static Vec2 cam_rotOffset;
+static cc_bool cam_isForwardThird;
+static float cam_deltaX, cam_deltaY;
+
+static void Camera_OnRawMovement(float deltaX, float deltaY) {
+	cam_deltaX += deltaX; cam_deltaY += deltaY;
+}
+
+void Camera_KeyLookUpdate(float delta) {
+	if (Gui.InputGrab) return;
+	/* divide by 25 to have reasonable sensitivity for default mouse sens */
+	float amount = (Camera.Sensitivity / 25.0f) * (1000 * delta);
+
+	if (InputBind_IsPressed(BIND_LOOK_UP))    cam_deltaY -= amount;
+	if (InputBind_IsPressed(BIND_LOOK_DOWN))  cam_deltaY += amount;
+	if (InputBind_IsPressed(BIND_LOOK_LEFT))  cam_deltaX -= amount;
+	if (InputBind_IsPressed(BIND_LOOK_RIGHT)) cam_deltaX += amount;
+}
+
+/*########################################################################################################################*
+*--------------------------------------------------Perspective camera-----------------------------------------------------*
+*#########################################################################################################################*/
+static void PerspectiveCamera_GetProjection(struct Matrix* proj) {
+	float fovy = Camera.Fov * MATH_DEG2RAD;
+	float aspectRatio = (float)Game.Width / (float)Game.Height;
+	Gfx_CalcPerspectiveMatrix(proj, fovy, aspectRatio, (float)Game_ViewDistance);
+}
+
+static void PerspectiveCamera_GetView(struct Matrix* mat) {
+	Vec3 pos = Camera.CurrentPos;
+	Vec2 rot = Camera.Active->GetOrientation();
+	Matrix_LookRot(mat, pos, rot);
+	Matrix_MulBy(mat, &Camera.TiltM);
+}
+
+static void PerspectiveCamera_GetPickedBlock(struct RayTracer* t) {
+	struct LocalPlayer* p = Entities.CurPlayer;
+	struct Entity* e      = &p->Base;
+
+	Vec3 dir    = Vec3_GetDirVector(e->Yaw * MATH_DEG2RAD, e->Pitch * MATH_DEG2RAD + Camera.TiltPitch);
+	Vec3 eyePos = Entity_GetEyePosition(e);
+	Picking_CalcPickedBlock(&eyePos, &dir, p->ReachDistance, t);
+}
+
+#define CAMERA_SENSI_FACTOR (0.0002f / 3.0f * MATH_RAD2DEG)
+
+static Vec2 PerspectiveCamera_GetMouseDelta(float delta) {
+	float sensitivity = CAMERA_SENSI_FACTOR * Camera.Sensitivity;
+	static float speedX, speedY, newSpeedX, newSpeedY, accelX, accelY;
+	Vec2 v;
+
+	if (Camera.Smooth) {
+		accelX = (cam_deltaX - speedX) * 35 / Camera.Mass;
+		accelY = (cam_deltaY - speedY) * 35 / Camera.Mass;
+		newSpeedX = accelX * delta + speedX;
+		newSpeedY = accelY * delta + speedY;
+
+		/* High acceleration means velocity overshoots the correct position on low FPS, */
+		/* causing wiggling. If newSpeed has opposite sign of speed, set speed to 0 */
+		if (newSpeedX * speedX < 0) speedX = 0;
+		else speedX = newSpeedX;
+		if (newSpeedY * speedY < 0) speedY = 0;
+		else speedY = newSpeedY;
+	} else {
+		speedX = cam_deltaX;
+		speedY = cam_deltaY;
+	}
+
+	v.x = speedX * sensitivity; v.y = speedY * sensitivity;
+	if (Camera.Invert) v.y = -v.y;
+	return v;
+}
+
+static void PerspectiveCamera_UpdateMouseRotation(struct LocalPlayer* p, float delta) {
+	struct Entity* e = &p->Base;
+	struct LocationUpdate update;
+	Vec2 rot = PerspectiveCamera_GetMouseDelta(delta);
+
+	if (Input_IsAltPressed() && Camera.Active->isThirdPerson) {
+		cam_rotOffset.x += rot.x; cam_rotOffset.y += rot.y;
+		return;
+	}
+	
+	update.flags = LU_HAS_YAW | LU_HAS_PITCH;
+	update.yaw   = e->next.yaw   + rot.x;
+	update.pitch = e->next.pitch + rot.y;
+	update.pitch = Math_ClampAngle(update.pitch);
+
+	/* Need to make sure we don't cross the vertical axes, because that gets weird. */
+	if (update.pitch >= 90.0f && update.pitch <= 270.0f) {
+		update.pitch = e->next.pitch < 180.0f ? 90.0f : 270.0f;
+	}
+	e->VTABLE->SetLocation(e, &update);
+}
+
+static void PerspectiveCamera_UpdateMouse(struct LocalPlayer* p, float delta) {
+	if (!Gui.InputGrab && Window_Main.Focused) Window_UpdateRawMouse();
+
+	PerspectiveCamera_UpdateMouseRotation(p, delta);
+	cam_deltaX = 0; cam_deltaY = 0;
+}
+
+static void PerspectiveCamera_CalcViewBobbing(struct LocalPlayer* p, float t, float velTiltScale) {
+	struct Entity* e = &p->Base;
+
+	struct Matrix tiltY, velX;
+	float vel, fall;
+	if (!Game_ViewBobbing) { 
+		Camera.TiltM     = Matrix_Identity;
+		Camera.TiltPitch = 0.0f;
+		return; 
+	}
+
+	Matrix_RotateZ(&Camera.TiltM, -p->Tilt.TiltX                  * e->Anim.BobStrength);
+	Matrix_RotateX(&tiltY,        Math_AbsF(p->Tilt.TiltY) * 3.0f * e->Anim.BobStrength);
+	Matrix_MulBy(&Camera.TiltM, &tiltY);
+
+	Camera.BobbingHor = (e->Anim.BobbingHor * 0.3f) * e->Anim.BobStrength;
+	Camera.BobbingVer = (e->Anim.BobbingVer * 0.6f) * e->Anim.BobStrength;
+
+	/* When standing on the ground, velocity.y is -0.08 (-gravity) */
+	/* So add 0.08 to counteract that, so that vel is 0 when standing on ground */
+	vel  = 0.08f + Math_Lerp(p->OldVelocity.y, e->Velocity.y, t);
+	fall = -vel * 0.05f * p->Tilt.VelTiltStrength / velTiltScale;
+
+	Matrix_RotateX(&velX, fall);
+	Matrix_MulBy(&Camera.TiltM, &velX);
+	if (!Game_ClassicMode) Camera.TiltPitch = fall;
+}
+
+
+/*########################################################################################################################*
+*---------------------------------------------------First person camera---------------------------------------------------*
+*#########################################################################################################################*/
+static Vec2 FirstPersonCamera_GetOrientation(void) {
+	struct LocalPlayer* p = Entities.CurPlayer;
+	struct Entity* e = &p->Base;
+
+	Vec2 v;	
+	v.x = e->Yaw   * MATH_DEG2RAD; 
+	v.y = e->Pitch * MATH_DEG2RAD;
+	return v;
+}
+
+static Vec3 FirstPersonCamera_GetPosition(float t) {
+	struct LocalPlayer* p = Entities.CurPlayer;
+	struct Entity* e = &p->Base;
+
+	Vec3 camPos   = Entity_GetEyePosition(e);
+	float yaw     = e->Yaw * MATH_DEG2RAD;
+	PerspectiveCamera_CalcViewBobbing(p, t, 1);
+	
+	camPos.y += Camera.BobbingVer;
+	camPos.x += Camera.BobbingHor * Math_CosF(yaw);
+	camPos.z += Camera.BobbingHor * Math_SinF(yaw);
+	return camPos;
+}
+
+static cc_bool FirstPersonCamera_Zoom(float amount) { return false; }
+static struct Camera cam_FirstPerson = {
+	false,
+	PerspectiveCamera_GetProjection,  PerspectiveCamera_GetView,
+	FirstPersonCamera_GetOrientation, FirstPersonCamera_GetPosition,
+	PerspectiveCamera_UpdateMouse,    Camera_OnRawMovement,
+	Window_EnableRawMouse,            Window_DisableRawMouse,
+	PerspectiveCamera_GetPickedBlock, FirstPersonCamera_Zoom,
+};
+
+
+/*########################################################################################################################*
+*---------------------------------------------------Third person camera---------------------------------------------------*
+*#########################################################################################################################*/
+#define DEF_ZOOM 3.0f
+static float dist_third = DEF_ZOOM, dist_forward = DEF_ZOOM;
+
+static Vec2 ThirdPersonCamera_GetOrientation(void) {
+	struct LocalPlayer* p = Entities.CurPlayer;
+	struct Entity* e = &p->Base;
+
+	Vec2 v;	
+	v.x = e->Yaw   * MATH_DEG2RAD; 
+	v.y = e->Pitch * MATH_DEG2RAD;
+	if (cam_isForwardThird) { v.x += MATH_PI; v.y = -v.y; }
+
+	v.x += cam_rotOffset.x * MATH_DEG2RAD; 
+	v.y += cam_rotOffset.y * MATH_DEG2RAD;
+	return v;
+}
+
+static float ThirdPersonCamera_GetZoom(struct LocalPlayer* p) {
+	float dist = cam_isForwardThird ? dist_forward : dist_third;
+	/* Don't allow zooming out when -fly */
+	if (dist > DEF_ZOOM && !LocalPlayer_CheckCanZoom(p)) dist = DEF_ZOOM;
+	return dist;
+}
+
+static Vec3 ThirdPersonCamera_GetPosition(float t) {
+	struct LocalPlayer* p = Entities.CurPlayer;
+	struct Entity* e = &p->Base;
+
+	float dist = ThirdPersonCamera_GetZoom(p);
+	Vec3 target, dir;
+	Vec2 rot;
+
+	PerspectiveCamera_CalcViewBobbing(p, t, dist);
+	target = Entity_GetEyePosition(e);
+	target.y += Camera.BobbingVer;
+
+	rot = Camera.Active->GetOrientation();
+	dir = Vec3_GetDirVector(rot.x, rot.y);
+	Vec3_Negate(&dir, &dir);
+
+	Picking_ClipCameraPos(&target, &dir, dist, &cameraClipPos);
+	return cameraClipPos.intersect;
+}
+
+static cc_bool ThirdPersonCamera_Zoom(float amount) {
+	float* dist   = cam_isForwardThird ? &dist_forward : &dist_third;
+	float newDist = *dist - amount;
+
+	*dist = max(newDist, 2.0f); 
+	return true;
+}
+
+static struct Camera cam_ThirdPerson = {
+	true,
+	PerspectiveCamera_GetProjection,  PerspectiveCamera_GetView,
+	ThirdPersonCamera_GetOrientation, ThirdPersonCamera_GetPosition,
+	PerspectiveCamera_UpdateMouse,    Camera_OnRawMovement,
+	Window_EnableRawMouse,            Window_DisableRawMouse,
+	PerspectiveCamera_GetPickedBlock, ThirdPersonCamera_Zoom,
+};
+static struct Camera cam_ForwardThird = {
+	true,
+	PerspectiveCamera_GetProjection,  PerspectiveCamera_GetView,
+	ThirdPersonCamera_GetOrientation, ThirdPersonCamera_GetPosition,
+	PerspectiveCamera_UpdateMouse,    Camera_OnRawMovement,
+	Window_EnableRawMouse,            Window_DisableRawMouse,
+	PerspectiveCamera_GetPickedBlock, ThirdPersonCamera_Zoom,
+};
+
+
+/*########################################################################################################################*
+*-----------------------------------------------------General camera------------------------------------------------------*
+*#########################################################################################################################*/
+static void OnRawMovement(void* obj, float deltaX, float deltaY) {
+	Camera.Active->OnRawMovement(deltaX, deltaY);
+}
+
+static void OnAxisUpdate(void* obj, int port, int axis, float x, float y) {
+	if (!Input.RawMode) return;
+	if (Gamepad_AxisBehaviour[axis] != AXIS_BEHAVIOUR_CAMERA) return;
+
+	Camera.Active->OnRawMovement(x, y);
+}
+
+static void OnHacksChanged(void* obj) {
+	struct HacksComp* h = &Entities.CurPlayer->Hacks;
+	/* Leave third person if not allowed anymore */
+	if (!h->CanUseThirdPerson || !h->Enabled) Camera_CycleActive();
+}
+
+void Camera_CycleActive(void) {
+	struct LocalPlayer* p = &LocalPlayer_Instances[0];
+	if (Game_ClassicMode) return;
+	Camera.Active = Camera.Active->next;
+
+	if (!p->Hacks.CanUseThirdPerson || !p->Hacks.Enabled) {
+		Camera.Active = &cam_FirstPerson;
+	}
+	cam_isForwardThird = Camera.Active == &cam_ForwardThird;
+
+	/* reset rotation offset when changing cameras */
+	cam_rotOffset.x = 0.0f; cam_rotOffset.y = 0.0f;
+	Camera_UpdateProjection();
+}
+
+static struct Camera* cams_head;
+static struct Camera* cams_tail;
+void Camera_Register(struct Camera* cam) {
+	LinkedList_Append(cam, cams_head, cams_tail);
+	/* want a circular linked list */
+	cam->next = cams_head;
+}
+
+static cc_bool cam_focussed;
+void Camera_CheckFocus(void) {
+	cc_bool focus = Gui.InputGrab == NULL;
+	if (focus == cam_focussed) return;
+	cam_focussed = focus;
+
+	if (focus) {
+		Camera.Active->AcquireFocus();
+	} else {
+		Camera.Active->LoseFocus();
+	}
+}
+
+void Camera_SetFov(int fov) {
+	if (Camera.Fov == fov) return;
+	Camera.Fov = fov;
+	Camera_UpdateProjection();
+}
+
+void Camera_UpdateProjection(void) {
+	Camera.Active->GetProjection(&Gfx.Projection);
+	Gfx_LoadMatrix(MATRIX_PROJECTION, &Gfx.Projection);
+	Event_RaiseVoid(&GfxEvents.ProjectionChanged);
+}
+
+static void OnInit(void) {
+	Camera_Register(&cam_FirstPerson);
+	Camera_Register(&cam_ThirdPerson);
+	Camera_Register(&cam_ForwardThird);
+
+	Camera.Active = &cam_FirstPerson;
+	Event_Register_(&PointerEvents.RawMoved,      NULL, OnRawMovement);
+	Event_Register_(&ControllerEvents.AxisUpdate, NULL, OnAxisUpdate);
+	Event_Register_(&UserEvents.HackPermsChanged, NULL, OnHacksChanged);
+
+#ifdef CC_BUILD_WIN
+	Camera.Sensitivity = Options_GetInt(OPT_SENSITIVITY, 1, 200, 40);
+#else
+	Camera.Sensitivity = Options_GetInt(OPT_SENSITIVITY, 1, 200, 30);
+#endif
+	Camera.Clipping    = Options_GetBool(OPT_CAMERA_CLIPPING, true);
+	Camera.Invert      = Options_GetBool(OPT_INVERT_MOUSE, false);
+	Camera.Mass        = Options_GetFloat(OPT_CAMERA_MASS, 1, 100, 20);
+	Camera.Smooth      = Options_GetBool(OPT_CAMERA_SMOOTH, false);
+
+	Camera.DefaultFov  = Options_GetInt(OPT_FIELD_OF_VIEW, 1, 179, 70);
+	Camera.Fov         = Camera.DefaultFov;
+	Camera.ZoomFov     = Camera.DefaultFov;
+	Camera_UpdateProjection();
+}
+
+struct IGameComponent Camera_Component = {
+	OnInit /* Init  */
+};