1
0
forked from 0ad/0ad

Improve camera restraints at edge of map to be independent of zoom, and to work on circular maps.

Fixes #573.

This was SVN commit r9011.
This commit is contained in:
Ykkrosh 2011-03-04 20:02:05 +00:00
parent bec4c6437b
commit 3a5ca7fdbc
6 changed files with 99 additions and 63 deletions

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2010 Wildfire Games.
/* Copyright (C) 2011 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -55,6 +55,7 @@
#include "simulation/LOSManager.h"
#include "simulation2/Simulation2.h"
#include "simulation2/components/ICmpPosition.h"
#include "simulation2/components/ICmpRangeManager.h"
extern int g_xres, g_yres;
@ -62,7 +63,10 @@ const float CGameView::defaultFOV = DEGTORAD(20.f);
const float CGameView::defaultNear = 16.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
const float CGameView::cameraPivotMargin = -20.0f;
// Maximum distance outside the edge of the map that the camera's
// focus point can be moved
static const float CAMERA_EDGE_MARGIN = 2.0f*CELL_SIZE;
/**
* A value with exponential decay towards the target value.
@ -292,7 +296,7 @@ public:
static void ScriptingInit();
};
static void SetupCameraMatrix(CGameViewImpl* m, CMatrix3D* orientation)
static void SetupCameraMatrixSmooth(CGameViewImpl* m, CMatrix3D* orientation)
{
orientation->SetIdentity();
orientation->RotateX(m->RotateX.GetSmoothedValue());
@ -300,6 +304,22 @@ static void SetupCameraMatrix(CGameViewImpl* m, CMatrix3D* orientation)
orientation->Translate(m->PosX.GetSmoothedValue(), m->PosY.GetSmoothedValue(), m->PosZ.GetSmoothedValue());
}
static void SetupCameraMatrixSmoothRot(CGameViewImpl* m, CMatrix3D* orientation)
{
orientation->SetIdentity();
orientation->RotateX(m->RotateX.GetSmoothedValue());
orientation->RotateY(m->RotateY.GetSmoothedValue());
orientation->Translate(m->PosX.GetValue(), m->PosY.GetValue(), m->PosZ.GetValue());
}
static void SetupCameraMatrixNonSmooth(CGameViewImpl* m, CMatrix3D* orientation)
{
orientation->SetIdentity();
orientation->RotateX(m->RotateX.GetValue());
orientation->RotateY(m->RotateY.GetValue());
orientation->Translate(m->PosX.GetValue(), m->PosY.GetValue(), m->PosZ.GetValue());
}
CGameView::CGameView(CGame *pGame):
m(new CGameViewImpl(pGame))
{
@ -311,7 +331,7 @@ CGameView::CGameView(CGame *pGame):
m->ViewCamera.SetViewPort(vp);
m->ViewCamera.SetProjection(defaultNear, defaultFar, defaultFOV);
SetupCameraMatrix(m, &m->ViewCamera.m_Orientation);
SetupCameraMatrixSmooth(m, &m->ViewCamera.m_Orientation);
m->ViewCamera.UpdateFrustum();
m->CullCamera = m->ViewCamera;
@ -569,11 +589,7 @@ static void ClampDistance(CGameViewImpl* m, bool smooth)
return;
CCamera targetCam = m->ViewCamera;
targetCam.m_Orientation.SetIdentity();
targetCam.m_Orientation.RotateX(m->RotateX.GetSmoothedValue());
targetCam.m_Orientation.RotateY(m->RotateY.GetSmoothedValue());
// Use non-smoothed position:
targetCam.m_Orientation.Translate(m->PosX.GetValue(), m->PosY.GetValue(), m->PosZ.GetValue());
SetupCameraMatrixSmoothRot(m, &targetCam.m_Orientation);
CVector3D forwards = targetCam.m_Orientation.GetIn();
@ -721,14 +737,9 @@ void CGameView::Update(float DeltaTime)
}
else
{
// move the camera after unit
// use smoothed values of rotation around X and Y, since we need to hold user's rotation done
// in this function above
// Move the camera to match the unit
CCamera targetCam = m->ViewCamera;
targetCam.m_Orientation.SetIdentity();
targetCam.m_Orientation.RotateX(m->RotateX.GetSmoothedValue());
targetCam.m_Orientation.RotateY(m->RotateY.GetSmoothedValue());
targetCam.m_Orientation.Translate(m->PosX.GetValue(), m->PosY.GetValue(), m->PosZ.GetValue());
SetupCameraMatrixSmoothRot(m, &targetCam.m_Orientation);
CVector3D pivot = targetCam.GetFocus();
CVector3D delta = pos - pivot;
@ -768,18 +779,35 @@ void CGameView::Update(float DeltaTime)
if (m->ConstrainCamera)
{
CCamera targetCam = m->ViewCamera;
SetupCameraMatrix(m, &targetCam.m_Orientation);
SetupCameraMatrixSmoothRot(m, &targetCam.m_Orientation);
CTerrain* pTerrain = m->Game->GetWorld()->GetTerrain();
float min_x = pTerrain->GetMinX() + cameraPivotMargin;
float max_x = pTerrain->GetMaxX() - cameraPivotMargin;
float min_z = pTerrain->GetMinZ() + cameraPivotMargin;
float max_z = pTerrain->GetMaxZ() - cameraPivotMargin;
// Clamp so that pos+in is within the margin
CVector3D in = targetCam.m_Orientation.GetIn() * m->ViewZoomDefault;
m->PosX.ClampSmoothly(min_x - in.X, max_x - in.X);
m->PosZ.ClampSmoothly(min_z - in.Z, max_z - in.Z);
CVector3D pivot = targetCam.GetFocus();
CVector3D delta = targetCam.m_Orientation.GetTranslation() - pivot;
CVector3D desiredPivot = pivot;
CmpPtr<ICmpRangeManager> cmpRangeManager(*m->Game->GetSimulation2(), SYSTEM_ENTITY);
if (!cmpRangeManager.null() && cmpRangeManager->GetLosCircular())
{
// Clamp to a circular region around the center of the map
float r = pTerrain->GetMaxX() / 2;
CVector3D center(r, desiredPivot.Y, r);
float dist = (desiredPivot - center).Length();
if (dist > r + CAMERA_EDGE_MARGIN)
desiredPivot = center + (desiredPivot - center).Normalized() * (r + CAMERA_EDGE_MARGIN);
}
else
{
// Clamp to the square edges of the map
desiredPivot.X = Clamp(desiredPivot.X, pTerrain->GetMinX() - CAMERA_EDGE_MARGIN, pTerrain->GetMaxX() + CAMERA_EDGE_MARGIN);
desiredPivot.Z = Clamp(desiredPivot.Z, pTerrain->GetMinZ() - CAMERA_EDGE_MARGIN, pTerrain->GetMaxZ() + CAMERA_EDGE_MARGIN);
}
// Update the position so that pivot is within the margin
m->PosX.SetValueSmoothly(desiredPivot.X + delta.X);
m->PosZ.SetValueSmoothly(desiredPivot.Z + delta.Z);
}
m->PosX.Update(DeltaTime);
@ -789,7 +817,7 @@ void CGameView::Update(float DeltaTime)
// Handle rotation around the Y (vertical) axis
{
CCamera targetCam = m->ViewCamera;
SetupCameraMatrix(m, &targetCam.m_Orientation);
SetupCameraMatrixSmooth(m, &targetCam.m_Orientation);
float rotateYDelta = m->RotateY.Update(DeltaTime);
if (rotateYDelta)
@ -816,7 +844,7 @@ void CGameView::Update(float DeltaTime)
// Handle rotation around the X (sideways, relative to camera) axis
{
CCamera targetCam = m->ViewCamera;
SetupCameraMatrix(m, &targetCam.m_Orientation);
SetupCameraMatrixSmooth(m, &targetCam.m_Orientation);
float rotateXDelta = m->RotateX.Update(DeltaTime);
if (rotateXDelta)
@ -858,7 +886,7 @@ void CGameView::Update(float DeltaTime)
m->RotateY.Wrap(-(float)M_PI, (float)M_PI);
// Update the camera matrix
SetupCameraMatrix(m, &m->ViewCamera.m_Orientation);
SetupCameraMatrixSmooth(m, &m->ViewCamera.m_Orientation);
m->ViewCamera.UpdateFrustum();
}
@ -870,10 +898,7 @@ void CGameView::MoveCameraTarget(const CVector3D& target, bool minimap)
// that difference to our new target)
CCamera targetCam = m->ViewCamera;
targetCam.m_Orientation.SetIdentity();
targetCam.m_Orientation.RotateX(m->RotateX.GetValue());
targetCam.m_Orientation.RotateY(m->RotateY.GetValue());
targetCam.m_Orientation.Translate(m->PosX.GetValue(), m->PosY.GetValue(), m->PosZ.GetValue());
SetupCameraMatrixNonSmooth(m, &targetCam.m_Orientation);
CVector3D pivot = targetCam.GetFocus();
CVector3D delta = target - pivot;
@ -906,7 +931,7 @@ void CGameView::ResetCameraTarget(const CVector3D& target)
ClampDistance(m, false);
SetupCameraMatrix(m, &m->ViewCamera.m_Orientation);
SetupCameraMatrixSmooth(m, &m->ViewCamera.m_Orientation);
m->ViewCamera.UpdateFrustum();
// Break out of following mode so the camera really moves to the target
@ -916,10 +941,7 @@ void CGameView::ResetCameraTarget(const CVector3D& target)
void CGameView::ResetCameraAngleZoom()
{
CCamera targetCam = m->ViewCamera;
targetCam.m_Orientation.SetIdentity();
targetCam.m_Orientation.RotateX(m->RotateX.GetValue());
targetCam.m_Orientation.RotateY(m->RotateY.GetValue());
targetCam.m_Orientation.Translate(m->PosX.GetValue(), m->PosY.GetValue(), m->PosZ.GetValue());
SetupCameraMatrixNonSmooth(m, &targetCam.m_Orientation);
// Compute the zoom adjustment to get us back to the default
CVector3D forwards = targetCam.m_Orientation.GetIn();

View File

@ -46,7 +46,6 @@ public:
static const float defaultFOV, defaultCullFOV, defaultNear, defaultFar;
private:
static const float cameraPivotMargin;
CGameViewImpl* m;
// Check whether lighting environment has changed and update vertex data if necessary

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2009 Wildfire Games.
/* Copyright (C) 2011 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -26,6 +26,14 @@
#include "maths/Bound.h"
#include "maths/Vector3D.h"
// To cope well with points that are slightly off the edge of the map,
// we act as if there's an N-tile margin around the edges of the heightfield.
// (N shouldn't be too huge else it'll hurt performance a little when
// RayIntersect loops through it all.)
// CTerrain::CalcPosition implements clamp-to-edge behaviour so the tracer
// will have that behaviour.
static const int MARGIN_SIZE = 64;
///////////////////////////////////////////////////////////////////////////////
// CHFTracer constructor
CHFTracer::CHFTracer(CTerrain *pTerrain):
@ -121,8 +129,8 @@ bool CHFTracer::RayIntersect(const CVector3D& origin, const CVector3D& dir, int&
{
// intersect first against bounding box
CBound bound;
bound[0]=CVector3D(0,0,0);
bound[1]=CVector3D(m_MapSize*m_CellSize,65535*m_HeightScale,m_MapSize*m_CellSize);
bound[0] = CVector3D(-MARGIN_SIZE * m_CellSize, 0, -MARGIN_SIZE * m_CellSize);
bound[1] = CVector3D((m_MapSize + MARGIN_SIZE) * m_CellSize, 65535 * m_HeightScale, (m_MapSize + MARGIN_SIZE) * m_CellSize);
float tmin,tmax;
if (!bound.RayIntersect(origin,dir,tmin,tmax)) {
@ -145,10 +153,10 @@ bool CHFTracer::RayIntersect(const CVector3D& origin, const CVector3D& dir, int&
float invCellSize=1.0f/float(m_CellSize);
float fcx=traversalPt.X*invCellSize;
int cx=int(fcx);
int cx=(int)floor(fcx);
float fcz=traversalPt.Z*invCellSize;
int cz=int(fcz);
int cz=(int)floor(fcz);
float invdx = 1.0e20f;
float invdz = 1.0e20f;
@ -160,7 +168,8 @@ bool CHFTracer::RayIntersect(const CVector3D& origin, const CVector3D& dir, int&
do {
// test current cell
if (cx>=0 && cx<int(m_MapSize-1) && cz>=0 && cz<int(m_MapSize-1)) {
if (cx >= -MARGIN_SIZE && cx < int(m_MapSize + MARGIN_SIZE - 1) && cz >= -MARGIN_SIZE && cz < int(m_MapSize + MARGIN_SIZE - 1))
{
float dist;
if (CellIntersect(cx,cz,origin,dir,dist)) {
@ -174,14 +183,14 @@ bool CHFTracer::RayIntersect(const CVector3D& origin, const CVector3D& dir, int&
{
// Degenerate case: y close to zero
// catch travelling off the map
if( ( cx < 0 ) && ( sx < 0 ) )
return( false );
if( ( cx >= (int)m_MapSize ) && ( sx > 0 ) )
return( false );
if( ( cz < 0 ) && ( sz < 0 ) )
return( false );
if( ( cz >= (int)m_MapSize ) && ( sz > 0 ) )
return( false );
if ((cx < -MARGIN_SIZE) && (sx < 0))
return false;
if ((cx >= (int)(m_MapSize + MARGIN_SIZE - 1)) && (sx > 0))
return false;
if ((cz < -MARGIN_SIZE) && (sz < 0))
return false;
if ((cz >= (int)(m_MapSize + MARGIN_SIZE - 1)) && (sz > 0))
return false;
}
// get coords of current cell

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2010 Wildfire Games.
/* Copyright (C) 2011 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -105,13 +105,13 @@ CStr8 CTerrain::GetMovementClass(ssize_t i, ssize_t j) const
///////////////////////////////////////////////////////////////////////////////
// CalcPosition: calculate the world space position of the vertex at (i,j)
// If i,j is off the map, it acts as if the edges of the terrain are extended
// outwards to infinity
void CTerrain::CalcPosition(ssize_t i, ssize_t j, CVector3D& pos) const
{
u16 height;
if ((size_t)i < (size_t)m_MapSize && (size_t)j < (size_t)m_MapSize) // will reject negative coordinates
height = m_Heightmap[j*m_MapSize + i];
else
height = 0;
ssize_t hi = clamp(i, (ssize_t)0, m_MapSize-1);
ssize_t hj = clamp(j, (ssize_t)0, m_MapSize-1);
u16 height = m_Heightmap[hj*m_MapSize + hi];
pos.X = float(i*CELL_SIZE);
pos.Y = float(height*HEIGHT_SCALE);
pos.Z = float(j*CELL_SIZE);
@ -121,11 +121,9 @@ void CTerrain::CalcPosition(ssize_t i, ssize_t j, CVector3D& pos) const
// CalcPositionFixed: calculate the world space position of the vertex at (i,j)
void CTerrain::CalcPositionFixed(ssize_t i, ssize_t j, CFixedVector3D& pos) const
{
u16 height;
if ((size_t)i < (size_t)m_MapSize && (size_t)j < (size_t)m_MapSize) // will reject negative coordinates
height = m_Heightmap[j*m_MapSize + i];
else
height = 0;
ssize_t hi = clamp(i, (ssize_t)0, m_MapSize-1);
ssize_t hj = clamp(j, (ssize_t)0, m_MapSize-1);
u16 height = m_Heightmap[hj*m_MapSize + hi];
pos.X = fixed::FromInt(i) * (int)CELL_SIZE;
pos.Y = fixed::FromInt(height) / (int)HEIGHT_UNITS_PER_METRE;
pos.Z = fixed::FromInt(j) * (int)CELL_SIZE;

View File

@ -83,6 +83,13 @@ void CVector3D::Normalize ()
Z *= scale;
}
CVector3D CVector3D::Normalized () const
{
float scale = 1.0f/Length ();
return CVector3D(X * scale, Y * scale, Z * scale);
}
//-----------------------------------------------------------------------------

View File

@ -102,6 +102,7 @@ class CVector3D
float Length () const;
float LengthSquared () const;
void Normalize ();
CVector3D Normalized () const;
// Returns 3 element array of floats, e.g. for glVertex3fv
const float* GetFloatArray() const { return &X; }