@@ -1658,11 +1658,10 @@ static void calcVis(const FrustumClass & frustum, WorldHeightMap *pMap, Int minX
16581658// =============================================================================
16591659/* * Updates the positioning of the drawn portion of the height map in the
16601660heightmap. As the view slides around, this determines what is the actually
1661- rendered portion of the terrain. Only a 96x96 section is rendered at any time,
1662- even though maps can be up to 1024x1024. This function determines which subset
1663- is rendered. */
1661+ rendered portion of the terrain. Only a small section is rendered at any time.
1662+ */
16641663// =============================================================================
1665- void HeightMapRenderObjClass::updateCenter (CameraClass *camera , RefRenderObjListIterator *pLightsIterator)
1664+ void HeightMapRenderObjClass::updateCenter (CameraClass *camera, const Vector3 *cameraPivot , RefRenderObjListIterator *pLightsIterator)
16661665{
16671666 if (m_map==nullptr ) {
16681667 return ;
@@ -1673,105 +1672,127 @@ void HeightMapRenderObjClass::updateCenter(CameraClass *camera , RefRenderObjLis
16731672 if (m_vertexBufferTiles ==nullptr )
16741673 return ; // did not initialize resources yet.
16751674
1676- BaseHeightMapRenderObjClass::updateCenter (camera, pLightsIterator);
1675+ BaseHeightMapRenderObjClass::updateCenter (camera, cameraPivot, pLightsIterator);
16771676
16781677 if (m_x >= m_map->getXExtent () && m_y >= m_map->getYExtent ())
16791678 {
16801679 return ; // no need to center.
16811680 }
16821681
1682+ const Real cameraPitch = WWMath::Asin (fabsf (camera->Get_Forward_Dir ().Z ));
16831683 Int newOrgX;
16841684 Int newOrgY;
16851685
1686- // determine the ray corresponding to the camera and distance to projection plane
1687- Matrix3D camera_matrix = camera->Get_Transform ();
1688-
1689- Vector3 camera_location = camera->Get_Position ();
1690-
1691- Vector3 rayLocation;
1692- Vector3 rayDirection;
1693- Vector3 rayDirectionPt;
1694- // the projected ray has the same origin as the camera
1695- rayLocation = camera_location;
1696- // determine the location of the screen coordinate in camera-model space
1697- const ViewportClass &viewport = camera->Get_Viewport ();
1698- Int i, j, minHt;
1699-
1700- Real intersectionZ;
1701- minHt = m_map->getMaxHeightValue ();
1702- for (j=0 ; j<m_y; j+=4 ) {
1703- for (i=0 ; i<m_x; i+=4 ) {
1704- Short cur = m_map->getDisplayHeight (i,j);
1705- if (cur<minHt) minHt = cur;
1686+ if (cameraPitch > ViewDefaultLowPitchRadians)
1687+ {
1688+ // TheSuperHackers @info This is the original code to determine the center position for the visible terrain area.
1689+ // It is relatively expensive and breaks when the frustum planes can no longer intersect with the terrain at low camera
1690+ // pitch or when the camera is too far from the terrain, but it is very accurate when the camera is close to the
1691+ // terrain. For now, we prefer to keep this code for the original camera pitch and above.
1692+
1693+ // determine the ray corresponding to the camera and distance to projection plane
1694+ const Matrix3D& camera_matrix = camera->Get_Transform ();
1695+ Vector3 camera_location = camera->Get_Position ();
1696+ Vector3 rayLocation;
1697+ Vector3 rayDirection;
1698+ Vector3 rayDirectionPt;
1699+ // the projected ray has the same origin as the camera
1700+ rayLocation = camera_location;
1701+ // determine the location of the screen coordinate in camera-model space
1702+ const ViewportClass &viewport = camera->Get_Viewport ();
1703+ Int i, j, minHt;
1704+
1705+ Real intersectionZ;
1706+ minHt = m_map->getMaxHeightValue ();
1707+ for (j=0 ; j<m_y; j+=4 ) {
1708+ for (i=0 ; i<m_x; i+=4 ) {
1709+ Short cur = m_map->getDisplayHeight (i,j);
1710+ if (cur<minHt) minHt = cur;
1711+ }
17061712 }
1707- }
1708- intersectionZ = ( float )minHt ;
1709- // float aspect = camera->Get_Aspect_Ratio();
1710-
1711- Vector2 min,max;
1712- camera-> Get_View_Plane (min, max);
1713- float xscale = (max.X - min.X );
1714- float yscale = (max. Y - min. Y );
1715-
1716- float zmod = - 1.0 ; // Scene->vpd; // Note: view plane distance is now always 1.0 from the camera
1717- float minX = 200000 ;
1718- float maxX = -minX ;
1719- float minY = 200000 ;
1720- float maxY = -minY;
1721- for (i =0 ; i <2 ; i ++) {
1722- for (j= 0 ; j< 2 ; j++) {
1723- float xmod = (-i + 0.5 + viewport.Min .X ) * zmod * xscale ;// / aspect;
1724- float ymod = (j - 0.5 - viewport. Min . Y ) * zmod * yscale; // * aspect;
1725-
1726- // transform the screen coordinates by the camera's matrix into world coordinates.
1727- float x = zmod * camera_matrix[0 ][2 ] + xmod * camera_matrix[0 ][0 ] + ymod * camera_matrix[0 ][1 ];
1728- float y = zmod * camera_matrix[1 ][2 ] + xmod * camera_matrix[1 ][0 ] + ymod * camera_matrix[1 ][1 ];
1729- float z = zmod * camera_matrix[ 2 ][ 2 ] + xmod * camera_matrix[ 2 ][ 0 ] + ymod * camera_matrix[ 2 ][ 1 ];
1730-
1731- rayDirection.Set (x,y,z );
1732- rayDirection. Normalize () ;
1733- rayDirectionPt = rayLocation+rayDirection;
1734-
1735- x = Vector3::Find_X_At_Z (intersectionZ, rayLocation, rayDirectionPt);
1736- y = Vector3::Find_Y_At_Z (intersectionZ, rayLocation, rayDirectionPt) ;
1737- if (x<minX) minX = x;
1738- if (x>maxX) maxX = x ;
1739- if (y<minY) minY = y;
1740- if (y>maxY) maxY = y;
1713+ intersectionZ = ( float )minHt;
1714+ // float aspect = camera->Get_Aspect_Ratio() ;
1715+
1716+ Vector2 min,max;
1717+ camera-> Get_View_Plane ( min,max) ;
1718+ float xscale = ( max. X - min. X );
1719+ float yscale = (max.Y - min.Y );
1720+
1721+ float zmod = - 1.0 ; // Scene->vpd; // Note: view plane distance is now always 1.0 from the camera
1722+ float minX = 200000 ;
1723+ float maxX = -minX ;
1724+ float minY = 200000 ;
1725+ float maxY = -minY ;
1726+ for (i= 0 ; i< 2 ; i++) {
1727+ for (j =0 ; j <2 ; j ++) {
1728+ float xmod = (-i + 0.5 + viewport. Min . X ) * zmod * xscale; // / aspect;
1729+ float ymod = (j - 0.5 - viewport.Min .Y ) * zmod * yscale ;// * aspect;
1730+
1731+ // transform the screen coordinates by the camera's matrix into world coordinates.
1732+ float x = zmod * camera_matrix[ 0 ][ 2 ] + xmod * camera_matrix[ 0 ][ 0 ] + ymod * camera_matrix[ 0 ][ 1 ];
1733+ float y = zmod * camera_matrix[1 ][2 ] + xmod * camera_matrix[1 ][0 ] + ymod * camera_matrix[1 ][1 ];
1734+ float z = zmod * camera_matrix[2 ][2 ] + xmod * camera_matrix[2 ][0 ] + ymod * camera_matrix[2 ][1 ];
1735+
1736+ rayDirection. Set (x,y,z);
1737+ rayDirection.Normalize ( );
1738+ rayDirectionPt = rayLocation+ rayDirection;
1739+
1740+ x = Vector3::Find_X_At_Z (intersectionZ, rayLocation, rayDirectionPt);
1741+ y = Vector3::Find_Y_At_Z (intersectionZ, rayLocation, rayDirectionPt);
1742+ if (x<minX) minX = x ;
1743+ if (x>maxX) maxX = x;
1744+ if (y<minY) minY = y ;
1745+ if (y>maxY) maxY = y;
1746+ }
17411747 }
1742- }
17431748
1744- // convert back to cell indexes.
1745- minX /= MAP_XY_FACTOR;
1746- maxX /= MAP_XY_FACTOR;
1747- minY /= MAP_XY_FACTOR;
1748- maxY /= MAP_XY_FACTOR;
1749-
1750- minX += m_map->getBorderSizeInline ();
1751- maxX += m_map->getBorderSizeInline ();
1752- minY += m_map->getBorderSizeInline ();
1753- maxY += m_map->getBorderSizeInline ();
1754-
1755- visMinX = m_map->getXExtent ();
1756- visMinY = m_map->getYExtent ();
1757- visMaxX = 0 ;
1758- visMaxY = 0 ;
1759-
1760- // /< @todo find out why values go out of range
1761- if (minX<0 ) minX=0 ;
1762- if (minY<0 ) minY=0 ;
1763- if (maxX > visMinX) maxX = visMinX;
1764- if (maxY > visMinY) maxY = visMinY;
1765-
1766- const FrustumClass & frustum = camera->Get_Frustum ();
1767- Int limit = (maxX-minX)/2 ;
1768- if (limit > WIDE_STEP/2 ) {
1769- limit=WIDE_STEP/2 ;
1770- }
1771- calcVis (frustum, m_map, minX-WIDE_STEP/2 , minY-WIDE_STEP/2 , maxX+WIDE_STEP/2 , maxY+WIDE_STEP/2 , limit);
1749+ // convert back to cell indexes.
1750+ minX /= MAP_XY_FACTOR;
1751+ maxX /= MAP_XY_FACTOR;
1752+ minY /= MAP_XY_FACTOR;
1753+ maxY /= MAP_XY_FACTOR;
1754+
1755+ minX += m_map->getBorderSizeInline ();
1756+ maxX += m_map->getBorderSizeInline ();
1757+ minY += m_map->getBorderSizeInline ();
1758+ maxY += m_map->getBorderSizeInline ();
1759+
1760+ visMinX = m_map->getXExtent ();
1761+ visMinY = m_map->getYExtent ();
1762+ visMaxX = 0 ;
1763+ visMaxY = 0 ;
1764+
1765+ // /< @todo find out why values go out of range
1766+ if (minX<0 ) minX=0 ;
1767+ if (minY<0 ) minY=0 ;
1768+ if (maxX > visMinX) maxX = visMinX;
1769+ if (maxY > visMinY) maxY = visMinY;
1770+
1771+ const FrustumClass & frustum = camera->Get_Frustum ();
1772+ Int limit = (maxX-minX)/2 ;
1773+ if (limit > WIDE_STEP/2 ) {
1774+ limit=WIDE_STEP/2 ;
1775+ }
1776+ calcVis (frustum, m_map, minX-WIDE_STEP/2 , minY-WIDE_STEP/2 , maxX+WIDE_STEP/2 , maxY+WIDE_STEP/2 , limit);
17721777
1773- newOrgX = (visMaxX+visMinX)/2 - m_x/2.0 ;
1774- newOrgY = (visMaxY+visMinY)/2 - m_y/2.0 ;
1778+ newOrgX = (visMaxX+visMinX)/2 - m_x/2 ;
1779+ newOrgY = (visMaxY+visMinY)/2 - m_y/2 ;
1780+ }
1781+ else
1782+ {
1783+ // TheSuperHackers @fix Very fast approximation. Works well for all camera pitches, but is less accurate
1784+ // than the original implementation. Using this method for higher camera pitch would require to increase
1785+ // the normal draw width by at least one tile length.
1786+ const Real visibleTerrainEdgeLen = (m_x+m_y)/2 * MAP_XY_FACTOR;
1787+ const Real magicEdgeLenScale = 0 .25f * visibleTerrainEdgeLen;
1788+ Vector3 viewDir = camera->Get_Forward_Dir ();
1789+ Vector2 shiftPivot;
1790+ shiftPivot.X = viewDir.X * magicEdgeLenScale;
1791+ shiftPivot.Y = viewDir.Y * magicEdgeLenScale;
1792+
1793+ newOrgX = WWMath::Round ((cameraPivot->X + shiftPivot.X )/MAP_XY_FACTOR) - m_x/2 + m_map->getBorderSizeInline ();
1794+ newOrgY = WWMath::Round ((cameraPivot->Y + shiftPivot.Y )/MAP_XY_FACTOR) - m_y/2 + m_map->getBorderSizeInline ();
1795+ }
17751796
17761797 WorldHeightMap::DrawArea newDrawArea = m_map->createDrawArea (newOrgX, newOrgY);
17771798
0 commit comments