Pyrogenesis  13997
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
Brush.cpp
Go to the documentation of this file.
1 /* Copyright (C) 2012 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  * Implementation of CBrush, a class representing a convex object
20  */
21 
22 #include "precompiled.h"
23 
24 #include "lib/ogl.h"
25 
26 #include <float.h>
27 
28 #include "Brush.h"
29 #include "BoundingBoxAligned.h"
30 #include "graphics/Frustum.h"
31 
32 
33 ///////////////////////////////////////////////////////////////////////////////
34 // Convert the given bounds into a brush
36 {
37  m_Vertices.resize(8);
38 
39  for(size_t i = 0; i < 8; ++i)
40  {
41  m_Vertices[i][0] = bounds[(i & 1) ? 1 : 0][0]; // X
42  m_Vertices[i][1] = bounds[(i & 2) ? 1 : 0][1]; // Y
43  m_Vertices[i][2] = bounds[(i & 4) ? 1 : 0][2]; // Z
44  }
45 
46  // construct cube face indices, 5 vertex indices per face (start vertex included twice)
47 
48  m_Faces.resize(30);
49 
50  m_Faces[0] = 0; m_Faces[1] = 1; m_Faces[2] = 3; m_Faces[3] = 2; m_Faces[4] = 0; // Z = min
51  m_Faces[5] = 4; m_Faces[6] = 5; m_Faces[7] = 7; m_Faces[8] = 6; m_Faces[9] = 4; // Z = max
52 
53  m_Faces[10] = 0; m_Faces[11] = 2; m_Faces[12] = 6; m_Faces[13] = 4; m_Faces[14] = 0; // X = min
54  m_Faces[15] = 1; m_Faces[16] = 3; m_Faces[17] = 7; m_Faces[18] = 5; m_Faces[19] = 1; // X = max
55 
56  m_Faces[20] = 0; m_Faces[21] = 1; m_Faces[22] = 5; m_Faces[23] = 4; m_Faces[24] = 0; // Y = min
57  m_Faces[25] = 2; m_Faces[26] = 3; m_Faces[27] = 7; m_Faces[28] = 6; m_Faces[29] = 2; // Y = max
58 }
59 
60 
61 ///////////////////////////////////////////////////////////////////////////////
62 // Calculate bounds of this brush
64 {
65  result.SetEmpty();
66 
67  for(size_t i = 0; i < m_Vertices.size(); ++i)
68  result += m_Vertices[i];
69 }
70 
71 
72 ///////////////////////////////////////////////////////////////////////////////
73 // Cut the brush according to a given plane
74 
75 /// Holds information about what happens to a single vertex in a brush during a slicing operation.
77 {
78  float planeDist; ///< Signed distance from this vertex to the slicing plane.
79  size_t resIdx; ///< Index of this vertex in the resulting brush (or NO_VERTEX if cut away)
80 };
81 
82 /// Holds information about a newly introduced vertex on an edge in a brush as the result of a slicing operation.
84 {
85  /// Indices of adjacent edge vertices in original brush
86  size_t edgeIdx1, edgeIdx2;
87  /// Index of newly introduced vertex in resulting brush
88  size_t resIdx;
89 
90  /**
91  * Index into SliceOpInfo.nvInfo; hold the indices of this new vertex's direct neighbours in the slicing plane face,
92  * with no consistent winding direction around the face for either field (e.g., the neighb1 of X can point back to
93  * X with either its neighb1 or neighb2).
94  */
96 };
97 
98 /// Holds support information during a CBrush/CPlane slicing operation.
100 {
102  const CBrush* original;
103 
104  /**
105  * Holds information about what happens to each vertex in the original brush after the slice operation.
106  * Same size as m_Vertices of the brush getting sliced.
107  */
108  std::vector<SliceOpVertexInfo> ovInfo;
109 
110  /// Holds information about newly inserted vertices during a slice operation.
111  std::vector<SliceOpNewVertexInfo> nvInfo;
112 
113  /**
114  * Indices into nvInfo; during the execution of the slicing algorithm, holds the previously inserted new vertex on
115  * one of the edges of the face that's currently being evaluated for slice points, or NO_VERTEX if no such vertex
116  * exists.
117  */
119 };
120 
122 {
123  /**
124  * Creates a new vertex between the given two vertices (indexed into the original brush).
125  * Returns the index of the new vertex in the resulting brush.
126  */
127  static size_t SliceNewVertex(SliceOpInfo& sliceInfo, size_t v1, size_t v2);
128 };
129 
130 size_t CBrush::Helper::SliceNewVertex(SliceOpInfo& sliceOp, size_t edgeIdx1, size_t edgeIdx2)
131 {
132  // check if a new vertex has already been inserted on this edge
133  size_t idx;
134  for(idx = 0; idx < sliceOp.nvInfo.size(); ++idx)
135  {
136  if ((sliceOp.nvInfo[idx].edgeIdx1 == edgeIdx1 && sliceOp.nvInfo[idx].edgeIdx2 == edgeIdx2) ||
137  (sliceOp.nvInfo[idx].edgeIdx1 == edgeIdx2 && sliceOp.nvInfo[idx].edgeIdx2 == edgeIdx1))
138  break;
139  }
140 
141  if (idx >= sliceOp.nvInfo.size())
142  {
143  // no previously inserted new vertex found on this edge; insert a new one
145  CVector3D newPos;
146 
147  // interpolate between the two vertices based on their distance from the plane
148  float inv = 1.0 / (sliceOp.ovInfo[edgeIdx1].planeDist - sliceOp.ovInfo[edgeIdx2].planeDist);
149  newPos = sliceOp.original->m_Vertices[edgeIdx2] * ( sliceOp.ovInfo[edgeIdx1].planeDist * inv) +
150  sliceOp.original->m_Vertices[edgeIdx1] * (-sliceOp.ovInfo[edgeIdx2].planeDist * inv);
151 
152  nvi.edgeIdx1 = edgeIdx1;
153  nvi.edgeIdx2 = edgeIdx2;
154  nvi.resIdx = sliceOp.result->m_Vertices.size();
155  nvi.neighbIdx1 = NO_VERTEX;
156  nvi.neighbIdx2 = NO_VERTEX;
157 
158  sliceOp.result->m_Vertices.push_back(newPos);
159  sliceOp.nvInfo.push_back(nvi);
160  }
161 
162  // at this point, 'idx' is the index into nvInfo of the vertex inserted onto the edge
163 
164  if (sliceOp.thisFaceNewVertexIdx != NO_VERTEX)
165  {
166  // a vertex has been previously inserted onto another edge of this face; link them together as neighbours
167  // (using whichever one of the neighbIdx1 or -2 links is still available)
168 
169  if (sliceOp.nvInfo[sliceOp.thisFaceNewVertexIdx].neighbIdx1 == NO_VERTEX)
170  sliceOp.nvInfo[sliceOp.thisFaceNewVertexIdx].neighbIdx1 = idx;
171  else
172  sliceOp.nvInfo[sliceOp.thisFaceNewVertexIdx].neighbIdx2 = idx;
173 
174  if (sliceOp.nvInfo[idx].neighbIdx1 == NO_VERTEX)
175  sliceOp.nvInfo[idx].neighbIdx1 = sliceOp.thisFaceNewVertexIdx;
176  else
177  sliceOp.nvInfo[idx].neighbIdx2 = sliceOp.thisFaceNewVertexIdx;
178 
179  // a plane should slice a face only in two locations, so reset for the next face
181  }
182  else
183  {
184  // store the index of the inserted vertex on this edge, so that we can retrieve it when the plane slices
185  // this face again in another edge
186  sliceOp.thisFaceNewVertexIdx = idx;
187  }
188 
189  return sliceOp.nvInfo[idx].resIdx;
190 }
191 
192 void CBrush::Slice(const CPlane& plane, CBrush& result) const
193 {
194  ENSURE(&result != this);
195 
196  SliceOpInfo sliceOp;
197 
198  sliceOp.original = this;
199  sliceOp.result = &result;
201  sliceOp.ovInfo.resize(m_Vertices.size());
202  sliceOp.nvInfo.reserve(m_Vertices.size() / 2);
203 
204  result.m_Vertices.resize(0); // clear any left-overs
205  result.m_Faces.resize(0);
206  result.m_Vertices.reserve(m_Vertices.size() + 2);
207  result.m_Faces.reserve(m_Faces.size() + 5);
208 
209  // Copy vertices that weren't sliced away by the plane to the resulting brush.
210  for(size_t i = 0; i < m_Vertices.size(); ++i)
211  {
212  const CVector3D& vtx = m_Vertices[i]; // current vertex
213  SliceOpVertexInfo& vtxInfo = sliceOp.ovInfo[i]; // slicing operation info about current vertex
214 
215  vtxInfo.planeDist = plane.DistanceToPlane(vtx);
216  if (vtxInfo.planeDist >= 0.0)
217  {
218  // positive side of the plane; not sliced away
219  vtxInfo.resIdx = result.m_Vertices.size();
220  result.m_Vertices.push_back(vtx);
221  }
222  else
223  {
224  // other side of the plane; sliced away
225  vtxInfo.resIdx = NO_VERTEX;
226  }
227  }
228 
229  // Transfer faces. (Recall how faces are specified; see CBrush::m_Faces). The idea is to examine each face separately,
230  // and see where its edges cross the slicing plane (meaning that exactly one of the vertices of that edge was cut away).
231  // On those edges, new vertices are introduced where the edge intersects the plane, and the resulting brush's m_Faces
232  // array is updated to refer to the newly inserted vertices instead of the original one that got cut away.
233 
234  size_t currentFaceStartIdx = NO_VERTEX; // index of the first vertex of the current face in the original brush
235  size_t resultFaceStartIdx = NO_VERTEX; // index of the first vertex of the current face in the resulting brush
236 
237  for(size_t i = 0; i < m_Faces.size(); ++i)
238  {
239  if (currentFaceStartIdx == NO_VERTEX)
240  {
241  // starting a new face
243 
244  currentFaceStartIdx = m_Faces[i];
245  resultFaceStartIdx = result.m_Faces.size();
246  continue;
247  }
248 
249  size_t prevIdx = m_Faces[i-1]; // index of previous vertex in this face list
250  size_t curIdx = m_Faces[i]; // index of current vertex in this face list
251 
252  if (sliceOp.ovInfo[prevIdx].resIdx == NO_VERTEX)
253  {
254  // previous face vertex got sliced away by the plane; see if the edge (prev,current) crosses the slicing plane
255  if (sliceOp.ovInfo[curIdx].resIdx != NO_VERTEX)
256  {
257  // re-entering the front side of the plane; insert vertex on intersection of plane and (prev,current) edge
258  result.m_Faces.push_back(Helper::SliceNewVertex(sliceOp, prevIdx, curIdx));
259  result.m_Faces.push_back(sliceOp.ovInfo[curIdx].resIdx);
260  }
261  }
262  else
263  {
264  // previous face vertex didn't get sliced away; see if the edge (prev,current) crosses the slicing plane
265  if (sliceOp.ovInfo[curIdx].resIdx != NO_VERTEX)
266  {
267  // perfectly normal edge; doesn't cross the plane
268  result.m_Faces.push_back(sliceOp.ovInfo[curIdx].resIdx);
269  }
270  else
271  {
272  // leaving the front side of the plane; insert vertex on intersection of plane and edge (prev, current)
273  result.m_Faces.push_back(Helper::SliceNewVertex(sliceOp, prevIdx, curIdx));
274  }
275  }
276 
277  // if we're back at the first vertex of the current face, then we've completed the face
278  if (curIdx == currentFaceStartIdx)
279  {
280  // close the index loop
281  if (result.m_Faces.size() > resultFaceStartIdx)
282  result.m_Faces.push_back(result.m_Faces[resultFaceStartIdx]);
283 
284  currentFaceStartIdx = NO_VERTEX; // start a new face
285  }
286  }
287 
288  ENSURE(currentFaceStartIdx == NO_VERTEX);
289 
290  // Create the face that lies in the slicing plane. Remember, all the intersections of the slicing plane with face
291  // edges of the brush have been stored in sliceOp.nvInfo by the SliceNewVertex function, and refer to their direct
292  // neighbours in the slicing plane face using the neighbIdx1 and neighbIdx2 fields (in no consistent winding order).
293 
294  if (sliceOp.nvInfo.size())
295  {
296  // push the starting vertex
297  result.m_Faces.push_back(sliceOp.nvInfo[0].resIdx);
298 
299  // At this point, there is no consistent winding order in the neighbX fields, so at each vertex we need to figure
300  // out whether neighb1 or neighb2 points 'onwards' along the face, according to an initially chosen winding direction.
301  // (or, equivalently, which one points back to the one we were just at). At each vertex, we then set neighb1 to be the
302  // one to point onwards, deleting any pointers which we no longer need to complete the trace.
303 
304  size_t idx;
305  size_t prev = 0;
306 
307  idx = sliceOp.nvInfo[0].neighbIdx2; // pick arbitrary starting direction
308  sliceOp.nvInfo[0].neighbIdx2 = NO_VERTEX;
309 
310  while(idx != 0)
311  {
312  ENSURE(idx < sliceOp.nvInfo.size());
313  if (idx >= sliceOp.nvInfo.size())
314  break;
315 
316  if (sliceOp.nvInfo[idx].neighbIdx1 == prev)
317  {
318  // neighb1 is pointing the wrong way; we want to normalize it to point onwards in the direction
319  // we initially chose, so swap it with neighb2 and delete neighb2 (no longer needed)
320  sliceOp.nvInfo[idx].neighbIdx1 = sliceOp.nvInfo[idx].neighbIdx2;
321  sliceOp.nvInfo[idx].neighbIdx2 = NO_VERTEX;
322  }
323  else
324  {
325  // neighb1 isn't pointing to the previous vertex, so neighb2 must be (otherwise a pair of vertices failed to
326  // get paired properly during face/plane slicing).
327  ENSURE(sliceOp.nvInfo[idx].neighbIdx2 == prev);
328  sliceOp.nvInfo[idx].neighbIdx2 = NO_VERTEX;
329  }
330 
331  result.m_Faces.push_back(sliceOp.nvInfo[idx].resIdx);
332 
333  // move to next vertex; neighb1 has been normalized to point onward
334  prev = idx;
335  idx = sliceOp.nvInfo[idx].neighbIdx1;
336  sliceOp.nvInfo[prev].neighbIdx1 = NO_VERTEX; // no longer needed, we've moved on
337  }
338 
339  // push starting vertex again to close the shape
340  result.m_Faces.push_back(sliceOp.nvInfo[0].resIdx);
341  }
342 }
343 
344 
345 
346 ///////////////////////////////////////////////////////////////////////////////
347 // Intersect with frustum by repeated slicing
348 void CBrush::Intersect(const CFrustum& frustum, CBrush& result) const
349 {
350  ENSURE(&result != this);
351 
352  if (!frustum.GetNumPlanes())
353  {
354  result = *this;
355  return;
356  }
357 
358  CBrush buf;
359  const CBrush* prev = this;
360  CBrush* next;
361 
362  // Repeatedly slice this brush with each plane of the frustum, alternating between 'result' and 'buf' to
363  // save intermediate results. Set up the starting brush so that the final version always ends up in 'result'.
364 
365  if (frustum.GetNumPlanes() & 1)
366  next = &result;
367  else
368  next = &buf;
369 
370  for(size_t i = 0; i < frustum.GetNumPlanes(); ++i)
371  {
372  prev->Slice(frustum[i], *next);
373  prev = next;
374  if (prev == &buf)
375  next = &result;
376  else
377  next = &buf;
378  }
379 
380  ENSURE(prev == &result);
381 }
382 std::vector<CVector3D> CBrush::GetVertices() const
383 {
384  return m_Vertices;
385 }
386 
387 void CBrush::GetFaces(std::vector<std::vector<size_t> >& out) const
388 {
389  // split the back-to-back faces into separate face vectors, so that they're in a
390  // user-friendlier format than the back-to-back vertex index array
391  // i.e. split 'x--xy------yz----z' into 'x--x', 'y-------y', 'z---z'
392 
393  size_t faceStartIdx = 0;
394  while (faceStartIdx < m_Faces.size())
395  {
396  // start new face
397  std::vector<size_t> singleFace;
398  singleFace.push_back(m_Faces[faceStartIdx]);
399 
400  // step over all the values in the face until we hit the starting value again (which closes the face)
401  size_t j = faceStartIdx + 1;
402  while (j < m_Faces.size() && m_Faces[j] != m_Faces[faceStartIdx])
403  {
404  singleFace.push_back(m_Faces[j]);
405  j++;
406  }
407 
408  // each face must be closed by the same value that started it
409  ENSURE(m_Faces[faceStartIdx] == m_Faces[j]);
410 
411  singleFace.push_back(m_Faces[j]);
412  out.push_back(singleFace);
413 
414  faceStartIdx = j + 1;
415  }
416 }
CBrush()
Definition: Brush.h:40
void GetFaces(std::vector< std::vector< size_t > > &out) const
Writes a vector of the faces in this brush to out.
Definition: Brush.cpp:387
void Intersect(const CFrustum &frustum, CBrush &result) const
Intersect: Intersect the brush with the given frustum.
Definition: Brush.cpp:348
size_t neighbIdx1
Index into SliceOpInfo.nvInfo; hold the indices of this new vertex&#39;s direct neighbours in the slicing...
Definition: Brush.cpp:95
static void out(const wchar_t *fmt,...)
Definition: wdbg_sym.cpp:419
std::vector< CVector3D > GetVertices() const
Returns a copy of the vertices in this brush.
Definition: Brush.cpp:382
size_t thisFaceNewVertexIdx
Indices into nvInfo; during the execution of the slicing algorithm, holds the previously inserted new...
Definition: Brush.cpp:118
static const size_t NO_VERTEX
Definition: Brush.h:97
std::vector< SliceOpNewVertexInfo > nvInfo
Holds information about newly inserted vertices during a slice operation.
Definition: Brush.cpp:111
#define ENSURE(expr)
ensure the expression &lt;expr&gt; evaluates to non-zero.
Definition: debug.h:282
float planeDist
Signed distance from this vertex to the slicing plane.
Definition: Brush.cpp:78
Class CBrush: Represents a convex object, supports some CSG operations.
Definition: Brush.h:35
Holds information about what happens to a single vertex in a brush during a slicing operation...
Definition: Brush.cpp:76
float DistanceToPlane(const CVector3D &point) const
Definition: Plane.cpp:99
size_t resIdx
Index of this vertex in the resulting brush (or NO_VERTEX if cut away)
Definition: Brush.cpp:79
const CBrush * original
Definition: Brush.cpp:102
size_t edgeIdx1
Indices of adjacent edge vertices in original brush.
Definition: Brush.cpp:86
static size_t SliceNewVertex(SliceOpInfo &sliceInfo, size_t v1, size_t v2)
Creates a new vertex between the given two vertices (indexed into the original brush).
Definition: Brush.cpp:130
Holds support information during a CBrush/CPlane slicing operation.
Definition: Brush.cpp:99
void Slice(const CPlane &plane, CBrush &result) const
Slice: Cut the object along the given plane, resulting in a smaller (or even empty) brush representin...
Definition: Brush.cpp:192
Vertices m_Vertices
Collection of unique vertices that make up this shape.
Definition: Brush.h:103
FaceIndices m_Faces
Holds the face definitions of this brush.
Definition: Brush.h:110
void Bounds(CBoundingBoxAligned &result) const
Bounds: Calculate the axis-aligned bounding box for this brush.
Definition: Brush.cpp:63
CBrush * result
Definition: Brush.cpp:101
size_t GetNumPlanes() const
Definition: Frustum.h:49
Definition: Plane.h:38
std::vector< SliceOpVertexInfo > ovInfo
Holds information about what happens to each vertex in the original brush after the slice operation...
Definition: Brush.cpp:108
size_t resIdx
Index of newly introduced vertex in resulting brush.
Definition: Brush.cpp:88
Holds information about a newly introduced vertex on an edge in a brush as the result of a slicing op...
Definition: Brush.cpp:83