From e5c1864fd8ed5be19663a90eb23a9c158c0aa43e Mon Sep 17 00:00:00 2001 From: Milan Nikolic Date: Fri, 10 Nov 2023 08:27:42 +0100 Subject: [PATCH] Reimplement camera in Go --- raylib/rcamera.go | 353 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 341 insertions(+), 12 deletions(-) diff --git a/raylib/rcamera.go b/raylib/rcamera.go index 7b32804..938c10a 100644 --- a/raylib/rcamera.go +++ b/raylib/rcamera.go @@ -1,21 +1,350 @@ -//go:build !android -// +build !android - package rl -/* -#include "raylib.h" -*/ -import "C" +// GetCameraForward - Returns the cameras forward vector (normalized) +func GetCameraForward(camera *Camera) Vector3 { + return Vector3Normalize(Vector3Subtract(camera.Target, camera.Position)) +} + +// GetCameraUp - Returns the cameras up vector (normalized) +// Note: The up vector might not be perpendicular to the forward vector +func GetCameraUp(camera *Camera) Vector3 { + return Vector3Normalize(camera.Up) +} + +// GetCameraRight - Returns the cameras right vector (normalized) +func GetCameraRight(camera *Camera) Vector3 { + forward := GetCameraForward(camera) + up := GetCameraUp(camera) + + return Vector3CrossProduct(forward, up) +} + +// CameraMoveForward - Moves the camera in its forward direction +func CameraMoveForward(camera *Camera, distance float32, moveInWorldPlane uint8) { + forward := GetCameraForward(camera) + + if moveInWorldPlane != 0 { + // Project vector onto world plane + forward.Y = float32(0) + forward = Vector3Normalize(forward) + } + + // Scale by distance + forward = Vector3Scale(forward, distance) + + // Move position and target + camera.Position = Vector3Add(camera.Position, forward) + camera.Target = Vector3Add(camera.Target, forward) +} + +// CameraMoveUp - Moves the camera in its up direction +func CameraMoveUp(camera *Camera, distance float32) { + up := GetCameraUp(camera) + + // Scale by distance + up = Vector3Scale(up, distance) + + // Move position and target + camera.Position = Vector3Add(camera.Position, up) + camera.Target = Vector3Add(camera.Target, up) +} + +// CameraMoveRight - Moves the camera target in its current right direction +func CameraMoveRight(camera *Camera, distance float32, moveInWorldPlane uint8) { + right := GetCameraRight(camera) + + if moveInWorldPlane != 0 { + // Project vector onto world plane + right.Y = float32(0) + right = Vector3Normalize(right) + } + + // Scale by distance + right = Vector3Scale(right, distance) + + // Move position and target + camera.Position = Vector3Add(camera.Position, right) + camera.Target = Vector3Add(camera.Target, right) +} + +// CameraMoveToTarget - Moves the camera position closer/farther to/from the camera target +func CameraMoveToTarget(camera *Camera, delta float32) { + distance := Vector3Distance(camera.Position, camera.Target) + + // Apply delta + distance = distance + delta + + // Distance must be greater than 0 + if distance <= float32(0) { + distance = 0.001 + } + + // Set new distance by moving the position along the forward vector + forward := GetCameraForward(camera) + camera.Position = Vector3Add(camera.Target, Vector3Scale(forward, -distance)) +} + +// CameraYaw - Rotates the camera around its up vector +// Yaw is "looking left and right" +// If rotateAroundTarget is false, the camera rotates around its position +// Note: angle must be provided in radians +func CameraYaw(camera *Camera, angle float32, rotateAroundTarget uint8) { + // Rotation axis + var up = GetCameraUp(camera) + + // View vector + var targetPosition = Vector3Subtract(camera.Target, camera.Position) + + // Rotate view vector around up axis + targetPosition = Vector3RotateByAxisAngle(targetPosition, up, angle) + + if rotateAroundTarget != 0 { + // Move position relative to target + camera.Position = Vector3Subtract(camera.Target, targetPosition) + } else { + // Move target relative to position + camera.Target = Vector3Add(camera.Position, targetPosition) + } +} + +// CameraPitch - Rotates the camera around its right vector, pitch is "looking up and down" +// - lockView prevents camera overrotation (aka "somersaults") +// - rotateAroundTarget defines if rotation is around target or around its position +// - rotateUp rotates the up direction as well (typically only usefull in CAMERA_FREE) +// +// NOTE: angle must be provided in radians +func CameraPitch(camera *Camera, angle float32, lockView uint8, rotateAroundTarget uint8, rotateUp uint8) { + // Up direction + var up = GetCameraUp(camera) + + // View vector + var targetPosition = Vector3Subtract(camera.Target, camera.Position) + + if lockView != 0 { + // In these camera modes we clamp the Pitch angle + // to allow only viewing straight up or down. + + // Clamp view up + var maxAngleUp float32 = Vector3Angle(up, targetPosition) + maxAngleUp = maxAngleUp - 0.001 // avoid numerical errors + if angle > maxAngleUp { + angle = maxAngleUp + } + + // Clamp view down + var maxAngleDown float32 = Vector3Angle(Vector3Negate(up), targetPosition) + maxAngleDown = maxAngleDown * -1.0 // downwards angle is negative + maxAngleDown = maxAngleDown + 0.001 // avoid numerical errors + if angle < maxAngleDown { + angle = maxAngleDown + } + } + + // Rotation axis + var right = GetCameraRight(camera) + + // Rotate view vector around right axis + targetPosition = Vector3RotateByAxisAngle(targetPosition, right, angle) + + if rotateAroundTarget != 0 { + // Move position relative to target + camera.Position = Vector3Subtract(camera.Target, targetPosition) + } else { + // Move target relative to position + camera.Target = Vector3Add(camera.Position, targetPosition) + } + + if rotateUp != 0 { + // Rotate up direction around right axis + camera.Up = Vector3RotateByAxisAngle(camera.Up, right, angle) + } +} + +// CameraRoll - Rotates the camera around its forward vector +// Roll is "turning your head sideways to the left or right" +// Note: angle must be provided in radians +func CameraRoll(camera *Camera, angle float32) { + // Rotation axis + var forward = GetCameraForward(camera) + + // Rotate up direction around forward axis + camera.Up = Vector3RotateByAxisAngle(camera.Up, forward, angle) +} + +// GetCameraViewMatrix - Returns the camera view matrix +func GetCameraViewMatrix(camera *Camera) Matrix { + return MatrixLookAt(camera.Position, camera.Target, camera.Up) +} + +// GetCameraProjectionMatrix - Returns the camera projection matrix +func GetCameraProjectionMatrix(camera *Camera, aspect float32) Matrix { + if camera.Projection == CameraPerspective { + return MatrixPerspective(camera.Fovy*(Pi/180.0), aspect, 0.01, 1000.0) + } else if camera.Projection == CameraOrthographic { + top := camera.Fovy / 2.0 + right := top * aspect + + return MatrixOrtho(-right, right, -top, top, 0.01, 1000.0) + } + + return MatrixIdentity() +} // UpdateCamera - Update camera position for selected mode +// Camera mode: CameraFree, CameraFirstPerson, CameraThirdPerson, CameraOrbital or Custom func UpdateCamera(camera *Camera, mode CameraMode) { - ccamera := camera.cptr() - C.UpdateCamera(ccamera, C.int(mode)) + var mousePositionDelta = GetMouseDelta() + + moveInWorldPlaneBool := mode == CameraFirstPerson || mode == CameraThirdPerson + var moveInWorldPlane uint8 + if moveInWorldPlaneBool { + moveInWorldPlane = 1 + } + + rotateAroundTargetBool := mode == CameraThirdPerson || mode == CameraOrbital + var rotateAroundTarget uint8 + if rotateAroundTargetBool { + rotateAroundTarget = 1 + } + + lockViewBool := mode == CameraFirstPerson || mode == CameraThirdPerson || mode == CameraOrbital + var lockView uint8 + if lockViewBool { + lockView = 1 + } + + var rotateUp uint8 + + if mode == CameraOrbital { + // Orbital can just orbit + var rotation = MatrixRotate(GetCameraUp(camera), 0.5*GetFrameTime()) + var view = Vector3Subtract(camera.Position, camera.Target) + view = Vector3Transform(view, rotation) + camera.Position = Vector3Add(camera.Target, view) + } else { + // Camera rotation + if IsKeyDown(KeyDown) { + CameraPitch(camera, -0.03, lockView, rotateAroundTarget, rotateUp) + } + if IsKeyDown(KeyUp) { + CameraPitch(camera, 0.03, lockView, rotateAroundTarget, rotateUp) + } + if IsKeyDown(KeyRight) { + CameraYaw(camera, -0.03, rotateAroundTarget) + } + if IsKeyDown(KeyLeft) { + CameraYaw(camera, 0.03, rotateAroundTarget) + } + if IsKeyDown(KeyQ) { + CameraRoll(camera, -0.03) + } + if IsKeyDown(KeyE) { + CameraRoll(camera, 0.03) + } + + // Camera movement + if !(IsGamepadAvailable(0)) { + // Camera pan (for CameraFree) + if mode == CameraFree && IsMouseButtonDown(MouseMiddleButton) { + var mouseDelta = GetMouseDelta() + if mouseDelta.X > 0.0 { + CameraMoveRight(camera, 0.2, moveInWorldPlane) + } + if mouseDelta.X < 0.0 { + CameraMoveRight(camera, -0.2, moveInWorldPlane) + } + if mouseDelta.Y > 0.0 { + CameraMoveUp(camera, -0.2) + } + if mouseDelta.Y < 0.0 { + CameraMoveUp(camera, 0.2) + } + } else { + // Mouse support + CameraYaw(camera, -mousePositionDelta.X*0.003, rotateAroundTarget) + CameraPitch(camera, -mousePositionDelta.Y*0.003, lockView, rotateAroundTarget, rotateUp) + } + + // Keyboard support + if IsKeyDown(KeyW) { + CameraMoveForward(camera, 0.09, moveInWorldPlane) + } + if IsKeyDown(KeyA) { + CameraMoveRight(camera, -0.09, moveInWorldPlane) + } + if IsKeyDown(KeyS) { + CameraMoveForward(camera, -0.09, moveInWorldPlane) + } + if IsKeyDown(KeyD) { + CameraMoveRight(camera, 0.09, moveInWorldPlane) + } + } else { + // Gamepad controller support + CameraYaw(camera, -(GetGamepadAxisMovement(0, GamepadAxisRightX)*float32(2))*0.003, rotateAroundTarget) + CameraPitch(camera, -(GetGamepadAxisMovement(0, GamepadAxisRightY)*float32(2))*0.003, lockView, rotateAroundTarget, rotateUp) + + if GetGamepadAxisMovement(0, GamepadAxisLeftY) <= -0.25 { + CameraMoveForward(camera, 0.09, moveInWorldPlane) + } + if GetGamepadAxisMovement(0, GamepadAxisLeftX) <= -0.25 { + CameraMoveRight(camera, -0.09, moveInWorldPlane) + } + if GetGamepadAxisMovement(0, GamepadAxisLeftY) >= 0.25 { + CameraMoveForward(camera, -0.09, moveInWorldPlane) + } + if GetGamepadAxisMovement(0, GamepadAxisLeftX) >= 0.25 { + CameraMoveRight(camera, 0.09, moveInWorldPlane) + } + } + + if mode == CameraFree { + if IsKeyDown(KeySpace) { + CameraMoveUp(camera, 0.09) + } + if IsKeyDown(KeyLeftControl) { + CameraMoveUp(camera, -0.09) + } + } + } + + if mode == CameraThirdPerson || mode == CameraOrbital || mode == CameraFree { + // Zoom target distance + CameraMoveToTarget(camera, -GetMouseWheelMove()) + if IsKeyPressed(KeyKpSubtract) { + CameraMoveToTarget(camera, 2.0) + } + if IsKeyPressed(KeyKpAdd) { + CameraMoveToTarget(camera, -2.0) + } + } } -// UpdateCameraPro - Update camera movement/rotation +// UpdateCameraPro - Update camera movement, movement/rotation values should be provided by user func UpdateCameraPro(camera *Camera, movement Vector3, rotation Vector3, zoom float32) { - ccamera := camera.cptr() - C.UpdateCameraPro(ccamera, *movement.cptr(), *rotation.cptr(), C.float(zoom)) + // Required values + // movement.X - Move forward/backward + // movement.Y - Move right/left + // movement.Z - Move up/down + // rotation.X - yaw + // rotation.Y - pitch + // rotation.Z - roll + // zoom - Move towards target + + lockView := uint8(1) + rotateAroundTarget := uint8(0) + rotateUp := uint8(0) + moveInWorldPlane := uint8(1) + + // Camera rotation + CameraPitch(camera, -rotation.Y*(Pi/180.0), lockView, rotateAroundTarget, rotateUp) + CameraYaw(camera, -rotation.X*(Pi/180.0), rotateAroundTarget) + CameraRoll(camera, rotation.Z*(Pi/180.0)) + + // Camera movement + CameraMoveForward(camera, movement.X, moveInWorldPlane) + CameraMoveRight(camera, movement.Y, moveInWorldPlane) + CameraMoveUp(camera, movement.Z) + + // Zoom target distance + CameraMoveToTarget(camera, zoom) }