208 lines
5.9 KiB
C++
208 lines
5.9 KiB
C++
// Copyright © 2013-14 Meteoric Games Ltd
|
|
// Licensed under the terms of the GPL v3. See licenses/GPL-3.txt
|
|
|
|
#include "ShipCockpit.h"
|
|
|
|
#include "Easing.h"
|
|
#include "Game.h"
|
|
#include "Pi.h"
|
|
#include "Player.h"
|
|
#include "WorldView.h"
|
|
#include "graphics/Renderer.h"
|
|
#include "ship/CameraController.h"
|
|
|
|
ShipCockpit::ShipCockpit(const std::string &modelName) :
|
|
m_shipDir(0.0),
|
|
m_shipYaw(0.0),
|
|
m_dir(0.0),
|
|
m_yaw(0.0),
|
|
m_rotInterp(0.f),
|
|
m_transInterp(0.f),
|
|
m_gForce(0.f),
|
|
m_offset(0.f),
|
|
m_shipVel(0.f),
|
|
m_translate(0.0),
|
|
m_transform(matrix4x4d::Identity())
|
|
{
|
|
assert(!modelName.empty());
|
|
SetModel(modelName.c_str());
|
|
assert(GetModel());
|
|
SetColliding(false);
|
|
m_icc = nullptr;
|
|
}
|
|
|
|
ShipCockpit::~ShipCockpit()
|
|
{
|
|
}
|
|
|
|
void ShipCockpit::Render(Graphics::Renderer *renderer, const Camera *camera, const vector3d &viewCoords, const matrix4x4d &viewTransform)
|
|
{
|
|
PROFILE_SCOPED()
|
|
RenderModel(renderer, camera, viewCoords, viewTransform);
|
|
}
|
|
|
|
inline void ShipCockpit::resetInternalCameraController()
|
|
{
|
|
m_icc = static_cast<InternalCameraController *>(Pi::game->GetWorldView()->shipView->GetCameraController());
|
|
}
|
|
|
|
void ShipCockpit::Update(const Player *player, float timeStep)
|
|
{
|
|
m_transform = matrix4x4d::Identity();
|
|
|
|
if (m_icc == nullptr) {
|
|
// I don't know where to put this
|
|
resetInternalCameraController();
|
|
}
|
|
|
|
double rotX;
|
|
double rotY;
|
|
m_icc->getRots(rotX, rotY);
|
|
m_transform.RotateX(rotX);
|
|
m_transform.RotateY(rotY);
|
|
|
|
vector3d cur_dir = player->GetOrient().VectorZ().Normalized();
|
|
if (cur_dir.Dot(m_shipDir) < 1.0f) {
|
|
m_rotInterp = 0.0f;
|
|
m_shipDir = cur_dir;
|
|
}
|
|
|
|
//---------------------------------------- Acceleration
|
|
float cur_vel = CalculateSignedForwardVelocity(-cur_dir, player->GetVelocity()); // Forward is -Z
|
|
float gforce = Clamp(floorf(((fabs(cur_vel) - m_shipVel) / timeStep) / 9.8f), -COCKPIT_MAX_GFORCE, COCKPIT_MAX_GFORCE);
|
|
if (fabs(cur_vel) > 500000.0f || // Limit gforce measurement so we don't get astronomical fluctuations
|
|
fabs(gforce - m_gForce) > 100.0) { // Smooth out gforce one frame spikes, sometimes happens when hitting max speed due to the thrust limiters
|
|
gforce = 0.0f;
|
|
}
|
|
if (fabs(gforce - m_gForce) > 100.0) {
|
|
gforce = 0.0f;
|
|
}
|
|
if (fabs(m_translate.z - m_offset) < 0.001f) {
|
|
m_transInterp = 0.0f;
|
|
}
|
|
float offset = (gforce > 14.0f ? -1.0f : (gforce < -14.0f ? 1.0f : 0.0f)) * COCKPIT_ACCEL_OFFSET;
|
|
m_transInterp += timeStep * COCKPIT_ACCEL_INTERP_MULTIPLIER;
|
|
if (m_transInterp > 1.0f) {
|
|
m_transInterp = 1.0f;
|
|
m_translate.z = offset;
|
|
}
|
|
m_translate.z = Easing::Quad::EaseIn(double(m_transInterp), m_translate.z, offset - m_translate.z, 1.0);
|
|
m_gForce = gforce;
|
|
m_offset = offset;
|
|
m_shipVel = cur_vel;
|
|
|
|
//------------------------------------------ Rotation
|
|
// For yaw/pitch
|
|
vector3d rot_axis = cur_dir.Cross(m_dir).Normalized();
|
|
vector3d yaw_axis = player->GetOrient().VectorY().Normalized();
|
|
vector3d pitch_axis = player->GetOrient().VectorX().Normalized();
|
|
float dot = cur_dir.Dot(m_dir);
|
|
float angle = acos(dot);
|
|
// For roll
|
|
if (yaw_axis.Dot(m_shipYaw) < 1.0f) {
|
|
m_rotInterp = 0.0f;
|
|
m_shipYaw = yaw_axis;
|
|
}
|
|
vector3d rot_yaw_axis = yaw_axis.Cross(m_yaw).Normalized();
|
|
float dot_yaw = yaw_axis.Dot(m_yaw);
|
|
float angle_yaw = acos(dot_yaw);
|
|
|
|
if (dot < 1.0f || dot_yaw < 1.0f) {
|
|
// Lag/Recovery interpolation
|
|
m_rotInterp += timeStep * COCKPIT_ROTATION_INTERP_MULTIPLIER;
|
|
if (m_rotInterp > 1.0f) {
|
|
m_rotInterp = 1.0f;
|
|
}
|
|
|
|
// Yaw and Pitch
|
|
if (dot < 1.0f) {
|
|
if (angle > DEG2RAD(COCKPIT_LAG_MAX_ANGLE)) {
|
|
angle = DEG2RAD(COCKPIT_LAG_MAX_ANGLE);
|
|
}
|
|
angle = Easing::Quad::EaseOut(m_rotInterp, angle, -angle, 1.0f);
|
|
m_dir = cur_dir;
|
|
if (angle >= 0.0f) {
|
|
m_dir.ArbRotate(rot_axis, angle);
|
|
// Apply pitch
|
|
vector3d yz_proj = (m_dir - (m_dir.Dot(pitch_axis) * pitch_axis)).Normalized();
|
|
float pitch_cos = yz_proj.Dot(cur_dir);
|
|
float pitch_angle = 0.0f;
|
|
if (pitch_cos < 1.0f) {
|
|
pitch_angle = acos(pitch_cos);
|
|
if (rot_axis.Dot(pitch_axis) < 0) {
|
|
pitch_angle = -pitch_angle;
|
|
}
|
|
m_transform.RotateX(-pitch_angle);
|
|
}
|
|
// Apply yaw
|
|
vector3d xz_proj = (m_dir - (m_dir.Dot(yaw_axis) * yaw_axis)).Normalized();
|
|
float yaw_cos = xz_proj.Dot(cur_dir);
|
|
float yaw_angle = 0.0f;
|
|
if (yaw_cos < 1.0f) {
|
|
yaw_angle = acos(yaw_cos);
|
|
if (rot_axis.Dot(yaw_axis) < 0) {
|
|
yaw_angle = -yaw_angle;
|
|
}
|
|
m_transform.RotateY(-yaw_angle);
|
|
}
|
|
} else {
|
|
angle = 0.0f;
|
|
}
|
|
}
|
|
|
|
// Roll
|
|
if (dot_yaw < 1.0f) {
|
|
if (angle_yaw > DEG2RAD(COCKPIT_LAG_MAX_ANGLE)) {
|
|
angle_yaw = DEG2RAD(COCKPIT_LAG_MAX_ANGLE);
|
|
}
|
|
if (dot_yaw < 1.0f) {
|
|
angle_yaw = Easing::Quad::EaseOut(m_rotInterp, angle_yaw, -angle_yaw, 1.0f);
|
|
}
|
|
m_yaw = yaw_axis;
|
|
if (angle_yaw >= 0.0f) {
|
|
m_yaw.ArbRotate(rot_yaw_axis, angle_yaw);
|
|
// Apply roll
|
|
vector3d xy_proj = (m_yaw - (m_yaw.Dot(cur_dir) * cur_dir)).Normalized();
|
|
float roll_cos = xy_proj.Dot(yaw_axis);
|
|
float roll_angle = 0.0f;
|
|
if (roll_cos < 1.0f) {
|
|
roll_angle = acos(roll_cos);
|
|
if (rot_yaw_axis.Dot(cur_dir) < 0) {
|
|
roll_angle = -roll_angle;
|
|
}
|
|
m_transform.RotateZ(-roll_angle);
|
|
}
|
|
} else {
|
|
angle_yaw = 0.0f;
|
|
}
|
|
}
|
|
} else {
|
|
m_rotInterp = 0.0f;
|
|
}
|
|
}
|
|
|
|
void ShipCockpit::RenderCockpit(Graphics::Renderer *renderer, const Camera *camera, FrameId frameId)
|
|
{
|
|
PROFILE_SCOPED()
|
|
renderer->ClearDepthBuffer();
|
|
Body::SetFrame(frameId);
|
|
Render(renderer, camera, m_translate, m_transform);
|
|
Body::SetFrame(FrameId::Invalid);
|
|
}
|
|
|
|
void ShipCockpit::OnActivated(const Player *player)
|
|
{
|
|
assert(player);
|
|
m_dir = player->GetOrient().VectorZ().Normalized();
|
|
m_yaw = player->GetOrient().VectorY().Normalized();
|
|
m_shipDir = m_dir;
|
|
m_shipYaw = m_yaw;
|
|
m_shipVel = CalculateSignedForwardVelocity(-m_shipDir, player->GetVelocity());
|
|
}
|
|
|
|
float ShipCockpit::CalculateSignedForwardVelocity(const vector3d &normalized_forward, const vector3d &velocity)
|
|
{
|
|
float velz_cos = velocity.Dot(normalized_forward);
|
|
return (velz_cos * normalized_forward).Length() * (velz_cos < 0.0 ? -1.0 : 1.0);
|
|
}
|