diff --git a/source/graphics/GameView.cpp b/source/graphics/GameView.cpp index f2b3f76259..b54b645863 100644 --- a/source/graphics/GameView.cpp +++ b/source/graphics/GameView.cpp @@ -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 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(); diff --git a/source/graphics/GameView.h b/source/graphics/GameView.h index a85a97b324..ba87e1cfbd 100644 --- a/source/graphics/GameView.h +++ b/source/graphics/GameView.h @@ -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 diff --git a/source/graphics/HFTracer.cpp b/source/graphics/HFTracer.cpp index 0f96bc56e1..ee684b87c9 100644 --- a/source/graphics/HFTracer.cpp +++ b/source/graphics/HFTracer.cpp @@ -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=0 && cz= -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 diff --git a/source/graphics/Terrain.cpp b/source/graphics/Terrain.cpp index b96462faec..1d1a8e1965 100644 --- a/source/graphics/Terrain.cpp +++ b/source/graphics/Terrain.cpp @@ -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; diff --git a/source/maths/Vector3D.cpp b/source/maths/Vector3D.cpp index 04ed878195..5a1a1b1859 100644 --- a/source/maths/Vector3D.cpp +++ b/source/maths/Vector3D.cpp @@ -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); +} + //----------------------------------------------------------------------------- diff --git a/source/maths/Vector3D.h b/source/maths/Vector3D.h index 05994c90ff..5a49c954cf 100644 --- a/source/maths/Vector3D.h +++ b/source/maths/Vector3D.h @@ -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; }