Pyrogenesis  13997
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
HFTracer.cpp
Go to the documentation of this file.
1 /* Copyright (C) 2011 Wildfire Games.
2  * This file is part of 0 A.D.
3  *
4  * 0 A.D. is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * 0 A.D. is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 /*
19  * Determine intersection of rays with a heightfield.
20  */
21 
22 #include "precompiled.h"
23 
24 #include "HFTracer.h"
25 #include "Terrain.h"
27 #include "maths/Vector3D.h"
28 
29 // To cope well with points that are slightly off the edge of the map,
30 // we act as if there's an N-tile margin around the edges of the heightfield.
31 // (N shouldn't be too huge else it'll hurt performance a little when
32 // RayIntersect loops through it all.)
33 // CTerrain::CalcPosition implements clamp-to-edge behaviour so the tracer
34 // will have that behaviour.
35 static const int MARGIN_SIZE = 64;
36 
37 ///////////////////////////////////////////////////////////////////////////////
38 // CHFTracer constructor
40  m_pTerrain(pTerrain),
41  m_Heightfield(m_pTerrain->GetHeightMap()),
42  m_MapSize(m_pTerrain->GetVerticesPerSide()),
43  m_CellSize((float)TERRAIN_TILE_SIZE),
44  m_HeightScale(HEIGHT_SCALE)
45 {
46 }
47 
48 
49 ///////////////////////////////////////////////////////////////////////////////
50 // RayTriIntersect: intersect a ray with triangle defined by vertices
51 // v0,v1,v2; return true if ray hits triangle at distance less than dist,
52 // or false otherwise
53 bool CHFTracer::RayTriIntersect(const CVector3D& v0, const CVector3D& v1, const CVector3D& v2,
54  const CVector3D& origin, const CVector3D& dir, float& dist) const
55 {
56  const float EPSILON=0.00001f;
57 
58  // calculate edge vectors
59  CVector3D edge0=v1-v0;
60  CVector3D edge1=v2-v0;
61 
62  // begin calculating determinant - also used to calculate U parameter
63  CVector3D pvec=dir.Cross(edge1);
64 
65  // if determinant is near zero, ray lies in plane of triangle
66  float det = edge0.Dot(pvec);
67  if (fabs(det)<EPSILON)
68  return false;
69 
70  float inv_det = 1.0f/det;
71 
72  // calculate vector from vert0 to ray origin
73  CVector3D tvec=origin-v0;
74 
75  // calculate U parameter, test bounds
76  float u=tvec.Dot(pvec)*inv_det;
77  if (u<-0.01f || u>1.01f)
78  return false;
79 
80  // prepare to test V parameter
81  CVector3D qvec=tvec.Cross(edge0);
82 
83  // calculate V parameter and test bounds
84  float v=dir.Dot(qvec)*inv_det;
85  if (v<0.0f || u+v>1.0f)
86  return false;
87 
88  // calculate distance to intersection point from ray origin
89  float d=edge1.Dot(qvec)*inv_det;
90  if (d>=0 && d<dist) {
91  dist=d;
92  return true;
93  }
94 
95  return false;
96 }
97 
98 ///////////////////////////////////////////////////////////////////////////////
99 // CellIntersect: test if ray intersects either of the triangles in the given
100 // cell - return hit result, and distance to hit, if hit occurred
101 bool CHFTracer::CellIntersect(int cx, int cz, const CVector3D& origin, const CVector3D& dir, float& dist) const
102 {
103  bool res=false;
104 
105  // get vertices for this cell
106  CVector3D vpos[4];
107  m_pTerrain->CalcPosition(cx,cz,vpos[0]);
108  m_pTerrain->CalcPosition(cx+1,cz,vpos[1]);
109  m_pTerrain->CalcPosition(cx+1,cz+1,vpos[2]);
110  m_pTerrain->CalcPosition(cx,cz+1,vpos[3]);
111 
112  dist=1.0e30f;
113  if (RayTriIntersect(vpos[0],vpos[1],vpos[2],origin,dir,dist)) {
114  res=true;
115  }
116 
117  if (RayTriIntersect(vpos[0],vpos[2],vpos[3],origin,dir,dist)) {
118  res=true;
119  }
120 
121  return res;
122 }
123 
124 ///////////////////////////////////////////////////////////////////////////////
125 // RayIntersect: intersect ray with this heightfield; return true if
126 // intersection occurs (and fill in grid coordinates of intersection), or false
127 // otherwise
128 bool CHFTracer::RayIntersect(const CVector3D& origin, const CVector3D& dir, int& x, int& z, CVector3D& ipt) const
129 {
130  // If the map is empty (which should never happen),
131  // return early before we crash when reading zero-sized heightmaps
132  if (!m_MapSize)
133  {
134  debug_warn(L"CHFTracer::RayIntersect called with zero-size map");
135  return false;
136  }
137 
138  // intersect first against bounding box
139  CBoundingBoxAligned bound;
140  bound[0] = CVector3D(-MARGIN_SIZE * m_CellSize, 0, -MARGIN_SIZE * m_CellSize);
141  bound[1] = CVector3D((m_MapSize + MARGIN_SIZE) * m_CellSize, 65535 * m_HeightScale, (m_MapSize + MARGIN_SIZE) * m_CellSize);
142 
143  float tmin,tmax;
144  if (!bound.RayIntersect(origin,dir,tmin,tmax)) {
145  // ray missed world bounds; no intersection
146  return false;
147  }
148 
149  // project origin onto grid, if necessary, to get starting point for traversal
150  CVector3D traversalPt;
151  if (tmin>0) {
152  traversalPt=origin+dir*tmin;
153  } else {
154  traversalPt=origin;
155  }
156 
157  // setup traversal variables
158  int sx=dir.X<0 ? -1 : 1;
159  int sz=dir.Z<0 ? -1 : 1;
160 
161  float invCellSize=1.0f/float(m_CellSize);
162 
163  float fcx=traversalPt.X*invCellSize;
164  int cx=(int)floor(fcx);
165 
166  float fcz=traversalPt.Z*invCellSize;
167  int cz=(int)floor(fcz);
168 
169  float invdx = 1.0e20f;
170  float invdz = 1.0e20f;
171 
172  if (fabs(dir.X) > 1.0e-20)
173  invdx = float(1.0/fabs(dir.X));
174  if (fabs(dir.Z) > 1.0e-20)
175  invdz = float(1.0/fabs(dir.Z));
176 
177  do {
178  // test current cell
179  if (cx >= -MARGIN_SIZE && cx < int(m_MapSize + MARGIN_SIZE - 1) && cz >= -MARGIN_SIZE && cz < int(m_MapSize + MARGIN_SIZE - 1))
180  {
181  float dist;
182 
183  if (CellIntersect(cx,cz,origin,dir,dist)) {
184  x=cx;
185  z=cz;
186  ipt=origin+dir*dist;
187  return true;
188  }
189  }
190  else
191  {
192  // Degenerate case: y close to zero
193  // catch travelling off the map
194  if ((cx < -MARGIN_SIZE) && (sx < 0))
195  return false;
196  if ((cx >= (int)(m_MapSize + MARGIN_SIZE - 1)) && (sx > 0))
197  return false;
198  if ((cz < -MARGIN_SIZE) && (sz < 0))
199  return false;
200  if ((cz >= (int)(m_MapSize + MARGIN_SIZE - 1)) && (sz > 0))
201  return false;
202  }
203 
204  // get coords of current cell
205  fcx=traversalPt.X*invCellSize;
206  fcz=traversalPt.Z*invCellSize;
207 
208  // get distance to next cell in x,z
209  float dx=(sx==-1) ? fcx-float(cx) : 1-(fcx-float(cx));
210  dx*=invdx;
211  float dz=(sz==-1) ? fcz-float(cz) : 1-(fcz-float(cz));
212  dz*=invdz;
213 
214  // advance ..
215  float dist;
216  if (dx<dz) {
217  cx+=sx;
218  dist=dx;
219  } else {
220  cz+=sz;
221  dist=dz;
222  }
223 
224  traversalPt+=dir*dist;
225  } while (traversalPt.Y>=0);
226 
227  // fell off end of heightmap with no intersection; return a miss
228  return false;
229 }
const ssize_t TERRAIN_TILE_SIZE
metres [world space units] per tile in x and z
Definition: Terrain.h:40
float Dot(const CVector3D &vector) const
Definition: Vector3D.cpp:48
float m_HeightScale
Definition: HFTracer.h:59
CVector3D Cross(const CVector3D &vector) const
Definition: Vector3D.cpp:55
void CalcPosition(ssize_t i, ssize_t j, CVector3D &pos) const
Definition: Terrain.cpp:118
float m_CellSize
Definition: HFTracer.h:57
bool RayIntersect(const CVector3D &origin, const CVector3D &dir, int &x, int &z, CVector3D &ipt) const
Definition: HFTracer.cpp:128
bool CellIntersect(int cx, int cz, const CVector3D &origin, const CVector3D &dir, float &dist) const
Definition: HFTracer.cpp:101
float X
Definition: Vector3D.h:31
const float HEIGHT_SCALE
metres per u16 height unit
Definition: Terrain.h:46
float Y
Definition: Vector3D.h:31
CTerrain * m_pTerrain
Definition: HFTracer.h:51
static const int MARGIN_SIZE
Definition: HFTracer.cpp:35
bool RayTriIntersect(const CVector3D &v0, const CVector3D &v1, const CVector3D &v2, const CVector3D &origin, const CVector3D &dir, float &dist) const
Definition: HFTracer.cpp:53
bool RayIntersect(const CVector3D &origin, const CVector3D &dir, float &tmin, float &tmax) const
Check if a given ray intersects this AABB.
CHFTracer(CTerrain *pTerrain)
Definition: HFTracer.cpp:39
float Z
Definition: Vector3D.h:31
#define debug_warn(expr)
display the error dialog with the given text.
Definition: debug.h:324
const float EPSILON
Definition: Quaternion.cpp:23
size_t m_MapSize
Definition: HFTracer.h:55