diff options
| author | Ethan Morgan <ethan@gweithio.com> | 2026-02-14 16:44:06 +0000 |
|---|---|---|
| committer | Ethan Morgan <ethan@gweithio.com> | 2026-02-14 16:44:06 +0000 |
| commit | 54409423f767d8b1cf30cb7d0efca6b4ca138823 (patch) | |
| tree | d915ac7828703ce4b963efdd9728a1777ba18c1e /apps/openmb/scene/Camera.cpp | |
Diffstat (limited to 'apps/openmb/scene/Camera.cpp')
| -rw-r--r-- | apps/openmb/scene/Camera.cpp | 203 |
1 files changed, 203 insertions, 0 deletions
diff --git a/apps/openmb/scene/Camera.cpp b/apps/openmb/scene/Camera.cpp new file mode 100644 index 0000000..d3a3e7d --- /dev/null +++ b/apps/openmb/scene/Camera.cpp @@ -0,0 +1,203 @@ +#include "Camera.hpp" + +#include <algorithm> +#include <glm/gtc/matrix_transform.hpp> +#include <vector> + +namespace scene { +Camera::Camera () + : mPosition( 0.0f, 0.0f, 3.0f ), mFront( 0.0f, 0.0f, -1.0f ), mWorldUp( 0.0f, 1.0f, 0.0f ), mYaw( -90.0f ), + mPitch( 0.0f ), mMovementSpeed( 3.0f ), mMouseSensitivity( 0.1f ), mFov( 60.0f ), + mVelocity( 0.0f, 0.0f, 0.0f ), mFlying( true ), mGrounded( false ), mSpeedMultiplier( 1.0f ) { + updateCameraVectors(); +} + +Camera::Camera ( const glm::vec3& position, const glm::vec3& up, float yaw, float pitch ) + : mPosition( position ), mWorldUp( up ), mYaw( yaw ), mPitch( pitch ), mMovementSpeed( 3.0f ), + mMouseSensitivity( 0.1f ), mFov( 60.0f ), mVelocity( 0.0f, 0.0f, 0.0f ), mFlying( true ), mGrounded( false ), + mSpeedMultiplier( 1.0f ) { + mFront = glm::vec3( 0.0f, 0.0f, -1.0f ); + updateCameraVectors(); +} + +glm::mat4 Camera::getViewMatrix () const { + return glm::lookAt( mPosition, mPosition + mFront, mUp ); +} + +glm::mat4 Camera::getProjectionMatrix ( float aspect ) const { + return glm::perspective( glm::radians( mFov ), aspect, 0.1f, 100.0f ); +} + +void Camera::processKeyboard ( Movement dir, float deltaTime ) { + float velocity = mMovementSpeed * mSpeedMultiplier * deltaTime; + + glm::vec3 moveForward = mFront; + glm::vec3 moveRight = mRight; + if( !mFlying ) { + moveForward.y = 0.0f; + if( glm::length( moveForward ) < 1e-6f ) + moveForward = glm::vec3( 0.0f, 0.0f, -1.0f ); + moveForward = glm::normalize( moveForward ); + moveRight = glm::normalize( glm::cross( moveForward, mWorldUp ) ); + } + + if( dir == Movement::Forward ) + mPosition += moveForward * velocity; + if( dir == Movement::Backward ) + mPosition -= moveForward * velocity; + if( dir == Movement::Left ) + mPosition -= moveRight * velocity; + if( dir == Movement::Right ) + mPosition += moveRight * velocity; + if( dir == Movement::Up && mFlying ) + mPosition += mWorldUp * velocity; + if( dir == Movement::Down && mFlying ) + mPosition -= mWorldUp * velocity; +} + +void Camera::toggleFly () { + mFlying = !mFlying; + if( mFlying ) { + + mVelocity.y = 0.0f; + } +} + +void Camera::setSpeedMultiplier ( float m ) { + mSpeedMultiplier = m; +} + +static bool aabbOverlap ( const glm::vec3& amin, const glm::vec3& amax, const glm::vec3& bmin, + const glm::vec3& bmax ) { + return ( amin.x <= bmax.x && amax.x >= bmin.x ) && ( amin.y <= bmax.y && amax.y >= bmin.y ) && + ( amin.z <= bmax.z && amax.z >= bmin.z ); +} + +void Camera::updatePhysics ( float deltaTime, const std::vector<std::pair<glm::vec3, glm::vec3>>& worldAABBs, + float floorY ) { + const glm::vec3 halfExtents( 0.3f, 0.9f, 0.3f ); + + if( mFlying ) { + mGrounded = false; + return; + } + + if( !mFlying ) { + const float gravity = -9.8f; + mVelocity.y += gravity * deltaTime; + if( mVelocity.y < -50.0f ) + mVelocity.y = -50.0f; + } + + glm::vec3 newPos = mPosition + mVelocity * deltaTime; + + float camBottom = newPos.y - halfExtents.y; + if( camBottom < floorY ) { + newPos.y = floorY + halfExtents.y; + mVelocity.y = 0.0f; + mGrounded = true; + } else { + mGrounded = false; + } + + for( const auto& box : worldAABBs ) { + glm::vec3 bmin = box.first; + glm::vec3 bmax = box.second; + + glm::vec3 camMin = newPos - halfExtents; + glm::vec3 camMax = newPos + halfExtents; + + if( !aabbOverlap( camMin, camMax, bmin, bmax ) ) + continue; + + float ox = std::min( camMax.x, bmax.x ) - std::max( camMin.x, bmin.x ); + float oy = std::min( camMax.y, bmax.y ) - std::max( camMin.y, bmin.y ); + float oz = std::min( camMax.z, bmax.z ) - std::max( camMin.z, bmin.z ); + + if( ox <= oy && ox <= oz ) { + + float boxCenterX = ( bmin.x + bmax.x ) * 0.5f; + if( newPos.x < boxCenterX ) + newPos.x -= ox; + else + newPos.x += ox; + } else if( oy <= ox && oy <= oz ) { + float boxCenterY = ( bmin.y + bmax.y ) * 0.5f; + if( newPos.y < boxCenterY ) { + newPos.y -= oy; + mVelocity.y = 0.0f; + } else { + newPos.y += oy; + mVelocity.y = 0.0f; + mGrounded = true; + } + } else { + float boxCenterZ = ( bmin.z + bmax.z ) * 0.5f; + if( newPos.z < boxCenterZ ) + newPos.z -= oz; + else + newPos.z += oz; + } + } + + mPosition = newPos; +} + +void Camera::jump () { + if( mFlying ) + return; + if( mGrounded ) { + const float jumpImpulse = 5.0f; + mVelocity.y = jumpImpulse; + mGrounded = false; + } +} + +bool Camera::isFlying () const { + return mFlying; +} + +bool Camera::isGrounded () const { + return mGrounded; +} + +float Camera::getSpeedMultiplier () const { + return mSpeedMultiplier; +} + +void Camera::processMouseMovement ( float xoffset, float yoffset, bool constrainPitch ) { + xoffset *= mMouseSensitivity; + yoffset *= mMouseSensitivity; + + mYaw += xoffset; + mPitch += yoffset; + + if( constrainPitch ) { + if( mPitch > 89.0f ) + mPitch = 89.0f; + if( mPitch < -89.0f ) + mPitch = -89.0f; + } + + updateCameraVectors(); +} + +void Camera::processMouseScroll ( float yoffset ) { + mFov -= yoffset; + if( mFov < 1.0f ) + mFov = 1.0f; + if( mFov > 90.0f ) + mFov = 90.0f; +} + +void Camera::updateCameraVectors () { + glm::vec3 front; + front.x = cos( glm::radians( mYaw ) ) * cos( glm::radians( mPitch ) ); + front.y = sin( glm::radians( mPitch ) ); + front.z = sin( glm::radians( mYaw ) ) * cos( glm::radians( mPitch ) ); + mFront = glm::normalize( front ); + mRight = glm::normalize( glm::cross( mFront, mWorldUp ) ); + mUp = glm::normalize( glm::cross( mRight, mFront ) ); +} + +} // namespace scene |