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