/* * This file is part of the Colobot: Gold Edition source code * Copyright (C) 2001-2016, Daniel Roux, EPSITEC SA & TerranovaTeam * http://epsitec.ch; http://colobot.info; http://github.com/colobot * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see http://gnu.org/licenses */ #include "graphics/engine/camera.h" #include "app/app.h" #include "app/input.h" #include "common/event.h" #include "graphics/engine/engine.h" #include "graphics/engine/terrain.h" #include "graphics/engine/water.h" #include "level/robotmain.h" #include "math/const.h" #include "math/geometry.h" #include "object/object.h" #include "object/object_manager.h" #include "object/interface/carrier_object.h" #include "object/interface/controllable_object.h" #include "object/interface/movable_object.h" #include "object/interface/powered_object.h" #include "object/interface/transportable_object.h" #include "physics/physics.h" // Graphics module namespace namespace Gfx { const float MOUSE_EDGE_MARGIN = 0.01f; //! Changes the level of transparency of an object and objects transported (battery & cargo) void SetTransparency(CObject* obj, float value) { obj->SetTransparency(value); if (obj->Implements(ObjectInterfaceType::Carrier)) { CObject* cargo = dynamic_cast(obj)->GetCargo(); if (cargo != nullptr) cargo->SetTransparency(value); } if (obj->Implements(ObjectInterfaceType::Powered)) { CObject* power = dynamic_cast(obj)->GetPower(); if (power != nullptr) power->SetTransparency(value); } } CCamera::CCamera() { m_engine = CEngine::GetInstancePointer(); m_water = m_engine->GetWater(); m_main = CRobotMain::GetInstancePointer(); m_terrain = m_main->GetTerrain(); m_input = CInput::GetInstancePointer(); m_type = CAM_TYPE_FREE; m_smooth = CAM_SMOOTH_NORM; m_cameraObj = nullptr; m_eyeDistance = 10.0f; m_initDelay = 0.0f; m_actualEye = Math::Vector(0.0f, 0.0f, 0.0f); m_actualLookat = Math::Vector(0.0f, 0.0f, 0.0f); m_finalEye = Math::Vector(0.0f, 0.0f, 0.0f); m_finalLookat = Math::Vector(0.0f, 0.0f, 0.0f); m_normEye = Math::Vector(0.0f, 0.0f, 0.0f); m_normLookat = Math::Vector(0.0f, 0.0f, 0.0f); m_focus = 1.0f; m_eyePt = Math::Vector(0.0f, 0.0f, 0.0f); m_directionH = 0.0f; m_directionV = 0.0f; m_heightEye = 20.0f; m_heightLookat = 0.0f; m_speed = 2.0f; m_backDist = 0.0f; m_backMin = 0.0f; m_addDirectionH = 0.0f; m_addDirectionV = 0.0f; m_transparency = false; m_fixDist = 0.0f; m_fixDirectionH = 0.0f; m_fixDirectionV = 0.0f; m_visitGoal = Math::Vector(0.0f, 0.0f, 0.0f); m_visitDist = 0.0f; m_visitTime = 0.0f; m_visitType = CAM_TYPE_NULL; m_visitDirectionV = 0.0f; m_editHeight = 40.0f; m_remotePan = 0.0f; m_centeringPhase = CAM_PHASE_NULL; m_centeringAngleH = 0.0f; m_centeringAngleV = 0.0f; m_centeringDist = 0.0f; m_centeringCurrentH = 0.0f; m_centeringCurrentV = 0.0f; m_centeringTime = 0.0f; m_centeringProgress = 0.0f; m_effectType = CAM_EFFECT_NULL; m_effectPos = Math::Vector(0.0f, 0.0f, 0.0f); m_effectForce = 0.0f; m_effectProgress = 0.0f; m_effectOffset = Math::Vector(0.0f, 0.0f, 0.0f); m_overType = CAM_OVER_EFFECT_NULL; m_overForce = 0.0f; m_overTime = 0.0f; m_overMode = 0; m_overFadeIn = 0.0f; m_overFadeOut = 0.0f; m_scriptEye = Math::Vector(0.0f, 0.0f, 0.0f); m_scriptLookat = Math::Vector(0.0f, 0.0f, 0.0f); m_effect = true; m_blood = true; m_oldCameraScroll = false; m_cameraInvertX = false; m_cameraInvertY = false; } CCamera::~CCamera() { } void CCamera::SetEffect(bool enable) { m_effect = enable; } bool CCamera::GetEffect() { return m_effect; } void CCamera::SetBlood(bool enable) { m_blood = enable; } bool CCamera::GetBlood() { return m_blood; } void CCamera::SetOldCameraScroll(bool scroll) { m_oldCameraScroll = scroll; } bool CCamera::GetOldCameraScroll() { return m_oldCameraScroll; } void CCamera::SetCameraInvertX(bool invert) { m_cameraInvertX = invert; } bool CCamera::GetCameraInvertX() { return m_cameraInvertX; } void CCamera::SetCameraInvertY(bool invert) { m_cameraInvertY = invert; } bool CCamera::GetCameraInvertY() { return m_cameraInvertY; } void CCamera::Init(Math::Vector eye, Math::Vector lookat, float delay) { m_initDelay = delay; eye.y += m_terrain->GetFloorLevel(eye, true); lookat.y += m_terrain->GetFloorLevel(lookat, true); m_type = CAM_TYPE_FREE; m_eyePt = eye; m_directionH = Math::RotateAngle(eye.x - lookat.x, eye.z - lookat.z) + Math::PI / 2.0f; m_directionV = -Math::RotateAngle(Math::DistanceProjected(eye, lookat), eye.y - lookat.y); m_eyeDistance = 10.0f; m_heightLookat = 10.0f; m_backDist = 30.0f; m_backMin = 10.0f; m_addDirectionH = 0.0f; m_addDirectionV = -Math::PI*0.05f; m_fixDist = 50.0f; m_fixDirectionH = Math::PI*0.25f; m_fixDirectionV = -Math::PI*0.10f; m_centeringPhase = CAM_PHASE_NULL; m_actualEye = m_eyePt; m_actualLookat = Math::LookatPoint(m_eyePt, m_directionH, m_directionV, 50.0f); m_finalEye = m_actualEye; m_finalLookat = m_actualLookat; m_scriptEye = m_actualEye; m_scriptLookat = m_actualLookat; m_focus = 1.00f; m_remotePan = 0.0f; FlushEffect(); FlushOver(); SetType(CAM_TYPE_FREE); } void CCamera::SetControllingObject(CObject* object) { m_cameraObj = object; } CObject* CCamera::GetControllingObject() { return m_cameraObj; } void CCamera::SetType(CameraType type) { m_remotePan = 0.0f; if ( (m_type == CAM_TYPE_BACK) && m_transparency ) { for (CObject* obj : CObjectManager::GetInstancePointer()->GetAllObjects()) { if (IsObjectBeingTransported(obj)) continue; SetTransparency(obj, 0.0f); // opaque object } } m_transparency = false; if (type == CAM_TYPE_INFO || type == CAM_TYPE_VISIT) // xx -> info ? { m_normEye = m_engine->GetEyePt(); m_normLookat = m_engine->GetLookatPt(); m_engine->SetFocus(1.00f); // normal m_type = type; return; } if (m_type == CAM_TYPE_INFO || m_type == CAM_TYPE_VISIT) // info -> xx ? { m_engine->SetFocus(m_focus); // gives initial focus m_type = type; Math::Vector upVec = Math::Vector(0.0f, 1.0f, 0.0f); SetViewParams(m_normEye, m_normLookat, upVec); return; } if ( m_type == CAM_TYPE_BACK && type == CAM_TYPE_FREE ) // back -> free ? m_eyePt = Math::LookatPoint(m_eyePt, m_directionH, m_directionV, -50.0f); if ( m_type == CAM_TYPE_BACK && type == CAM_TYPE_EDIT ) // back -> edit ? m_eyePt = Math::LookatPoint(m_eyePt, m_directionH, m_directionV, -1.0f); if ( m_type == CAM_TYPE_ONBOARD && type == CAM_TYPE_FREE ) // onboard -> free ? m_eyePt = Math::LookatPoint(m_eyePt, m_directionH, m_directionV, -30.0f); if ( m_type == CAM_TYPE_ONBOARD && type == CAM_TYPE_EDIT ) // onboard -> edit ? m_eyePt = Math::LookatPoint(m_eyePt, m_directionH, m_directionV, -30.0f); if ( m_type == CAM_TYPE_ONBOARD && type == CAM_TYPE_EXPLO ) // onboard -> explo ? m_eyePt = Math::LookatPoint(m_eyePt, m_directionH, m_directionV, -50.0f); if ( m_type == CAM_TYPE_BACK && type == CAM_TYPE_EXPLO ) // back -> explo ? m_eyePt = Math::LookatPoint(m_eyePt, m_directionH, m_directionV, -20.0f); if ( type == CAM_TYPE_FIX || type == CAM_TYPE_PLANE ) AbortCentering(); // Special stops framing m_fixDist = 50.0f; if ( type == CAM_TYPE_PLANE ) m_fixDist = 60.0f; if ( type == CAM_TYPE_BACK ) { AbortCentering(); // Special stops framing m_addDirectionH = 0.0f; m_addDirectionV = -Math::PI*0.05f; ObjectType oType; if ( m_cameraObj == nullptr ) oType = OBJECT_NULL; else oType = m_cameraObj->GetType(); m_backDist = 30.0f; if ( oType == OBJECT_BASE ) m_backDist = 200.0f; if ( oType == OBJECT_HUMAN ) m_backDist = 20.0f; if ( oType == OBJECT_TECH ) m_backDist = 20.0f; if ( oType == OBJECT_FACTORY ) m_backDist = 50.0f; if ( oType == OBJECT_RESEARCH ) m_backDist = 40.0f; if ( oType == OBJECT_DERRICK ) m_backDist = 40.0f; if ( oType == OBJECT_REPAIR ) m_backDist = 35.0f; if ( oType == OBJECT_DESTROYER) m_backDist = 35.0f; if ( oType == OBJECT_TOWER ) m_backDist = 45.0f; if ( oType == OBJECT_NUCLEAR ) m_backDist = 70.0f; if ( oType == OBJECT_PARA ) m_backDist = 180.0f; if ( oType == OBJECT_SAFE ) m_backDist = 50.0f; if ( oType == OBJECT_HUSTON ) m_backDist = 120.0f; if ( oType == OBJECT_MOTHER ) m_backDist = 55.0f; m_backMin = m_backDist/3.0f; if ( oType == OBJECT_HUMAN ) m_backMin = 10.0f; if ( oType == OBJECT_TECH ) m_backMin = 10.0f; if ( oType == OBJECT_FACTORY ) m_backMin = 30.0f; if ( oType == OBJECT_RESEARCH ) m_backMin = 20.0f; if ( oType == OBJECT_NUCLEAR ) m_backMin = 32.0f; if ( oType == OBJECT_PARA ) m_backMin = 40.0f; if ( oType == OBJECT_SAFE ) m_backMin = 25.0f; if ( oType == OBJECT_HUSTON ) m_backMin = 80.0f; } //if ( type != CAM_TYPE_ONBOARD && m_cameraObj != 0 ) // m_cameraObj->SetGunGoalH(0.0f); // puts the cannon right if ( type == CAM_TYPE_ONBOARD ) m_focus = 1.50f; // Wide else m_focus = 1.00f; // normal m_engine->SetFocus(m_focus); m_type = type; SetSmooth(CAM_SMOOTH_NORM); } CameraType CCamera::GetType() { return m_type; } void CCamera::SetSmooth(CameraSmooth type) { m_smooth = type; } CameraSmooth CCamera::GetSmooth() { return m_smooth; } void CCamera::SetDist(float dist) { m_fixDist = dist; } float CCamera::GetDist() { return m_fixDist; } void CCamera::SetFixDirectionH(float angle) { m_fixDirectionH = angle; } float CCamera::GetFixDirectionH() { return m_fixDirectionH; } void CCamera::SetFixDirectionV(float angle) { m_fixDirectionV = angle; } float CCamera::GetFixDirectionV() { return m_fixDirectionV; } void CCamera::SetRemotePan(float value) { m_remotePan = value; } float CCamera::GetRemotePan() { return m_remotePan; } void CCamera::SetRemoteZoom(float value) { value = Math::Norm(value); if ( m_type == CAM_TYPE_BACK ) m_backDist = m_backMin + (200.0f - m_backMin) * value; if ( m_type == CAM_TYPE_FIX || m_type == CAM_TYPE_PLANE ) m_fixDist = 10.0f + (200.0f - 10.0f) * value; } float CCamera::GetRemoteZoom() { if ( m_type == CAM_TYPE_BACK ) return (m_backDist - m_backMin) / (200.0f - m_backMin); if ( m_type == CAM_TYPE_FIX || m_type == CAM_TYPE_PLANE ) return (m_fixDist - 10.0f) / (200.0f - 10.0f); return 0.0f; } void CCamera::StartVisit(Math::Vector goal, float dist) { m_visitType = m_type; SetType(CAM_TYPE_VISIT); m_visitGoal = goal; m_visitDist = dist; m_visitTime = 0.0f; m_visitDirectionV = -Math::PI*0.10f; } void CCamera::StopVisit() { SetType(m_visitType); // presents the initial type } void CCamera::GetCamera(Math::Vector &eye, Math::Vector &lookat) { eye = m_eyePt; lookat = Math::LookatPoint(m_eyePt, m_directionH, m_directionV, 50.0f); } bool CCamera::StartCentering(CObject *object, float angleH, float angleV, float dist, float time) { if (m_type != CAM_TYPE_BACK) return false; if (object != m_cameraObj) return false; if (m_centeringPhase != CAM_PHASE_NULL) return false; if (m_addDirectionH > Math::PI) angleH = Math::PI * 2.0f - angleH; m_centeringPhase = CAM_PHASE_START; m_centeringAngleH = angleH; m_centeringAngleV = angleV; m_centeringDist = dist; m_centeringCurrentH = 0.0f; m_centeringCurrentV = 0.0f; m_centeringTime = time; m_centeringProgress = 0.0f; return true; } bool CCamera::StopCentering(CObject *object, float time) { if (m_type != CAM_TYPE_BACK) return false; if (object != m_cameraObj) return false; if (m_centeringPhase != CAM_PHASE_START && m_centeringPhase != CAM_PHASE_WAIT) return false; m_centeringPhase = CAM_PHASE_STOP; if (m_centeringAngleH != 99.9f) m_centeringAngleH = m_centeringCurrentH; if (m_centeringAngleV != 99.9f) m_centeringAngleV = m_centeringCurrentV; m_centeringTime = time; m_centeringProgress = 0.0f; return true; } void CCamera::AbortCentering() { if (m_type == CAM_TYPE_INFO || m_type == CAM_TYPE_VISIT ) return; if (m_centeringPhase == CAM_PHASE_NULL) return; m_centeringPhase = CAM_PHASE_NULL; if ( m_centeringAngleH != 99.9f ) m_addDirectionH = m_centeringCurrentH; if (m_centeringAngleV != 99.9f) m_addDirectionV = m_centeringCurrentV; } void CCamera::FlushEffect() { m_effectType = CAM_EFFECT_NULL; m_effectForce = 0.0f; m_effectProgress = 0.0f; m_effectOffset = Math::Vector(0.0f, 0.0f, 0.0f); CApplication::GetInstancePointer()->StopForceFeedbackEffect(); } void CCamera::StartEffect(CameraEffect effect, Math::Vector pos, float force) { if ( !m_effect ) return; m_effectType = effect; m_effectPos = pos; m_effectForce = force; m_effectProgress = 0.0f; } void CCamera::EffectFrame(const Event &event) { if (m_type == CAM_TYPE_INFO || m_type == CAM_TYPE_VISIT) return; if (m_effectType == CAM_EFFECT_NULL) return; m_effectOffset = Math::Vector(0.0f, 0.0f, 0.0f); float force = m_effectForce; if ( m_effectType == CAM_EFFECT_TERRAFORM ) { m_effectProgress += event.rTime * 0.7f; m_effectOffset.x = (Math::Rand() - 0.5f) * 10.0f; m_effectOffset.y = (Math::Rand() - 0.5f) * 10.0f; m_effectOffset.z = (Math::Rand() - 0.5f) * 10.0f; force *= 1.0f-m_effectProgress; } if ( m_effectType == CAM_EFFECT_EXPLO ) { m_effectProgress += event.rTime * 1.0f; m_effectOffset.x = (Math::Rand() - 0.5f) *5.0f; m_effectOffset.y = (Math::Rand() - 0.5f) * 5.0f; m_effectOffset.z = (Math::Rand() - 0.5f) * 5.0f; force *= 1.0f-m_effectProgress; } if ( m_effectType == CAM_EFFECT_SHOT ) { m_effectProgress += event.rTime * 1.0f; m_effectOffset.x = (Math::Rand() - 0.5f) * 2.0f; m_effectOffset.y = (Math::Rand() - 0.5f) * 2.0f; m_effectOffset.z = (Math::Rand() - 0.5f) * 2.0f; force *= 1.0f-m_effectProgress; } if ( m_effectType == CAM_EFFECT_CRASH ) { m_effectProgress += event.rTime * 5.0f; m_effectOffset.y = sinf(m_effectProgress * Math::PI) * 1.5f; m_effectOffset.x = (Math::Rand() - 0.5f) * 1.0f * (1.0f - m_effectProgress); m_effectOffset.z = (Math::Rand() - 0.5f) * 1.0f * (1.0f - m_effectProgress); } if ( m_effectType == CAM_EFFECT_VIBRATION ) { m_effectProgress += event.rTime * 0.1f; m_effectOffset.y = (Math::Rand() - 0.5f) * 1.0f * (1.0f - m_effectProgress); m_effectOffset.x = (Math::Rand() - 0.5f) * 1.0f * (1.0f - m_effectProgress); m_effectOffset.z = (Math::Rand() - 0.5f) * 1.0f * (1.0f - m_effectProgress); } if ( m_effectType == CAM_EFFECT_PET ) { m_effectProgress += event.rTime *5.0f; m_effectOffset.x = (Math::Rand() - 0.5f) * 0.2f; m_effectOffset.y = (Math::Rand() - 0.5f) * 2.0f; m_effectOffset.z = (Math::Rand() - 0.5f) * 0.2f; } float dist = Math::Distance(m_eyePt, m_effectPos); dist = Math::Norm((dist - 100.f) / 100.0f); force *= 1.0f-dist; m_effectOffset *= force; float forceFeedback = force; if (m_effectType == CAM_EFFECT_VIBRATION) { forceFeedback *= 0.5f; } if (m_effectType == CAM_EFFECT_PET) { forceFeedback *= 0.75f; } if (m_effectType == CAM_EFFECT_EXPLO) { forceFeedback *= 3.0f; } if (forceFeedback > 1.0f) forceFeedback = 1.0f; if (forceFeedback >= 0.1f) { CApplication::GetInstancePointer()->PlayForceFeedbackEffect(forceFeedback); } if (m_effectProgress >= 1.0f) FlushEffect(); } void CCamera::FlushOver() { m_overType = CAM_OVER_EFFECT_NULL; m_overColorBase = Color(0.0f, 0.0f, 0.0f, 0.0f); // black m_engine->SetOverColor(); // nothing } void CCamera::SetOverBaseColor(Color color) { m_overColorBase = color; } void CCamera::StartOver(CameraOverEffect effect, Math::Vector pos, float force) { m_overType = effect; m_overTime = 0.0f; float decay; if (m_overType == CAM_OVER_EFFECT_LIGHTNING) decay = 400.0f; else decay = 100.0f; float dist = Math::Distance(m_eyePt, pos); dist = (dist - decay) / decay; if (dist < 0.0f) dist = 0.0f; if (dist > 1.0f) dist = 1.0f; m_overForce = force * (1.0f - dist); if (m_overType == CAM_OVER_EFFECT_BLOOD) { m_overColor = Color(0.8f, 0.1f, 0.1f); // red m_overMode = ENG_RSTATE_TCOLOR_BLACK; m_overFadeIn = 0.4f; m_overFadeOut = 0.8f; m_overForce = 1.0f; } if ( m_overType == CAM_OVER_EFFECT_FADEIN_WHITE ) { m_overColor = Color(1.0f, 1.0f, 1.0f); // white m_overMode = ENG_RSTATE_TCOLOR_BLACK; m_overFadeIn = 0.0f; m_overFadeOut = 20.0f; m_overForce = 1.0f; } if ( m_overType == CAM_OVER_EFFECT_FADEOUT_WHITE ) { m_overColor = Color(1.0f, 1.0f, 1.0f); // white m_overMode = ENG_RSTATE_TCOLOR_BLACK; m_overFadeIn = 6.0f; m_overFadeOut = 100000.0f; m_overForce = 1.0f; } if ( m_overType == CAM_OVER_EFFECT_FADEOUT_BLACK ) { m_overColor = m_engine->GetFogColor(1); // fog color underwater m_overMode = ENG_RSTATE_TTEXTURE_WHITE; m_overFadeIn = 4.0f; m_overFadeOut = 100000.0f; m_overForce = 1.0f; } if ( m_overType == CAM_OVER_EFFECT_LIGHTNING ) { m_overColor = Color(0.9f, 1.0f, 1.0f); // white-cyan m_overMode = ENG_RSTATE_TCOLOR_BLACK; m_overFadeIn = 0.0f; m_overFadeOut = 1.0f; } } void CCamera::OverFrame(const Event &event) { if (m_type == CAM_TYPE_INFO || m_type == CAM_TYPE_VISIT) return; if (m_overType == CAM_OVER_EFFECT_NULL) return; m_overTime += event.rTime; if (m_overType == CAM_OVER_EFFECT_LIGHTNING) { Color color; if (rand() % 2 == 0) { color.r = m_overColor.r * m_overForce; color.g = m_overColor.g * m_overForce; color.b = m_overColor.b * m_overForce; } else { color = Color(0.0f, 0.0f, 0.0f); } color.a = 0.0f; m_engine->SetOverColor(color, m_overMode); } else { if ( (m_overFadeIn > 0.0f) && (m_overTime < m_overFadeIn) ) { float intensity = m_overTime / m_overFadeIn; intensity *= m_overForce; Color color; if (m_overMode == ENG_RSTATE_TCOLOR_WHITE) { color.r = 1.0f - (1.0f - m_overColor.r) * intensity; color.g = 1.0f - (1.0f - m_overColor.g) * intensity; color.b = 1.0f - (1.0f - m_overColor.b) * intensity; } else { color.r = m_overColor.r * intensity; color.g = m_overColor.g * intensity; color.b = m_overColor.b * intensity; color.r = 1.0f - (1.0f - color.r) * (1.0f - m_overColorBase.r); color.g = 1.0f - (1.0f - color.g) * (1.0f - m_overColorBase.g); color.b = 1.0f - (1.0f - color.b) * (1.0f - m_overColorBase.b); } color.a = 0.0f; m_engine->SetOverColor(color, m_overMode); } else if ( (m_overFadeOut > 0.0f) && (m_overTime - m_overFadeIn < m_overFadeOut) ) { float intensity = 1.0f - (m_overTime - m_overFadeIn) / m_overFadeOut; intensity *= m_overForce; Color color; if (m_overMode == ENG_RSTATE_TCOLOR_WHITE) { color.r = 1.0f-(1.0f-m_overColor.r) * intensity; color.g = 1.0f-(1.0f-m_overColor.g) * intensity; color.b = 1.0f-(1.0f-m_overColor.b) * intensity; } else { color.r = m_overColor.r * intensity; color.g = m_overColor.g * intensity; color.b = m_overColor.b * intensity; color.r = 1.0f - (1.0f - color.r)*(1.0f - m_overColorBase.r); color.g = 1.0f - (1.0f - color.g)*(1.0f - m_overColorBase.g); color.b = 1.0f - (1.0f - color.b)*(1.0f - m_overColorBase.b); } color.a = 0.0f; m_engine->SetOverColor(color, m_overMode); } } if ( m_overTime >= m_overFadeIn+m_overFadeOut ) { FlushOver(); return; } } void CCamera::FixCamera() { m_initDelay = 0.0f; m_actualEye = m_finalEye = m_scriptEye; m_actualLookat = m_finalLookat = m_scriptLookat; SetViewTime(m_scriptEye, m_scriptLookat, 0.0f); } void CCamera::SetViewTime(const Math::Vector &eyePt, const Math::Vector &lookatPt, float rTime) { Math::Vector eye, lookat; if (m_type == CAM_TYPE_INFO) { eye = eyePt; lookat = lookatPt; } else { if (m_initDelay > 0.0f) { m_initDelay -= rTime; if (m_initDelay < 0.0f) m_initDelay = 0.0f; rTime /= 1.0f+m_initDelay; } eye = eyePt; lookat = lookatPt; if ( !IsCollision(eye, lookat) ) { m_finalEye = eye; m_finalLookat = lookat; } float prog = 0.0f; float dist = Math::Distance(m_finalEye, m_actualEye); if (m_smooth == CAM_SMOOTH_NONE) prog = dist; if (m_smooth == CAM_SMOOTH_NORM) prog = powf(dist, 1.5f) * rTime * 0.75f; if (m_smooth == CAM_SMOOTH_HARD) prog = dist * rTime * 4.0f; if (dist == 0.0f) { m_actualEye = m_finalEye; } else { if (prog > dist) prog = dist; m_actualEye = (m_finalEye - m_actualEye) / dist * prog + m_actualEye; } dist = Math::Distance(m_finalLookat, m_actualLookat); if ( m_smooth == CAM_SMOOTH_NONE ) prog = dist; if ( m_smooth == CAM_SMOOTH_NORM ) prog = powf(dist, 1.5f) * rTime * 3.0f; if ( m_smooth == CAM_SMOOTH_HARD ) prog = dist * rTime * 4.0f; if ( dist == 0.0f ) { m_actualLookat = m_finalLookat; } else { if (prog > dist) prog = dist; m_actualLookat = (m_finalLookat - m_actualLookat) / dist * prog + m_actualLookat; } eye = m_effectOffset+m_actualEye; m_water->AdjustEye(eye); float h = m_terrain->GetFloorLevel(eye); if (eye.y < h + 4.0f) eye.y = h + 4.0f; lookat = m_effectOffset+m_actualLookat; } Math::Vector upVec = Math::Vector(0.0f, 1.0f, 0.0f); SetViewParams(eye, lookat, upVec); } bool CCamera::IsCollision(Math::Vector &eye, Math::Vector lookat) { if (m_type == CAM_TYPE_BACK ) return IsCollisionBack(eye, lookat); if (m_type == CAM_TYPE_FIX ) return IsCollisionFix (eye, lookat); if (m_type == CAM_TYPE_PLANE) return IsCollisionFix (eye, lookat); return false; } bool CCamera::IsCollisionBack(Math::Vector &eye, Math::Vector lookat) { ObjectType iType; if (m_cameraObj == nullptr) iType = OBJECT_NULL; else iType = m_cameraObj->GetType(); Math::Vector min; min.x = Math::Min(m_actualEye.x, m_actualLookat.x); min.y = Math::Min(m_actualEye.y, m_actualLookat.y); min.z = Math::Min(m_actualEye.z, m_actualLookat.z); Math::Vector max; max.x = Math::Max(m_actualEye.x, m_actualLookat.x); max.y = Math::Max(m_actualEye.y, m_actualLookat.y); max.z = Math::Max(m_actualEye.z, m_actualLookat.z); m_transparency = false; for (CObject* obj : CObjectManager::GetInstancePointer()->GetAllObjects()) { if (IsObjectBeingTransported(obj)) continue; SetTransparency(obj, 0.0f); // opaque object if (obj == m_cameraObj) continue; if ( iType == OBJECT_BASE || // building? iType == OBJECT_DERRICK || iType == OBJECT_FACTORY || iType == OBJECT_STATION || iType == OBJECT_CONVERT || iType == OBJECT_REPAIR || iType == OBJECT_DESTROYER|| iType == OBJECT_TOWER || iType == OBJECT_RESEARCH || iType == OBJECT_RADAR || iType == OBJECT_ENERGY || iType == OBJECT_LABO || iType == OBJECT_NUCLEAR || iType == OBJECT_PARA || iType == OBJECT_SAFE || iType == OBJECT_HUSTON ) continue; ObjectType oType = obj->GetType(); if ( oType == OBJECT_HUMAN || oType == OBJECT_TECH || oType == OBJECT_TOTO || oType == OBJECT_ANT || oType == OBJECT_SPIDER || oType == OBJECT_BEE || oType == OBJECT_WORM ) continue; Math::Sphere objSphere = obj->GetCameraCollisionSphere(); Math::Vector oPos = objSphere.pos; float oRadius = objSphere.radius; if ( oRadius <= 2.0f ) continue; // ignores small objects if ( oPos.x+oRadius < min.x || oPos.y+oRadius < min.y || oPos.z+oRadius < min.z || oPos.x-oRadius > max.x || oPos.y-oRadius > max.y || oPos.z-oRadius > max.z ) continue; Math::Vector proj = Projection(m_actualEye, m_actualLookat, oPos); float dpp = Math::Distance(proj, oPos); if ( dpp > oRadius ) continue; if ( oType == OBJECT_FACTORY ) { float angle = Math::RotateAngle(m_actualEye.x-oPos.x, oPos.z-m_actualEye.z); // CW ! angle = Math::Direction(angle, obj->GetRotationY()); if ( fabs(angle) < 30.0f*Math::PI/180.0f ) continue; // in the gate? } float del = Math::Distance(m_actualEye, m_actualLookat); if (oType == OBJECT_FACTORY) del += oRadius; float len = Math::Distance(m_actualEye, proj); if (len > del) continue; SetTransparency(obj, 1.0f); // transparent object m_transparency = true; } return false; } bool CCamera::IsCollisionFix(Math::Vector &eye, Math::Vector lookat) { for (CObject* obj : CObjectManager::GetInstancePointer()->GetAllObjects()) { if (obj == m_cameraObj) continue; ObjectType type = obj->GetType(); if ( type == OBJECT_TOTO || type == OBJECT_STONE || type == OBJECT_URANIUM || type == OBJECT_METAL || type == OBJECT_POWER || type == OBJECT_ATOMIC || type == OBJECT_BULLET || type == OBJECT_BBOX || type == OBJECT_KEYa || type == OBJECT_KEYb || type == OBJECT_KEYc || type == OBJECT_KEYd || type == OBJECT_ANT || type == OBJECT_SPIDER || type == OBJECT_BEE || type == OBJECT_WORM ) continue; Math::Sphere objSphere = obj->GetCameraCollisionSphere(); Math::Vector objPos = objSphere.pos; float objRadius = objSphere.radius; if (objRadius == 0.0f) continue; float dist = Math::Distance(eye, objPos); if (dist < objRadius) { dist = Math::Distance(eye, lookat); Math::Vector proj = Projection(eye, lookat, objPos); eye = (lookat - eye) * objRadius / dist + proj; return false; } } return false; } bool CCamera::EventProcess(const Event &event) { switch (event.type) { case EVENT_FRAME: EventFrame(event); break; case EVENT_MOUSE_BUTTON_DOWN: case EVENT_MOUSE_BUTTON_UP: EventMouseButton(event); break; case EVENT_MOUSE_MOVE: EventMouseMove(event); break; case EVENT_MOUSE_WHEEL: EventMouseWheel(event); break; default: break; } return true; } bool CCamera::EventMouseMove(const Event &event) { if (m_engine->GetMouseType() == ENG_MOUSE_SCROLLR || m_engine->GetMouseType() == ENG_MOUSE_SCROLLL || m_engine->GetMouseType() == ENG_MOUSE_SCROLLU || m_engine->GetMouseType() == ENG_MOUSE_SCROLLD || m_engine->GetMouseType() == ENG_MOUSE_MOVE ) { m_engine->SetMouseType(ENG_MOUSE_NORM); } if ((event.mouseButtonsState & MOUSE_BUTTON_RIGHT) != 0 || (event.mouseButtonsState & MOUSE_BUTTON_MIDDLE) != 0) { Math::Point newDelta = event.mousePos - m_mousePos; if (m_cameraInvertX) newDelta.x = -newDelta.x; if (m_cameraInvertY) newDelta.y = -newDelta.y; m_mouseDelta += newDelta; m_engine->SetMouseType(ENG_MOUSE_MOVE); } m_mouseDeltaEdge.LoadZero(); if (m_oldCameraScroll) { if (event.mousePos.x < MOUSE_EDGE_MARGIN) m_mouseDeltaEdge.x = event.mousePos.x / MOUSE_EDGE_MARGIN - 1.0f; if (event.mousePos.x > 1.0f - MOUSE_EDGE_MARGIN) m_mouseDeltaEdge.x = 1.0f - (1.0f - event.mousePos.x) / MOUSE_EDGE_MARGIN; if (event.mousePos.y < MOUSE_EDGE_MARGIN) m_mouseDeltaEdge.y = event.mousePos.y / MOUSE_EDGE_MARGIN - 1.0f; if (event.mousePos.y > 1.0f - MOUSE_EDGE_MARGIN) m_mouseDeltaEdge.y = 1.0f - (1.0f - event.mousePos.y) / MOUSE_EDGE_MARGIN; if (m_type == CAM_TYPE_FREE || m_type == CAM_TYPE_EDIT || m_type == CAM_TYPE_BACK || m_type == CAM_TYPE_FIX || m_type == CAM_TYPE_PLANE || m_type == CAM_TYPE_EXPLO ) { if (m_mouseDeltaEdge.x > 0.0f) m_engine->SetMouseType(ENG_MOUSE_SCROLLR); if (m_mouseDeltaEdge.x < 0.0f) m_engine->SetMouseType(ENG_MOUSE_SCROLLL); } if (m_type == CAM_TYPE_FREE || m_type == CAM_TYPE_EDIT ) { if (m_mouseDeltaEdge.y > 0.0f) m_engine->SetMouseType(ENG_MOUSE_SCROLLU); if (m_mouseDeltaEdge.y < 0.0f) m_engine->SetMouseType(ENG_MOUSE_SCROLLD); } m_mouseDeltaEdge.x /= 2*Math::PI; m_mouseDeltaEdge.y /= Math::PI; } m_mousePos = event.mousePos; return true; } void CCamera::EventMouseWheel(const Event &event) { auto dir = event.GetData()->y; if (m_type == CAM_TYPE_BACK) { m_backDist -= 8.0f*dir; if (m_backDist < m_backMin) m_backDist = m_backMin; if (m_backDist > 200.0f) m_backDist = 200.0f; } if ( m_type == CAM_TYPE_FIX || m_type == CAM_TYPE_PLANE ) { m_fixDist -= 8.0f*dir; if (m_fixDist < 10.0f) m_fixDist = 10.0f; if (m_fixDist > 200.0f) m_fixDist = 200.0f; } if ( m_type == CAM_TYPE_VISIT ) { m_visitDist -= 8.0f*dir; if (m_visitDist < 20.0f) m_visitDist = 20.0f; if (m_visitDist > 200.0f) m_visitDist = 200.0f; } } void CCamera::EventMouseButton(const Event &event) { if (event.GetData()->button == MOUSE_BUTTON_RIGHT || event.GetData()->button == MOUSE_BUTTON_MIDDLE) { if ((event.mouseButtonsState & MOUSE_BUTTON_RIGHT) != 0 || (event.mouseButtonsState & MOUSE_BUTTON_MIDDLE) != 0) { m_engine->SetMouseType(ENG_MOUSE_MOVE); } else { m_engine->SetMouseType(ENG_MOUSE_NORM); } } } bool CCamera::EventFrame(const Event &event) { Math::Point newDelta = m_mouseDeltaEdge * m_speed * event.rTime; if (m_cameraInvertX) newDelta.x = -newDelta.x; if (m_cameraInvertY) newDelta.y = -newDelta.y; m_mouseDelta += newDelta; EffectFrame(event); OverFrame(event); if (m_type == CAM_TYPE_FREE) return EventFrameFree(event); if (m_type == CAM_TYPE_EDIT) return EventFrameEdit(event); if (m_type == CAM_TYPE_DIALOG) return EventFrameDialog(event); if (m_type == CAM_TYPE_BACK) return EventFrameBack(event); if (m_type == CAM_TYPE_FIX || m_type == CAM_TYPE_PLANE) return EventFrameFix(event); if (m_type == CAM_TYPE_EXPLO) return EventFrameExplo(event); if (m_type == CAM_TYPE_ONBOARD) return EventFrameOnBoard(event); if (m_type == CAM_TYPE_SCRIPT) return EventFrameScript(event); if (m_type == CAM_TYPE_INFO) return EventFrameInfo(event); if (m_type == CAM_TYPE_VISIT) return EventFrameVisit(event); return true; } bool CCamera::EventFrameFree(const Event &event) { Math::Vector cameraInput = event.cameraInput; if (m_cameraObj == nullptr) { cameraInput = Math::Clamp(cameraInput + event.motionInput, Math::Vector(-1.0f, -1.0f, -1.0f), Math::Vector(1.0f, 1.0f, 1.0f)); } float factor = m_heightEye * 0.5f + 30.0f; m_directionH -= m_mouseDelta.x * 2*Math::PI; m_eyePt = Math::LookatPoint(m_eyePt, m_directionH, m_directionV, m_mouseDelta.y * factor * m_speed); m_mouseDelta.LoadZero(); // Up/Down m_eyePt = Math::LookatPoint(m_eyePt, m_directionH, m_directionV, cameraInput.y * event.rTime * factor * m_speed); // Left/Right if ( event.kmodState & KEY_MOD(CTRL) ) { if ( cameraInput.x < 0.0f ) m_eyePt = Math::LookatPoint(m_eyePt, m_directionH + Math::PI / 2.0f, m_directionV, -cameraInput.x * event.rTime * factor * m_speed); if ( cameraInput.x > 0.0f ) m_eyePt = Math::LookatPoint(m_eyePt, m_directionH - Math::PI / 2.0f, m_directionV, cameraInput.x * event.rTime * factor * m_speed); } else { m_directionH -= cameraInput.x * event.rTime * 0.7f * m_speed; } // PageUp/PageDown if ( m_input->GetKeyState(INPUT_SLOT_AWAY) ) { if (m_heightEye < 500.0f) m_heightEye += event.rTime * factor * m_speed; } if ( m_input->GetKeyState(INPUT_SLOT_NEAR) ) { if (m_heightEye > -2.0f) m_heightEye -= event.rTime * factor * m_speed; } if ( m_input->GetKeyState(INPUT_SLOT_CAMERA_UP) ) { m_heightLookat += event.rTime * factor * m_speed; } if ( m_input->GetKeyState(INPUT_SLOT_CAMERA_DOWN) ) { m_heightLookat -= event.rTime * factor * m_speed; } m_terrain->AdjustToBounds(m_eyePt, 10.0f); if (m_terrain->AdjustToFloor(m_eyePt, true)) { m_eyePt.y += m_heightEye; Math::Vector pos = m_eyePt; if (m_terrain->AdjustToFloor(pos, true)) { pos.y -= 2.0f; if (m_eyePt.y < pos.y) m_eyePt.y = pos.y; } } Math::Vector lookatPt = Math::LookatPoint(m_eyePt, m_directionH, m_directionV, 50.0f); if (m_terrain->AdjustToFloor(lookatPt, true)) lookatPt.y += m_heightLookat; SetViewTime(m_eyePt, lookatPt, event.rTime); return true; } bool CCamera::EventFrameEdit(const Event &event) { float factor = m_editHeight * 0.5f + 30.0f; m_directionH -= m_mouseDelta.x * 0.7f * 2*Math::PI; m_eyePt = Math::LookatPoint(m_eyePt, m_directionH, m_directionV, m_mouseDelta.y * factor * m_speed); m_fixDirectionH += m_mouseDelta.x * 2*Math::PI; m_fixDirectionH = Math::NormAngle(m_fixDirectionH); m_mouseDelta.LoadZero(); m_terrain->AdjustToBounds(m_eyePt, 10.0f); if (m_terrain->AdjustToFloor(m_eyePt, false)) { m_eyePt.y += m_editHeight; Math::Vector pos = m_eyePt; if (m_terrain->AdjustToFloor(pos, false)) { pos.y += 2.0f; if (m_eyePt.y < pos.y) m_eyePt.y = pos.y; } } Math::Vector lookatPt = Math::LookatPoint( m_eyePt, m_directionH, m_directionV, 50.0f ); if ( m_terrain->AdjustToFloor(lookatPt, true)) lookatPt.y += m_heightLookat; SetViewTime(m_eyePt, lookatPt, event.rTime); return true; } bool CCamera::EventFrameDialog(const Event &event) { return true; } bool CCamera::EventFrameBack(const Event &event) { ObjectType type; if (m_cameraObj == nullptr) type = OBJECT_NULL; else type = m_cameraObj->GetType(); // +/-. if (m_input->GetKeyState(INPUT_SLOT_NEAR)) { m_backDist -= event.rTime * 30.0f * m_speed; if (m_backDist < m_backMin) m_backDist = m_backMin; } if (m_input->GetKeyState(INPUT_SLOT_AWAY)) { m_backDist += event.rTime * 30.0f * m_speed; if (m_backDist > 200.0f) m_backDist = 200.0f; } m_addDirectionH -= m_mouseDelta.x * 2*Math::PI; m_addDirectionH = Math::NormAngle(m_addDirectionH); if (m_mouseDelta.Length() > 0) AbortCentering(); // special stops framing m_mouseDelta.LoadZero(); // Increase the special framework float centeringH = 0.0f; float centeringV = 0.0f; float centeringD = 0.0f; if (m_centeringPhase == CAM_PHASE_START) { m_centeringProgress += event.rTime / m_centeringTime; if (m_centeringProgress > 1.0f) m_centeringProgress = 1.0f; centeringH = m_centeringProgress; centeringV = m_centeringProgress; centeringD = m_centeringProgress; if (m_centeringProgress >= 1.0f) m_centeringPhase = CAM_PHASE_WAIT; } if (m_centeringPhase == CAM_PHASE_WAIT) { centeringH = 1.0f; centeringV = 1.0f; centeringD = 1.0f; } if (m_centeringPhase == CAM_PHASE_STOP) { m_centeringProgress += event.rTime / m_centeringTime; if (m_centeringProgress > 1.0f) m_centeringProgress = 1.0f; centeringH = 1.0f-m_centeringProgress; centeringV = 1.0f-m_centeringProgress; centeringD = 1.0f-m_centeringProgress; if (m_centeringProgress >= 1.0f) m_centeringPhase = CAM_PHASE_NULL; } if (m_centeringAngleH == 99.9f) centeringH = 0.0f; if (m_centeringAngleV == 99.9f) centeringV = 0.0f; if (m_centeringDist == 0.0f) centeringD = 0.0f; if (m_cameraObj != nullptr) { Math::Vector lookatPt = m_cameraObj->GetPosition(); if (type == OBJECT_BASE ) lookatPt.y += 40.0f; else if (type == OBJECT_HUMAN) lookatPt.y += 1.0f; else if (type == OBJECT_TECH ) lookatPt.y += 1.0f; else lookatPt.y += 4.0f; float h = -m_cameraObj->GetRotationY(); // angle vehicle / building if ( type == OBJECT_DERRICK || type == OBJECT_FACTORY || type == OBJECT_REPAIR || type == OBJECT_DESTROYER|| type == OBJECT_STATION || type == OBJECT_CONVERT || type == OBJECT_TOWER || type == OBJECT_RESEARCH || type == OBJECT_RADAR || type == OBJECT_INFO || type == OBJECT_ENERGY || type == OBJECT_LABO || type == OBJECT_NUCLEAR || type == OBJECT_PARA || type == OBJECT_SAFE || type == OBJECT_HUSTON || type == OBJECT_START || type == OBJECT_END ) // building? { h += Math::PI * 0.20f; // nearly face } else // vehicle? { h += Math::PI; // back } h = Math::NormAngle(h)+m_remotePan; float v = 0.0f; //? h += m_centeringCurrentH; h += m_addDirectionH * (1.0f - centeringH); h = Math::NormAngle(h); if (type == OBJECT_MOBILEdr) // designer? v -= 0.3f; // Camera top v += m_centeringCurrentV; v += m_addDirectionV * (1.0f - centeringV); float d = m_backDist; d += m_centeringDist * centeringD; m_centeringCurrentH = m_centeringAngleH * centeringH; m_centeringCurrentV = m_centeringAngleV * centeringV; m_eyePt = RotateView(lookatPt, h, v, d); bool ground = true; if (m_cameraObj->Implements(ObjectInterfaceType::Movable)) ground = dynamic_cast(m_cameraObj)->GetPhysics()->GetLand(); if ( ground ) // ground? { Math::Vector pos = lookatPt + (lookatPt - m_eyePt); float floor = m_terrain->GetHeightToFloor(pos) - 4.0f; if (floor > 0.0f) m_eyePt.y += floor; // shows the descent in front } m_eyePt = ExcludeTerrain(m_eyePt, lookatPt, h, v); m_eyePt = ExcludeObject(m_eyePt, lookatPt, h, v); SetViewTime(m_eyePt, lookatPt, event.rTime); m_directionH = h + Math::PI / 2.0f; m_directionV = v; } return true; } bool CCamera::EventFrameFix(const Event &event) { // +/-. if (m_input->GetKeyState(INPUT_SLOT_NEAR)) { m_fixDist -= event.rTime * 30.0f * m_speed; if (m_fixDist < 10.0f) m_fixDist = 10.0f; } if (m_input->GetKeyState(INPUT_SLOT_AWAY)) { m_fixDist += event.rTime * 30.0f * m_speed; if (m_fixDist > 200.0f) m_fixDist = 200.0f; } m_fixDirectionH -= m_mouseDelta.x * 2*Math::PI; if (m_mouseDelta.Length() > 0) AbortCentering(); // special stops framing m_mouseDelta.LoadZero(); // Left/Right m_fixDirectionH += event.cameraInput.x * event.rTime * 0.7f * m_speed; m_fixDirectionH = Math::NormAngle(m_fixDirectionH); // Up/Down m_fixDirectionV -= event.cameraInput.y * event.rTime * 0.7f * m_speed; m_fixDirectionV = Math::Min(Math::Max(m_fixDirectionV, -0.5*Math::PI), 0.25*Math::PI); if (m_cameraObj != nullptr) { Math::Vector lookatPt = m_cameraObj->GetPosition(); float h = m_fixDirectionH + m_remotePan; float v = m_fixDirectionV; float d = m_fixDist; m_eyePt = RotateView(lookatPt, h, v, d); if (m_type == CAM_TYPE_PLANE) m_eyePt.y += m_fixDist / 2.0f; m_eyePt = ExcludeTerrain(m_eyePt, lookatPt, h, v); m_eyePt = ExcludeObject(m_eyePt, lookatPt, h, v); SetViewTime(m_eyePt, lookatPt, event.rTime); m_directionH = h + Math::PI / 2.0f; m_directionV = v; } return true; } bool CCamera::EventFrameExplo(const Event &event) { m_directionH -= m_mouseDelta.x * 2*Math::PI; m_mouseDelta.LoadZero(); m_terrain->AdjustToBounds(m_eyePt, 10.0f); if ( m_terrain->AdjustToFloor(m_eyePt, false) ) { m_eyePt.y += m_heightEye; Math::Vector pos = m_eyePt; if ( m_terrain->AdjustToFloor(pos, false) ) { pos.y += 2.0f; if ( m_eyePt.y < pos.y ) m_eyePt.y = pos.y; } } Math::Vector lookatPt = Math::LookatPoint(m_eyePt, m_directionH, m_directionV, 50.0f); if (m_terrain->AdjustToFloor(lookatPt, true)) lookatPt.y += m_heightLookat; SetViewTime(m_eyePt, lookatPt, event.rTime); return true; } bool CCamera::EventFrameOnBoard(const Event &event) { if (m_cameraObj != nullptr) { assert(m_cameraObj->Implements(ObjectInterfaceType::Controllable)); Math::Vector lookatPt, upVec; dynamic_cast(m_cameraObj)->AdjustCamera(m_eyePt, m_directionH, m_directionV, lookatPt, upVec, m_type); Math::Vector eye = m_effectOffset * 0.3f + m_eyePt; Math::Vector lookat = m_effectOffset * 0.3f + lookatPt; SetViewParams(eye, lookat, upVec); m_actualEye = eye; m_actualLookat = lookat; } return true; } bool CCamera::EventFrameInfo(const Event &event) { SetViewTime(Math::Vector(0.0f, 0.0f, 0.0f), Math::Vector(0.0f, 0.0f, 1.0f), event.rTime); return true; } bool CCamera::EventFrameVisit(const Event &event) { m_visitTime += event.rTime; // +/-. if (m_input->GetKeyState(INPUT_SLOT_NEAR)) { m_visitDist -= event.rTime * 50.0f * m_speed; if (m_visitDist < 20.0f) m_visitDist = 20.0f; } if (m_input->GetKeyState(INPUT_SLOT_AWAY)) { m_visitDist += event.rTime * 50.0f * m_speed; if (m_visitDist > 200.0f) m_visitDist = 200.0f; } // PageUp/Down. if (m_input->GetKeyState(INPUT_SLOT_CAMERA_UP)) { m_visitDirectionV -= event.rTime * 1.0f * m_speed; if (m_visitDirectionV < -Math::PI * 0.40f) m_visitDirectionV = -Math::PI * 0.40f; } if (m_input->GetKeyState(INPUT_SLOT_CAMERA_DOWN)) { m_visitDirectionV += event.rTime * 1.0f * m_speed; if (m_visitDirectionV > 0.0f ) m_visitDirectionV = 0.0f; } m_visitDist -= m_mouseDelta.y * 100.0f * m_speed; m_mouseDelta.LoadZero(); if (m_visitDist < 20.0f) m_visitDist = 20.0f; if (m_visitDist > 200.0f) m_visitDist = 200.0f; float angleH = (m_visitTime / 10.0f) * (Math::PI * 2.0f); float angleV = m_visitDirectionV; Math::Vector eye = RotateView(m_visitGoal, angleH, angleV, m_visitDist); eye = ExcludeTerrain(eye, m_visitGoal, angleH, angleV); eye = ExcludeObject(eye, m_visitGoal, angleH, angleV); SetViewTime(eye, m_visitGoal, event.rTime); return true; } bool CCamera::EventFrameScript(const Event &event) { SetViewTime(m_scriptEye + m_effectOffset, m_scriptLookat + m_effectOffset, event.rTime); return true; } void CCamera::SetScriptEye(Math::Vector eye) { m_scriptEye = eye; } void CCamera::SetScriptLookat(Math::Vector lookat) { m_scriptLookat = lookat; } void CCamera::SetViewParams(const Math::Vector &eye, const Math::Vector &lookat, const Math::Vector &up) { m_engine->SetViewParams(eye, lookat, up, m_eyeDistance); bool under = (eye.y < m_water->GetLevel()); // Is it underwater? if (m_type == CAM_TYPE_INFO) under = false; m_engine->SetRankView(under ? 1 : 0); } Math::Vector CCamera::ExcludeTerrain(Math::Vector eye, Math::Vector lookat, float &angleH, float &angleV) { Math::Vector pos = eye; if (m_terrain->AdjustToFloor(pos)) { float dist = Math::DistanceProjected(lookat, pos); pos.y += 2.0f+dist*0.1f; if ( pos.y > eye.y ) { angleV = -Math::RotateAngle(dist, pos.y-lookat.y); eye = RotateView(lookat, angleH, angleV, dist); } } return eye; } Math::Vector CCamera::ExcludeObject(Math::Vector eye, Math::Vector lookat, float &angleH, float &angleV) { return eye; // TODO: check the commented out code: /* for (int i = 0; i < 1000000; i++) { CObject* obj = static_cast( iMan->SearchInstance(CLASS_OBJECT, i) ); if (obj == nullptr) break; int j = 0; Math::Vector oPos; float oRad; while (obj->GetCrashSphere(j++, oPos, oRad)) { float dist = Math::Distance(oPos, eye); if (dist < oRad + 2.0f) eye.y = oPos.y + oRad + 2.0f; } } return eye;*/ } void CCamera::SetCameraSpeed(float speed) { m_speed = speed; } }