1
0
forked from 0ad/0ad
0ad/source/graphics/GameView.cpp
Ykkrosh 310f3466a8 # Hotloading of actor XML files.
Stop ignoring actor XML animation speeds.
Add decentralised registration of hotloaders.
Move player ID storage into CModel, to simplify CUnit.
Remove obsolete unit ID allocation code.
Remove some material junk.

This was SVN commit r7605.
2010-06-03 01:29:43 +00:00

854 lines
24 KiB
C++

/* Copyright (C) 2010 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. 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 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. 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 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#include "precompiled.h"
#include "GameView.h"
#include "graphics/Camera.h"
#include "graphics/CinemaTrack.h"
#include "graphics/ColladaManager.h"
#include "graphics/HFTracer.h"
#include "graphics/LightEnv.h"
#include "graphics/Model.h"
#include "graphics/ObjectManager.h"
#include "graphics/Patch.h"
#include "graphics/SkeletonAnimManager.h"
#include "graphics/Terrain.h"
#include "graphics/TextureManager.h"
#include "graphics/Unit.h"
#include "graphics/UnitManager.h"
#include "lib/input.h"
#include "lib/timer.h"
#include "maths/Bound.h"
#include "maths/MathUtil.h"
#include "maths/Matrix3D.h"
#include "maths/Quaternion.h"
#include "ps/ConfigDB.h"
#include "ps/Game.h"
#include "ps/Globals.h"
#include "ps/Hotkey.h"
#include "ps/Loader.h"
#include "ps/LoaderThunks.h"
#include "ps/Profile.h"
#include "ps/Pyrogenesis.h"
#include "ps/World.h"
#include "renderer/Renderer.h"
#include "renderer/SkyManager.h"
#include "renderer/WaterManager.h"
#include "scripting/ScriptableObject.h"
#include "simulation/LOSManager.h"
#include "simulation2/Simulation2.h"
float g_MaxZoomHeight=350.0f; //note: Max terrain height is this minus YMinOffset
float g_YMinOffset=15.0f;
extern int g_xres, g_yres;
static CVector3D cameraBookmarks[10];
static bool bookmarkInUse[10] = { false, false, false, false, false, false, false, false, false, false };
static i8 currentBookmark = -1;
const float CGameView::defaultFOV = DEGTORAD(20.f);
const float CGameView::defaultNear = 4.f;
const float CGameView::defaultFar = 4096.f;
const float CGameView::defaultCullFOV = CGameView::defaultFOV + DEGTORAD(6.0f); //add 6 degrees to the default FOV for use with the culling frustum
class CGameViewImpl : public CJSObject<CGameViewImpl>
{
NONCOPYABLE(CGameViewImpl);
public:
CGameViewImpl(CGame* game)
: Game(game),
ColladaManager(), MeshManager(ColladaManager), SkeletonAnimManager(ColladaManager),
ObjectManager(MeshManager, SkeletonAnimManager, *game->GetSimulation2()),
ViewCamera(),
CullCamera(),
LockCullCamera(false),
Culling(true),
ViewScrollSpeed(60),
ViewRotateSensitivity(0.002f),
ViewRotateSensitivityKeyboard(1.0f),
ViewRotateAboutTargetSensitivity(0.010f),
ViewRotateAboutTargetSensitivityKeyboard(2.0f),
ViewDragSensitivity(0.5f),
ViewZoomSensitivityWheel(16.0f),
ViewZoomSensitivity(256.0f),
ViewZoomSmoothness(0.02f),
ViewSnapSmoothness(0.02f),
CameraDelta(),
CameraPivot(),
ZoomDelta(0)
{
}
CGame* Game;
CColladaManager ColladaManager;
CMeshManager MeshManager;
CSkeletonAnimManager SkeletonAnimManager;
CObjectManager ObjectManager;
/**
* this camera controls the eye position when rendering
*/
CCamera ViewCamera;
/**
* this camera controls the frustum that is used for culling
* and shadow calculations
*
* Note that all code that works with camera movements should only change
* m_ViewCamera. The render functions automatically sync the cull camera to
* the view camera depending on the value of m_LockCullCamera.
*/
CCamera CullCamera;
/**
* When @c true, the cull camera is locked in place.
* When @c false, the cull camera follows the view camera.
*
* Exposed to JS as gameView.lockCullCamera
*/
bool LockCullCamera;
/**
* When @c true, culling is enabled so that only models that have a chance of
* being visible are sent to the renderer.
* Otherwise, the entire world is sent to the renderer.
*
* Exposed to JS as gameView.culling
*/
bool Culling;
/**
* Cache global lighting environment. This is used to check whether the
* environment has changed during the last frame, so that vertex data can be updated etc.
*/
CLightEnv CachedLightEnv;
CCinemaManager TrackManager;
////////////////////////////////////////
// Settings
float ViewScrollSpeed;
float ViewRotateSensitivity;
float ViewRotateSensitivityKeyboard;
float ViewRotateAboutTargetSensitivity;
float ViewRotateAboutTargetSensitivityKeyboard;
float ViewDragSensitivity;
float ViewZoomSensitivityWheel;
float ViewZoomSensitivity;
float ViewZoomSmoothness; // 0.0 = instantaneous zooming, 1.0 = so slow it never moves
float ViewSnapSmoothness; // Just the same.
////////////////////////////////////////
// Camera Controls State
CVector3D CameraDelta;
CVector3D CameraPivot;
// CEntity* UnitAttach;
//float m_CameraZoom;
std::vector<CVector3D> CameraTargets;
// Accumulate zooming changes across frames for smoothness
float ZoomDelta;
static void ScriptingInit();
};
CGameView::CGameView(CGame *pGame):
m(new CGameViewImpl(pGame))
{
SViewPort vp;
vp.m_X=0;
vp.m_Y=0;
vp.m_Width=g_xres;
vp.m_Height=g_yres;
m->ViewCamera.SetViewPort(&vp);
m->ViewCamera.SetProjection (defaultNear, defaultFar, defaultFOV);
m->ViewCamera.m_Orientation.SetXRotation(DEGTORAD(30));
m->ViewCamera.m_Orientation.RotateY(DEGTORAD(0));
m->ViewCamera.m_Orientation.Translate (100, 150, -100);
m->CullCamera = m->ViewCamera;
g_Renderer.SetSceneCamera(m->ViewCamera, m->CullCamera);
}
CGameView::~CGameView()
{
UnloadResources();
delete m;
}
CObjectManager& CGameView::GetObjectManager() const
{
return m->ObjectManager;
}
JSObject* CGameView::GetScript()
{
return m->GetScript();
}
/*static*/ void CGameView::ScriptingInit()
{
return CGameViewImpl::ScriptingInit();
}
CCamera* CGameView::GetCamera()
{
return &m->ViewCamera;
}
CCinemaManager* CGameView::GetCinema()
{
return &m->TrackManager;
};
/*
void CGameView::AttachToUnit(CEntity* target)
{
m->UnitAttach = target;
}
*/
void CGameViewImpl::ScriptingInit()
{
AddProperty(L"culling", &CGameViewImpl::Culling);
AddProperty(L"lockCullCamera", &CGameViewImpl::LockCullCamera);
CJSObject<CGameViewImpl>::ScriptingInit("GameView");
}
int CGameView::Initialize(CGameAttributes* UNUSED(pAttribs))
{
CFG_GET_SYS_VAL( "view.scroll.speed", Float, m->ViewScrollSpeed );
CFG_GET_SYS_VAL( "view.rotate.speed", Float, m->ViewRotateSensitivity );
CFG_GET_SYS_VAL( "view.rotate.keyboard.speed", Float, m->ViewRotateSensitivityKeyboard );
CFG_GET_SYS_VAL( "view.rotate.abouttarget.speed", Float, m->ViewRotateAboutTargetSensitivity );
CFG_GET_SYS_VAL( "view.rotate.keyboard.abouttarget.speed", Float, m->ViewRotateAboutTargetSensitivityKeyboard );
CFG_GET_SYS_VAL( "view.drag.speed", Float, m->ViewDragSensitivity );
CFG_GET_SYS_VAL( "view.zoom.speed", Float, m->ViewZoomSensitivity );
CFG_GET_SYS_VAL( "view.zoom.wheel.speed", Float, m->ViewZoomSensitivityWheel );
CFG_GET_SYS_VAL( "view.zoom.smoothness", Float, m->ViewZoomSmoothness );
CFG_GET_SYS_VAL( "view.snap.smoothness", Float, m->ViewSnapSmoothness );
if( ( m->ViewZoomSmoothness < 0.0f ) || ( m->ViewZoomSmoothness > 1.0f ) )
m->ViewZoomSmoothness = 0.02f;
if( ( m->ViewSnapSmoothness < 0.0f ) || ( m->ViewSnapSmoothness > 1.0f ) )
m->ViewSnapSmoothness = 0.02f;
return 0;
}
void CGameView::RegisterInit(CGameAttributes *pAttribs)
{
// CGameView init
RegMemFun1(this, &CGameView::Initialize, pAttribs, L"CGameView init", 1);
// previously done by CGameView::InitResources
RegMemFun(g_TexMan.GetSingletonPtr(), &CTextureManager::LoadTerrainTextures, L"LoadTerrainTextures", 60);
RegMemFun(g_Renderer.GetSingletonPtr(), &CRenderer::LoadAlphaMaps, L"LoadAlphaMaps", 5);
RegMemFun(g_Renderer.GetSingletonPtr()->GetWaterManager(), &WaterManager::LoadWaterTextures, L"LoadWaterTextures", 80);
RegMemFun(g_Renderer.GetSingletonPtr()->GetSkyManager(), &SkyManager::LoadSkyTextures, L"LoadSkyTextures", 15);
}
void CGameView::Render()
{
if (m->LockCullCamera == false)
{
// Set up cull camera
m->CullCamera = m->ViewCamera;
// One way to fix shadows popping in at the edge of the screen is to widen the culling frustum so that
// objects aren't culled as early. The downside is that objects will get rendered even though they appear
// off screen, which is somewhat inefficient. A better solution would be to decouple shadow map rendering
// from model rendering; as it is now, a shadow map is only rendered if its associated model is to be
// rendered.
// (See http://trac.wildfiregames.com/ticket/504)
m->CullCamera.SetProjection(defaultNear, defaultFar, defaultCullFOV);
m->CullCamera.UpdateFrustum();
}
g_Renderer.SetSceneCamera(m->ViewCamera, m->CullCamera);
CheckLightEnv();
g_Renderer.RenderScene(this);
}
///////////////////////////////////////////////////////////
// This callback is part of the Scene interface
// Submit all objects visible in the given frustum
void CGameView::EnumerateObjects(const CFrustum& frustum, SceneCollector* c)
{
PROFILE_START( "submit terrain" );
CTerrain* pTerrain = m->Game->GetWorld()->GetTerrain();
const ssize_t patchesPerSide = pTerrain->GetPatchesPerSide();
// find out which patches will be drawn
for (ssize_t j=0; j<patchesPerSide; j++) {
for (ssize_t i=0; i<patchesPerSide; i++) {
CPatch* patch=pTerrain->GetPatch(i,j); // can't fail
// If the patch is underwater, calculate a bounding box that also contains the water plane
CBound bounds = patch->GetBounds();
float waterHeight = g_Renderer.GetWaterManager()->m_WaterHeight + 0.001f;
if(bounds[1].Y < waterHeight) {
bounds[1].Y = waterHeight;
}
if (!m->Culling || frustum.IsBoxVisible (CVector3D(0,0,0), bounds)) {
//c->Submit(patch);
// set the renderstate for this patch
patch->setDrawState(true);
// set the renderstate for the neighbors
CPatch *nPatch;
nPatch = pTerrain->GetPatch(i-1,j-1);
if(nPatch) nPatch->setDrawState(true);
nPatch = pTerrain->GetPatch(i,j-1);
if(nPatch) nPatch->setDrawState(true);
nPatch = pTerrain->GetPatch(i+1,j-1);
if(nPatch) nPatch->setDrawState(true);
nPatch = pTerrain->GetPatch(i-1,j);
if(nPatch) nPatch->setDrawState(true);
nPatch = pTerrain->GetPatch(i+1,j);
if(nPatch) nPatch->setDrawState(true);
nPatch = pTerrain->GetPatch(i-1,j+1);
if(nPatch) nPatch->setDrawState(true);
nPatch = pTerrain->GetPatch(i,j+1);
if(nPatch) nPatch->setDrawState(true);
nPatch = pTerrain->GetPatch(i+1,j+1);
if(nPatch) nPatch->setDrawState(true);
}
}
}
// draw the patches
for (ssize_t j=0; j<patchesPerSide; j++)
{
for (ssize_t i=0; i<patchesPerSide; i++)
{
CPatch* patch=pTerrain->GetPatch(i,j); // can't fail
if(patch->getDrawState() == true)
{
c->Submit(patch);
patch->setDrawState(false);
}
}
}
PROFILE_END( "submit terrain" );
PROFILE_START( "submit sim components" );
m->Game->GetSimulation2()->RenderSubmit(*c, frustum, m->Culling);
PROFILE_END( "submit sim components" );
}
//locks the camera in place
void CGameView::CameraLock(const CVector3D& Trans, bool smooth)
{
CameraLock(Trans.X, Trans.Y, Trans.Z, smooth);
}
void CGameView::CameraLock(float x, float y, float z, bool smooth)
{
CTerrain* pTerrain = m->Game->GetWorld()->GetTerrain();
float height = pTerrain->GetExactGroundLevel(
m->ViewCamera.m_Orientation._14 + x, m->ViewCamera.m_Orientation._34 + z) +
g_YMinOffset;
//is requested position within limits?
if (m->ViewCamera.m_Orientation._24 + y <= g_MaxZoomHeight)
{
if( m->ViewCamera.m_Orientation._24 + y >= height)
{
m->ViewCamera.m_Orientation.Translate(x, y, z);
}
else if (m->ViewCamera.m_Orientation._24 + y < height && smooth == true)
{
m->ViewCamera.m_Orientation.Translate(x, y, z);
m->ViewCamera.m_Orientation._24=height;
}
}
}
static void MarkUpdateColorRecursive(CModel& model)
{
model.SetDirty(RENDERDATA_UPDATE_COLOR);
const std::vector<CModel::Prop>& props = model.GetProps();
for(size_t i = 0; i < props.size(); ++i) {
debug_assert(props[i].m_Model);
MarkUpdateColorRecursive(*props[i].m_Model);
}
}
void CGameView::CheckLightEnv()
{
if (m->CachedLightEnv == g_LightEnv)
return;
m->CachedLightEnv = g_LightEnv;
CTerrain* pTerrain = m->Game->GetWorld()->GetTerrain();
if (!pTerrain)
return;
PROFILE("update light env");
pTerrain->MakeDirty(RENDERDATA_UPDATE_COLOR);
const std::vector<CUnit*>& units = m->Game->GetWorld()->GetUnitManager().GetUnits();
for(size_t i = 0; i < units.size(); ++i) {
MarkUpdateColorRecursive(units[i]->GetModel());
}
}
void CGameView::UnloadResources()
{
g_TexMan.UnloadTerrainTextures();
g_Renderer.UnloadAlphaMaps();
g_Renderer.GetWaterManager()->UnloadWaterTextures();
}
void CGameView::ResetCamera()
{
// quick hack to return camera home, for screenshots (after alt+tabbing)
m->ViewCamera.SetProjection (defaultNear, defaultFar, defaultFOV);
m->ViewCamera.m_Orientation.SetXRotation(DEGTORAD(30));
m->ViewCamera.m_Orientation.RotateY(DEGTORAD(-45));
m->ViewCamera.m_Orientation.Translate (100, 150, -100);
}
void CGameView::ResetCameraOrientation()
{
CVector3D origin = m->ViewCamera.m_Orientation.GetTranslation();
CVector3D dir = m->ViewCamera.m_Orientation.GetIn();
CVector3D target = origin + dir * ( ( 50.0f - origin.Y ) / dir.Y );
target -= CVector3D( -22.474480f, 50.0f, 22.474480f );
m->ViewCamera.SetProjection (defaultNear, defaultFar, defaultFOV);
m->ViewCamera.m_Orientation.SetXRotation(DEGTORAD(30));
m->ViewCamera.m_Orientation.RotateY(DEGTORAD(-45));
target += CVector3D( 100.0f, 150.0f, -100.0f );
m->ViewCamera.m_Orientation.Translate( target );
}
void CGameView::RotateAboutTarget()
{
m->CameraPivot = m->ViewCamera.GetWorldCoordinates(true);
}
void CGameView::Update(float DeltaTime)
{
if (!g_app_has_focus)
return;
// TODO: this is probably not an ideal place for this, it should probably go
// in a CCmpWaterManager or some such thing (once such a thing exists)
g_Renderer.GetWaterManager()->m_WaterTexTimer += DeltaTime;
/*
if (m->UnitAttach)
{
CVector3D ToMove = m->UnitAttach->m_position - m->ViewCamera.GetFocus();
m->ViewCamera.m_Orientation._14 += ToMove.X;
m->ViewCamera.m_Orientation._34 += ToMove.Z;
m->ViewCamera.UpdateFrustum();
return;
}
*/
if (m->TrackManager.IsActive() && m->TrackManager.IsPlaying())
{
if (! m->TrackManager.Update(DeltaTime))
ResetCamera();
return;
}
float delta = powf( m->ViewSnapSmoothness, DeltaTime );
m->ViewCamera.m_Orientation.Translate( m->CameraDelta * ( 1.0f - delta ) );
m->CameraDelta *= delta;
// This could be rewritten much more reliably, so it doesn't e.g. accidentally tilt
// the camera, assuming we know exactly what limits the camera should have.
// Calculate mouse movement
static int mouse_last_x = 0;
static int mouse_last_y = 0;
int mouse_dx = g_mouse_x - mouse_last_x;
int mouse_dy = g_mouse_y - mouse_last_y;
mouse_last_x = g_mouse_x;
mouse_last_y = g_mouse_y;
// Miscellaneous vectors
CVector3D forwards = m->ViewCamera.m_Orientation.GetIn();
CVector3D rightwards = m->ViewCamera.m_Orientation.GetLeft() * -1.0f; // upwards.Cross(forwards);
CVector3D upwards( 0.0f, 1.0f, 0.0f );
// rightwards.Normalize();
CVector3D forwards_horizontal = forwards;
forwards_horizontal.Y = 0.0f;
forwards_horizontal.Normalize();
if( hotkeys[HOTKEY_CAMERA_ROTATE] || hotkeys[HOTKEY_CAMERA_ROTATE_KEYBOARD] )
{
// Ctrl + middle-drag or left-and-right-drag to rotate view
// Untranslate the camera, so it rotates around the correct point
CVector3D position = m->ViewCamera.m_Orientation.GetTranslation();
m->ViewCamera.m_Orientation.Translate(position*-1);
// Sideways rotation
float rightways = 0.0f;
if( hotkeys[HOTKEY_CAMERA_ROTATE] )
rightways = (float)mouse_dx * m->ViewRotateSensitivity;
if( hotkeys[HOTKEY_CAMERA_ROTATE_KEYBOARD] )
{
if( hotkeys[HOTKEY_CAMERA_LEFT] )
rightways -= m->ViewRotateSensitivityKeyboard * DeltaTime;
if( hotkeys[HOTKEY_CAMERA_RIGHT] )
rightways += m->ViewRotateSensitivityKeyboard * DeltaTime;
}
m->ViewCamera.m_Orientation.RotateY( rightways );
// Up/down rotation
float upways = 0.0f;
if( hotkeys[HOTKEY_CAMERA_ROTATE] )
upways = (float)mouse_dy * m->ViewRotateSensitivity;
if( hotkeys[HOTKEY_CAMERA_ROTATE_KEYBOARD] )
{
if( hotkeys[HOTKEY_CAMERA_UP] )
upways -= m->ViewRotateSensitivityKeyboard * DeltaTime;
if( hotkeys[HOTKEY_CAMERA_DOWN] )
upways += m->ViewRotateSensitivityKeyboard * DeltaTime;
}
CQuaternion temp;
temp.FromAxisAngle(rightwards, upways);
m->ViewCamera.m_Orientation.Rotate(temp);
// Retranslate back to the right position
m->ViewCamera.m_Orientation.Translate(position);
}
else if( hotkeys[HOTKEY_CAMERA_ROTATE_ABOUT_TARGET] )
{
CVector3D origin = m->ViewCamera.m_Orientation.GetTranslation();
CVector3D delta = origin - m->CameraPivot;
CQuaternion rotateH, rotateV; CMatrix3D rotateM;
// Sideways rotation
float rightways = (float)mouse_dx * m->ViewRotateAboutTargetSensitivity;
rotateH.FromAxisAngle( upwards, rightways );
// Up/down rotation
float upways = (float)mouse_dy * m->ViewRotateAboutTargetSensitivity;
rotateV.FromAxisAngle( rightwards, upways );
rotateH *= rotateV;
rotateH.ToMatrix( rotateM );
delta = rotateM.Rotate( delta );
// Lock the inclination to a rather arbitrary values (for the sake of graphical decency)
float scan = sqrt( delta.X * delta.X + delta.Z * delta.Z ) / delta.Y;
if( ( scan >= 0.5f ) )
{
// Move the camera to the origin (in preparation for rotation )
m->ViewCamera.m_Orientation.Translate( origin * -1.0f );
m->ViewCamera.m_Orientation.Rotate( rotateH );
// Move the camera back to where it belongs
m->ViewCamera.m_Orientation.Translate( m->CameraPivot + delta );
}
}
else if( hotkeys[HOTKEY_CAMERA_ROTATE_ABOUT_TARGET_KEYBOARD] )
{
// Split up because the keyboard controls use the centre of the screen, not the mouse position.
CVector3D origin = m->ViewCamera.m_Orientation.GetTranslation();
CVector3D pivot = m->ViewCamera.GetFocus();
CVector3D delta = origin - pivot;
CQuaternion rotateH, rotateV; CMatrix3D rotateM;
// Sideways rotation
float rightways = 0.0f;
if( hotkeys[HOTKEY_CAMERA_LEFT] )
rightways -= m->ViewRotateAboutTargetSensitivityKeyboard * DeltaTime;
if( hotkeys[HOTKEY_CAMERA_RIGHT] )
rightways += m->ViewRotateAboutTargetSensitivityKeyboard * DeltaTime;
rotateH.FromAxisAngle( upwards, rightways );
// Up/down rotation
float upways = 0.0f;
if( hotkeys[HOTKEY_CAMERA_UP] )
upways -= m->ViewRotateAboutTargetSensitivityKeyboard * DeltaTime;
if( hotkeys[HOTKEY_CAMERA_DOWN] )
upways += m->ViewRotateAboutTargetSensitivityKeyboard * DeltaTime;
rotateV.FromAxisAngle( rightwards, upways );
rotateH *= rotateV;
rotateH.ToMatrix( rotateM );
delta = rotateM.Rotate( delta );
// Lock the inclination to a rather arbitrary values (for the sake of graphical decency)
float scan = sqrt( delta.X * delta.X + delta.Z * delta.Z ) / delta.Y;
if( ( scan >= 0.5f ) )
{
// Move the camera to the origin (in preparation for rotation )
m->ViewCamera.m_Orientation.Translate( origin * -1.0f );
m->ViewCamera.m_Orientation.Rotate( rotateH );
// Move the camera back to where it belongs
m->ViewCamera.m_Orientation.Translate( pivot + delta );
}
}
else if( hotkeys[HOTKEY_CAMERA_PAN] )
{
// Middle-drag to pan
//keep camera in bounds
CameraLock(rightwards * (m->ViewDragSensitivity * mouse_dx));
CameraLock(forwards_horizontal * (-m->ViewDragSensitivity * mouse_dy));
}
// Mouse movement
if( g_mouse_active && !hotkeys[HOTKEY_CAMERA_ROTATE] && !hotkeys[HOTKEY_CAMERA_ROTATE_ABOUT_TARGET] )
{
if (g_mouse_x >= g_xres-2 && g_mouse_x < g_xres)
CameraLock(rightwards * (m->ViewScrollSpeed * DeltaTime));
else if (g_mouse_x <= 3 && g_mouse_x >= 0)
CameraLock(-rightwards * (m->ViewScrollSpeed * DeltaTime));
if (g_mouse_y >= g_yres-2 && g_mouse_y < g_yres)
CameraLock(-forwards_horizontal * (m->ViewScrollSpeed * DeltaTime));
else if (g_mouse_y <= 3 && g_mouse_y >= 0)
CameraLock(forwards_horizontal * (m->ViewScrollSpeed * DeltaTime));
}
// Keyboard movement (added to mouse movement, so you can go faster if you want)
if( hotkeys[HOTKEY_CAMERA_PAN_KEYBOARD] )
{
if( hotkeys[HOTKEY_CAMERA_RIGHT] )
CameraLock(rightwards * (m->ViewScrollSpeed * DeltaTime));
if( hotkeys[HOTKEY_CAMERA_LEFT] )
CameraLock(-rightwards * (m->ViewScrollSpeed * DeltaTime));
if( hotkeys[HOTKEY_CAMERA_DOWN] )
CameraLock(-forwards_horizontal * (m->ViewScrollSpeed * DeltaTime));
if( hotkeys[HOTKEY_CAMERA_UP] )
CameraLock(forwards_horizontal * (m->ViewScrollSpeed * DeltaTime));
}
// Smoothed zooming (move a certain percentage towards the desired zoom distance every frame)
// Note that scroll wheel zooming is event-based and handled in game_view_handler
if( hotkeys[HOTKEY_CAMERA_ZOOM_IN] )
m->ZoomDelta += m->ViewZoomSensitivity*DeltaTime;
else if( hotkeys[HOTKEY_CAMERA_ZOOM_OUT] )
m->ZoomDelta -= m->ViewZoomSensitivity*DeltaTime;
if (fabsf(m->ZoomDelta) > 0.1f) // use a fairly high limit to avoid nasty flickering when zooming
{
float zoom_proportion = powf(m->ViewZoomSmoothness, DeltaTime);
CameraLock(forwards * (m->ZoomDelta * (1.0f-zoom_proportion)), false);
m->ZoomDelta *= zoom_proportion;
}
m->ViewCamera.UpdateFrustum ();
}
void CGameView::PushCameraTarget( const CVector3D& target )
{
// Save the current position
m->CameraTargets.push_back( m->ViewCamera.m_Orientation.GetTranslation() );
// And set the camera
SetCameraTarget( target );
}
void CGameView::SetCameraTarget( const CVector3D& target )
{
// Maintain the same orientation and level of zoom, if we can
// (do this by working out the point the camera is looking at, saving
// the difference between that position and the camera point, and restoring
// that difference to our new target)
CVector3D CurrentTarget = m->ViewCamera.GetFocus();
m->CameraDelta = target - CurrentTarget;
}
void CGameView::PopCameraTarget()
{
m->CameraDelta = m->CameraTargets.back() - m->ViewCamera.m_Orientation.GetTranslation();
m->CameraTargets.pop_back();
}
InReaction game_view_handler(const SDL_Event_* ev)
{
// put any events that must be processed even if inactive here
if(!g_app_has_focus || !g_Game)
return IN_PASS;
CGameView *pView=g_Game->GetView();
return pView->HandleEvent(ev);
}
InReaction CGameView::HandleEvent(const SDL_Event_* ev)
{
switch(ev->ev.type)
{
case SDL_HOTKEYDOWN:
switch(ev->ev.user.code)
{
case HOTKEY_WIREFRAME:
if (g_Renderer.GetModelRenderMode() == SOLID)
{
g_Renderer.SetTerrainRenderMode(EDGED_FACES);
g_Renderer.SetModelRenderMode(EDGED_FACES);
}
else if (g_Renderer.GetModelRenderMode() == EDGED_FACES)
{
g_Renderer.SetTerrainRenderMode(WIREFRAME);
g_Renderer.SetModelRenderMode(WIREFRAME);
}
else
{
g_Renderer.SetTerrainRenderMode(SOLID);
g_Renderer.SetModelRenderMode(SOLID);
}
return( IN_HANDLED );
case HOTKEY_CAMERA_RESET_ORIGIN:
ResetCamera();
return( IN_HANDLED );
case HOTKEY_CAMERA_RESET:
ResetCameraOrientation();
return( IN_HANDLED );
case HOTKEY_CAMERA_ROTATE_ABOUT_TARGET:
RotateAboutTarget();
return( IN_HANDLED );
// Mouse wheel must be treated using events instead of polling,
// because SDL auto-generates a sequence of mousedown/mouseup events
// and we never get to see the "down" state inside Update().
case HOTKEY_CAMERA_ZOOM_WHEEL_IN:
m->ZoomDelta += m->ViewZoomSensitivityWheel;
return( IN_HANDLED );
case HOTKEY_CAMERA_ZOOM_WHEEL_OUT:
m->ZoomDelta -= m->ViewZoomSensitivityWheel;
return( IN_HANDLED );
default:
if( ( ev->ev.user.code >= HOTKEY_CAMERA_BOOKMARK_0 ) && ( ev->ev.user.code <= HOTKEY_CAMERA_BOOKMARK_9 ) )
{
// The above test limits it to 10 bookmarks, so don't worry about overflowing
i8 id = (i8)( ev->ev.user.code - HOTKEY_CAMERA_BOOKMARK_0 );
if( hotkeys[HOTKEY_CAMERA_BOOKMARK_SAVE] )
{
// Attempt to track the ground we're looking at
cameraBookmarks[id] = GetCamera()->GetFocus();
bookmarkInUse[id] = true;
}
else if( hotkeys[HOTKEY_CAMERA_BOOKMARK_SNAP] )
{
if( bookmarkInUse[id] && ( currentBookmark == -1 ) )
{
PushCameraTarget( cameraBookmarks[id] );
currentBookmark = id;
}
}
else
{
if( bookmarkInUse[id] )
SetCameraTarget( cameraBookmarks[id] );
}
return( IN_HANDLED );
}
}
case SDL_HOTKEYUP:
switch( ev->ev.user.code )
{
case HOTKEY_CAMERA_BOOKMARK_SNAP:
if( currentBookmark != -1 )
PopCameraTarget();
currentBookmark = -1;
break;
default:
return( IN_PASS );
}
return( IN_HANDLED );
}
return IN_PASS;
}