Ticket #1027: 1027_atlas_brush_04mar12.patch
| File 1027_atlas_brush_04mar12.patch, 32.4 KB (added by vts, 15 months ago) |
|---|
-
source/maths/BoundingBoxAligned.cpp
diff --git a/source/maths/BoundingBoxAligned.cpp b/source/maths/BoundingBoxAligned.cpp index 2e0e706..80c25ef 100644
a b 1 /* Copyright (C) 201 1Wildfire Games.1 /* Copyright (C) 2012 Wildfire Games. 2 2 * This file is part of 0 A.D. 3 3 * 4 4 * 0 A.D. is free software: you can redistribute it and/or modify … … void CBoundingBoxAligned::Transform(const CMatrix3D& transform, CBoundingBoxOrie 217 217 // Intersect with the given frustum in a conservative manner 218 218 void CBoundingBoxAligned::IntersectFrustumConservative(const CFrustum& frustum) 219 219 { 220 // if this bound is empty, it cannot be meaningfully intersected with a frustum 221 // see http://trac.wildfiregames.com/ticket/1027 222 ENSURE(!IsEmpty()); 223 220 224 CBrush brush(*this); 221 225 CBrush buf; 222 226 -
source/maths/BoundingBoxAligned.h
diff --git a/source/maths/BoundingBoxAligned.h b/source/maths/BoundingBoxAligned.h index 43f0a5b..df83af6 100644
a b public: 126 126 * 127 127 * @note While not in the spirit of this function's purpose, a no-op would be a correct 128 128 * implementation of this function. 129 * @note Must not be called if this bound is empty 129 130 * 130 131 * @param frustum the frustum to intersect with 131 132 */ -
source/maths/Brush.cpp
diff --git a/source/maths/Brush.cpp b/source/maths/Brush.cpp index d6909cf..bd06280 100644
a b 1 /* Copyright (C) 20 09Wildfire Games.1 /* Copyright (C) 2012 Wildfire Games. 2 2 * This file is part of 0 A.D. 3 3 * 4 4 * 0 A.D. is free software: you can redistribute it and/or modify … … CBrush::CBrush(const CBoundingBoxAligned& bounds) 38 38 39 39 for(size_t i = 0; i < 8; ++i) 40 40 { 41 m_Vertices[i][0] = bounds[(i & 1) ? 1 : 0][0]; 42 m_Vertices[i][1] = bounds[(i & 2) ? 1 : 0][1]; 43 m_Vertices[i][2] = bounds[(i & 4) ? 1 : 0][2]; 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 44 } 45 45 46 // construct cube face indices, 5 vertex indices per face (start vertex included twice) 47 46 48 m_Faces.resize(30); 47 49 48 50 m_Faces[0] = 0; m_Faces[1] = 1; m_Faces[2] = 3; m_Faces[3] = 2; m_Faces[4] = 0; // Z = min … … void CBrush::Bounds(CBoundingBoxAligned& result) const 69 71 70 72 /////////////////////////////////////////////////////////////////////////////// 71 73 // Cut the brush according to a given plane 72 struct SliceVertexInfo {73 float d; // distance74 size_t res; // index in result brush (or no_vertex if cut away)75 };76 74 77 struct NewVertexInfo { 78 size_t v1, v2; // adjacent vertices in original brush 79 size_t res; // index in result brush 75 /// Holds information about what happens to a single vertex in a brush during a slicing operation. 76 struct SliceOpVertexInfo 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 }; 80 81 81 size_t neighb1, neighb2; // index into newv 82 /// Holds information about a newly introduced vertex on an edge in a brush as the result of a slicing operation. 83 struct SliceOpNewVertexInfo 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 /// Index into SliceOpInfo.nvInfo; hold the indices of this new vertex's direct neighbours in the slicing plane face, 90 /// with no consistent winding direction around the face for either field (e.g., the neighb1 of X can point back to 91 /// X with either its neighb1 or neighb2). 92 size_t neighbIdx1, neighbIdx2; 82 93 }; 83 94 84 struct SliceInfo { 85 std::vector<SliceVertexInfo> v; 86 std::vector<NewVertexInfo> newv; 87 size_t thisFaceNewVertex; // index into newv 88 const CBrush* original; 95 /// Holds support information during a CBrush/CPlane slicing operation. 96 struct SliceOpInfo 97 { 89 98 CBrush* result; 99 const CBrush* original; 100 101 /// Holds information about what happens to each vertex in the original brush after the slice operation. 102 /// Same size as m_Vertices of the brush getting sliced. 103 std::vector<SliceOpVertexInfo> ovInfo; 104 /// Holds information about newly inserted vertices during a slice operation. 105 std::vector<SliceOpNewVertexInfo> nvInfo; 106 /// Indices into nvInfo; during the execution of the slicing algorithm, holds the previously inserted new vertex on 107 /// one of the edges of the face that's currently being evaluated for slice points, or NO_VERTEX if no such vertex 108 /// exists. 109 size_t thisFaceNewVertexIdx; 90 110 }; 91 111 92 112 struct CBrush::Helper 93 113 { 94 static size_t SliceNewVertex(SliceInfo& si, size_t v1, size_t v2); 114 /// Creates a new vertex between the given two vertices (indexed into the original brush). 115 /// Returns the index of the new vertex in the resulting brush. 116 static size_t SliceNewVertex(SliceOpInfo& sliceInfo, size_t v1, size_t v2); 95 117 }; 96 118 97 // create a new vertex between the given two vertices (index into original brush) 98 // returns the index of the new vertex in the resulting brush 99 size_t CBrush::Helper::SliceNewVertex(SliceInfo& si, size_t v1, size_t v2) 119 size_t CBrush::Helper::SliceNewVertex(SliceOpInfo& sliceOp, size_t edgeIdx1, size_t edgeIdx2) 100 120 { 121 // check if a new vertex has already been inserted on this edge 101 122 size_t idx; 102 103 for(idx = 0; idx < si.newv.size(); ++idx) 123 for(idx = 0; idx < sliceOp.nvInfo.size(); ++idx) 104 124 { 105 if ((s i.newv[idx].v1 == v1 && si.newv[idx].v2 == v2) ||106 (s i.newv[idx].v1 == v2 && si.newv[idx].v2 == v1))125 if ((sliceOp.nvInfo[idx].edgeIdx1 == edgeIdx1 && sliceOp.nvInfo[idx].edgeIdx2 == edgeIdx2) || 126 (sliceOp.nvInfo[idx].edgeIdx1 == edgeIdx2 && sliceOp.nvInfo[idx].edgeIdx2 == edgeIdx1)) 107 127 break; 108 128 } 109 129 110 if (idx >= s i.newv.size())130 if (idx >= sliceOp.nvInfo.size()) 111 131 { 112 NewVertexInfo nvi; 113 CVector3D newpos; 114 float inv = 1.0 / (si.v[v1].d - si.v[v2].d); 115 116 newpos = si.original->m_Vertices[v2]*(si.v[v1].d*inv) + 117 si.original->m_Vertices[v1]*(-si.v[v2].d*inv); 118 119 nvi.v1 = v1; 120 nvi.v2 = v2; 121 nvi.res = si.result->m_Vertices.size(); 122 nvi.neighb1 = no_vertex; 123 nvi.neighb2 = no_vertex; 124 si.result->m_Vertices.push_back(newpos); 125 si.newv.push_back(nvi); 132 // no previously inserted new vertex found on this edge; insert a new one 133 SliceOpNewVertexInfo nvi; 134 CVector3D newPos; 135 136 // interpolate between the two vertices based on their distance from the plane 137 float inv = 1.0 / (sliceOp.ovInfo[edgeIdx1].planeDist - sliceOp.ovInfo[edgeIdx2].planeDist); 138 newPos = sliceOp.original->m_Vertices[edgeIdx2] * ( sliceOp.ovInfo[edgeIdx1].planeDist * inv) + 139 sliceOp.original->m_Vertices[edgeIdx1] * (-sliceOp.ovInfo[edgeIdx2].planeDist * inv); 140 141 nvi.edgeIdx1 = edgeIdx1; 142 nvi.edgeIdx2 = edgeIdx2; 143 nvi.resIdx = sliceOp.result->m_Vertices.size(); 144 nvi.neighbIdx1 = NO_VERTEX; 145 nvi.neighbIdx2 = NO_VERTEX; 146 147 sliceOp.result->m_Vertices.push_back(newPos); 148 sliceOp.nvInfo.push_back(nvi); 126 149 } 127 150 128 if (si.thisFaceNewVertex != no_vertex) 151 // at this point, 'idx' is the index into nvInfo of the vertex inserted onto the edge 152 153 if (sliceOp.thisFaceNewVertexIdx != NO_VERTEX) 129 154 { 130 if (si.newv[si.thisFaceNewVertex].neighb1 == no_vertex) 131 si.newv[si.thisFaceNewVertex].neighb1 = idx; 155 // a vertex has been previously inserted onto another edge of this face; link them together as neighbours 156 // (using whichever one of the neighbIdx1 or -2 links is still available) 157 158 if (sliceOp.nvInfo[sliceOp.thisFaceNewVertexIdx].neighbIdx1 == NO_VERTEX) 159 sliceOp.nvInfo[sliceOp.thisFaceNewVertexIdx].neighbIdx1 = idx; 132 160 else 133 s i.newv[si.thisFaceNewVertex].neighb2 = idx;161 sliceOp.nvInfo[sliceOp.thisFaceNewVertexIdx].neighbIdx2 = idx; 134 162 135 if (s i.newv[idx].neighb1 == no_vertex)136 s i.newv[idx].neighb1 = si.thisFaceNewVertex;163 if (sliceOp.nvInfo[idx].neighbIdx1 == NO_VERTEX) 164 sliceOp.nvInfo[idx].neighbIdx1 = sliceOp.thisFaceNewVertexIdx; 137 165 else 138 s i.newv[idx].neighb2 = si.thisFaceNewVertex;166 sliceOp.nvInfo[idx].neighbIdx2 = sliceOp.thisFaceNewVertexIdx; 139 167 140 si.thisFaceNewVertex = no_vertex; 168 // a plane should slice a face only in two locations, so reset for the next face 169 sliceOp.thisFaceNewVertexIdx = NO_VERTEX; 141 170 } 142 171 else 143 172 { 144 si.thisFaceNewVertex = idx; 173 // store the index of the inserted vertex on this edge, so that we can retrieve it when the plane slices 174 // this face again in another edge 175 sliceOp.thisFaceNewVertexIdx = idx; 145 176 } 146 177 147 return s i.newv[idx].res;178 return sliceOp.nvInfo[idx].resIdx; 148 179 } 149 180 150 181 void CBrush::Slice(const CPlane& plane, CBrush& result) const 151 182 { 152 183 ENSURE(&result != this); 153 184 154 Slice Info si;185 SliceOpInfo sliceOp; 155 186 156 si.original = this; 157 si.result = &result; 158 si.thisFaceNewVertex = no_vertex; 159 si.newv.reserve(m_Vertices.size() / 2); 187 sliceOp.original = this; 188 sliceOp.result = &result; 189 sliceOp.thisFaceNewVertexIdx = NO_VERTEX; 190 sliceOp.ovInfo.resize(m_Vertices.size()); 191 sliceOp.nvInfo.reserve(m_Vertices.size() / 2); 160 192 161 193 result.m_Vertices.resize(0); // clear any left-overs 162 194 result.m_Faces.resize(0); 163 195 result.m_Vertices.reserve(m_Vertices.size() + 2); 164 196 result.m_Faces.reserve(m_Faces.size() + 5); 165 197 166 // Classify and copy vertices 167 si.v.resize(m_Vertices.size()); 168 198 // Copy vertices that weren't sliced away by the plane to the resulting brush. 169 199 for(size_t i = 0; i < m_Vertices.size(); ++i) 170 200 { 171 si.v[i].d = plane.DistanceToPlane(m_Vertices[i]); 172 if (si.v[i].d >= 0.0) 201 const CVector3D& vtx = m_Vertices[i]; // current vertex 202 SliceOpVertexInfo& vtxInfo = sliceOp.ovInfo[i]; // slicing operation info about current vertex 203 204 vtxInfo.planeDist = plane.DistanceToPlane(vtx); 205 if (vtxInfo.planeDist >= 0.0) 173 206 { 174 si.v[i].res = result.m_Vertices.size(); 175 result.m_Vertices.push_back(m_Vertices[i]); 207 // positive side of the plane; not sliced away 208 vtxInfo.resIdx = result.m_Vertices.size(); 209 result.m_Vertices.push_back(vtx); 176 210 } 177 211 else 178 212 { 179 si.v[i].res = no_vertex; 213 // other side of the plane; sliced away 214 vtxInfo.resIdx = NO_VERTEX; 180 215 } 181 216 } 182 217 183 // Transfer faces 184 size_t firstInFace = no_vertex; // in original brush 185 size_t startInResultFaceArray = ~0u; 218 // Transfer faces. (Recall how faces are specified; see CBrush::m_Faces). The idea is to examine each face separately, 219 // and see where its edges cross the slicing plane (meaning that exactly one of the vertices of that edge was cut away). 220 // On those edges, new vertices are introduced where the edge intersects the plane, and the resulting brush's m_Faces 221 // array is updated to refer to the newly inserted vertices instead of the original one that got cut away. 222 223 size_t currentFaceStartIdx = NO_VERTEX; // index of the first vertex of the current face in the original brush 224 size_t resultFaceStartIdx = NO_VERTEX; // index of the first vertex of the current face in the resulting brush 186 225 187 226 for(size_t i = 0; i < m_Faces.size(); ++i) 188 227 { 189 if ( firstInFace == no_vertex)228 if (currentFaceStartIdx == NO_VERTEX) 190 229 { 191 ENSURE(si.thisFaceNewVertex == no_vertex); 230 // starting a new face 231 ENSURE(sliceOp.thisFaceNewVertexIdx == NO_VERTEX); 192 232 193 firstInFace= m_Faces[i];194 startInResultFaceArray= result.m_Faces.size();233 currentFaceStartIdx = m_Faces[i]; 234 resultFaceStartIdx = result.m_Faces.size(); 195 235 continue; 196 236 } 197 237 198 size_t prev = m_Faces[i-1];199 size_t cur = m_Faces[i];238 size_t prevIdx = m_Faces[i-1]; // index of previous vertex in this face list 239 size_t curIdx = m_Faces[i]; // index of current vertex in this face list 200 240 201 if (s i.v[prev].res == no_vertex)241 if (sliceOp.ovInfo[prevIdx].resIdx == NO_VERTEX) 202 242 { 203 if (si.v[cur].res != no_vertex) 243 // previous face vertex got sliced away by the plane; see if the edge (prev,current) crosses the slicing plane 244 if (sliceOp.ovInfo[curIdx].resIdx != NO_VERTEX) 204 245 { 205 // re-entering the front side of the plane 206 result.m_Faces.push_back(Helper::SliceNewVertex(s i, prev, cur));207 result.m_Faces.push_back(s i.v[cur].res);246 // re-entering the front side of the plane; insert vertex on intersection of plane and (prev,current) edge 247 result.m_Faces.push_back(Helper::SliceNewVertex(sliceOp, prevIdx, curIdx)); 248 result.m_Faces.push_back(sliceOp.ovInfo[curIdx].resIdx); 208 249 } 209 250 } 210 251 else 211 252 { 212 if (si.v[cur].res != no_vertex) 253 // previous face vertex didn't get sliced away; see if the edge (prev,current) crosses the slicing plane 254 if (sliceOp.ovInfo[curIdx].resIdx != NO_VERTEX) 213 255 { 214 // perfectly normal edge 215 result.m_Faces.push_back(s i.v[cur].res);256 // perfectly normal edge; doesn't cross the plane 257 result.m_Faces.push_back(sliceOp.ovInfo[curIdx].resIdx); 216 258 } 217 259 else 218 260 { 219 // leaving the front side of the plane 220 result.m_Faces.push_back(Helper::SliceNewVertex(s i, prev, cur));261 // leaving the front side of the plane; insert vertex on intersection of plane and edge (prev, current) 262 result.m_Faces.push_back(Helper::SliceNewVertex(sliceOp, prevIdx, curIdx)); 221 263 } 222 264 } 223 265 224 if (cur == firstInFace) 266 // if we're back at the first vertex of the current face, then we've completed the face 267 if (curIdx == currentFaceStartIdx) 225 268 { 226 if (result.m_Faces.size() > startInResultFaceArray) 227 result.m_Faces.push_back(result.m_Faces[startInResultFaceArray]); 228 firstInFace = no_vertex; // start a new face 269 // close the index loop 270 if (result.m_Faces.size() > resultFaceStartIdx) 271 result.m_Faces.push_back(result.m_Faces[resultFaceStartIdx]); 272 273 currentFaceStartIdx = NO_VERTEX; // start a new face 229 274 } 230 275 } 231 276 232 ENSURE(firstInFace == no_vertex); 277 ENSURE(currentFaceStartIdx == NO_VERTEX); 278 279 // Create the face that lies in the slicing plane. Remember, all the intersections of the slicing plane with face 280 // edges of the brush have been stored in sliceOp.nvInfo by the SliceNewVertex function, and refer to their direct 281 // neighbours in the slicing plane face using the neighbIdx1 and neighbIdx2 fields (in no consistent winding order). 233 282 234 // Create the face that lies in the slicing plane 235 if (si.newv.size()) 283 if (sliceOp.nvInfo.size()) 236 284 { 237 size_t prev = 0; 285 // push the starting vertex 286 result.m_Faces.push_back(sliceOp.nvInfo[0].resIdx); 287 288 // At this point, there is no consistent winding order in the neighbX fields, so at each vertex we need to figure 289 // out whether neighb1 or neighb2 points 'onwards' along the face, according to an initially chosen winding direction. 290 // (or, equivalently, which one points back to the one we were just at). At each vertex, we then set neighb1 to be the 291 // one to point onwards, deleting any pointers which we no longer need to complete the trace. 292 238 293 size_t idx; 294 size_t prev = 0; 239 295 240 result.m_Faces.push_back(si.newv[0].res); 241 idx = si.newv[0].neighb2; 242 si.newv[0].neighb2 = no_vertex; 296 idx = sliceOp.nvInfo[0].neighbIdx2; // pick arbitrary starting direction 297 sliceOp.nvInfo[0].neighbIdx2 = NO_VERTEX; 243 298 244 299 while(idx != 0) 245 300 { 246 ENSURE(idx < s i.newv.size());247 if (idx >= s i.newv.size())301 ENSURE(idx < sliceOp.nvInfo.size()); 302 if (idx >= sliceOp.nvInfo.size()) 248 303 break; 249 304 250 if (s i.newv[idx].neighb1 == prev)305 if (sliceOp.nvInfo[idx].neighbIdx1 == prev) 251 306 { 252 si.newv[idx].neighb1 = si.newv[idx].neighb2; 253 si.newv[idx].neighb2 = no_vertex; 307 // neighb1 is pointing the wrong way; we want to normalize it to point onwards in the direction 308 // we initially chose, so swap it with neighb2 and delete neighb2 (no longer needed) 309 sliceOp.nvInfo[idx].neighbIdx1 = sliceOp.nvInfo[idx].neighbIdx2; 310 sliceOp.nvInfo[idx].neighbIdx2 = NO_VERTEX; 254 311 } 255 312 else 256 313 { 257 ENSURE(si.newv[idx].neighb2 == prev); 258 259 si.newv[idx].neighb2 = no_vertex; 314 // neighb1 isn't pointing to the previous vertex, so neighb2 must be (otherwise a pair of vertices failed to 315 // get paired properly during face/plane slicing). 316 ENSURE(sliceOp.nvInfo[idx].neighbIdx2 == prev); 317 sliceOp.nvInfo[idx].neighbIdx2 = NO_VERTEX; 260 318 } 261 319 262 result.m_Faces.push_back(s i.newv[idx].res);320 result.m_Faces.push_back(sliceOp.nvInfo[idx].resIdx); 263 321 322 // move to next vertex; neighb1 has been normalized to point onward 264 323 prev = idx; 265 idx = s i.newv[idx].neighb1;266 s i.newv[prev].neighb1 = no_vertex;324 idx = sliceOp.nvInfo[idx].neighbIdx1; 325 sliceOp.nvInfo[prev].neighbIdx1 = NO_VERTEX; // no longer needed, we've moved on 267 326 } 268 327 269 result.m_Faces.push_back(si.newv[0].res); 328 // push starting vertex again to close the shape 329 result.m_Faces.push_back(sliceOp.nvInfo[0].resIdx); 270 330 } 271 331 } 272 332 273 333 334 274 335 /////////////////////////////////////////////////////////////////////////////// 275 336 // Intersect with frustum by repeated slicing 276 337 void CBrush::Intersect(const CFrustum& frustum, CBrush& result) const … … void CBrush::Intersect(const CFrustum& frustum, CBrush& result) const 287 348 const CBrush* prev = this; 288 349 CBrush* next; 289 350 351 // Repeatedly slice this brush with each plane of the frustum, alternating between 'result' and 'buf' to 352 // save intermediate results. Set up the starting brush so that the final version always ends up in 'result'. 353 290 354 if (frustum.GetNumPlanes() & 1) 291 355 next = &result; 292 356 else … … void CBrush::Intersect(const CFrustum& frustum, CBrush& result) const 304 368 305 369 ENSURE(prev == &result); 306 370 } 371 No newline at end of file 372 373 std::vector<CVector3D> CBrush::GetVertices() const 374 { 375 return m_Vertices; 376 } 377 378 void CBrush::GetFaces(std::vector<std::vector<size_t> >& out) const 379 { 380 // split the back-to-back faces into separate face vectors, so that they're in a 381 // user-friendlier format than the back-to-back vertex index array 382 // i.e. split 'x--xy------yz----z' into 'x--x', 'y-------y', 'z---z' 383 384 size_t faceStartIdx = 0; 385 while (faceStartIdx < m_Faces.size()) 386 { 387 // start new face 388 std::vector<size_t> singleFace; 389 singleFace.push_back(m_Faces[faceStartIdx]); 390 391 // step over all the values in the face until we hit the starting value again (which closes the face) 392 size_t j = faceStartIdx + 1; 393 while (j < m_Faces.size() && m_Faces[j] != m_Faces[faceStartIdx]) 394 { 395 singleFace.push_back(m_Faces[j]); 396 j++; 397 } 398 399 // each face must be closed by the same value that started it 400 ENSURE(m_Faces[faceStartIdx] == m_Faces[j]); 401 402 singleFace.push_back(m_Faces[j]); 403 out.push_back(singleFace); 404 405 faceStartIdx = j + 1; 406 } -
source/maths/Brush.h
+} \ No newline at end of file diff --git a/source/maths/Brush.h b/source/maths/Brush.h index bc87383..26996d8 100644
a b 1 /* Copyright (C) 20 09Wildfire Games.1 /* Copyright (C) 2012 Wildfire Games. 2 2 * This file is part of 0 A.D. 3 3 * 4 4 * 0 A.D. is free software: you can redistribute it and/or modify … … public: 59 59 void Bounds(CBoundingBoxAligned& result) const; 60 60 61 61 /** 62 * Slice: Cut the object along the given plane, resulting in a smaller (or even empty) 63 * brush representing the part of the object that lies in front of the plane. 62 * Slice: Cut the object along the given plane, resulting in a smaller (or even empty) brush representing 63 * the part of the object that lies in front of the plane (as defined by the positive direction of its 64 * normal vector). 64 65 * 65 66 * @param plane the slicing plane 66 67 * @param result the resulting brush is stored here … … public: 75 76 */ 76 77 void Intersect(const CFrustum& frustum, CBrush& result) const; 77 78 79 /** 80 * Returns a copy of the vertices in this brush. Intended for testing purposes; you should not need to use 81 * this method directly. 82 */ 83 std::vector<CVector3D> GetVertices() const; 84 85 /** 86 * Writes a vector of the faces in this brush to @p out. Each face is itself a vector, listing the vertex indices 87 * that make up the face, starting and ending with the same index. Intended for testing purposes; you should not 88 * need to use this method directly. 89 */ 90 void GetFaces(std::vector<std::vector<size_t> >& out) const; 91 78 92 private: 79 static const size_t no_vertex= ~0u;93 static const size_t NO_VERTEX = ~0u; 80 94 81 95 typedef std::vector<CVector3D> Vertices; 82 96 typedef std::vector<size_t> FaceIndices; 83 97 98 /// Collection of vertices that make up this shape. 84 99 Vertices m_Vertices; 100 101 /// Holds the face definitions of this brush. Each face is a sequence of indices into m_Vertices that starts and ends with 102 /// the same vertex index, completing a loop through all the vertices that make up the face. This vector holds all the face 103 /// sequences back-to-back, thus looking something like 'x---xy--------yz--z' in the general case. 85 104 FaceIndices m_Faces; 86 105 87 106 struct Helper; -
source/maths/tests/test_Bound.h
diff --git a/source/maths/tests/test_Bound.h b/source/maths/tests/test_Bound.h index 54d3c73..282f54f 100644
a b 1 /* Copyright (C) 20 09Wildfire Games.1 /* Copyright (C) 2012 Wildfire Games. 2 2 * This file is part of 0 A.D. 3 3 * 4 4 * 0 A.D. is free software: you can redistribute it and/or modify … … 18 18 #include "lib/self_test.h" 19 19 20 20 #include "maths/BoundingBoxAligned.h" 21 #include "maths/BoundingBoxOriented.h" 21 22 22 23 class TestBound : public CxxTest::TestSuite 23 24 { 24 25 public: 25 void test_empty ()26 void test_empty_aabb() 26 27 { 27 28 CBoundingBoxAligned bound; 28 29 TS_ASSERT(bound.IsEmpty()); … … public: 32 33 TS_ASSERT(bound.IsEmpty()); 33 34 } 34 35 36 void test_empty_obb() 37 { 38 CBoundingBoxOriented bound; 39 TS_ASSERT(bound.IsEmpty()); 40 bound.m_Basis[0] = CVector3D(1,0,0); 41 bound.m_Basis[1] = CVector3D(0,1,0); 42 bound.m_Basis[2] = CVector3D(0,0,1); 43 bound.m_HalfSizes = CVector3D(1,2,3); 44 TS_ASSERT(!bound.IsEmpty()); 45 bound.SetEmpty(); 46 TS_ASSERT(bound.IsEmpty()); 47 } 48 35 49 void test_extend_vector() 36 50 { 37 51 CBoundingBoxAligned bound; -
new file source/maths/tests/test_Brush.h
diff --git a/source/maths/tests/test_Brush.h b/source/maths/tests/test_Brush.h new file mode 100644 index 0000000..ffbe840
- + 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 #include "lib/self_test.h" 19 20 #include "maths/Brush.h" 21 #include "maths/BoundingBoxAligned.h" 22 #include "graphics/Frustum.h" 23 24 class TestBrush : public CxxTest::TestSuite 25 { 26 public: 27 void setUp() 28 { 29 CxxTest::setAbortTestOnFail(true); 30 } 31 32 void tearDown() 33 { 34 35 } 36 37 void test_slice_plane_simple() 38 { 39 // slice a 1x1x1 cube vertically down the middle at z = 0.5, with the plane normal pointing towards the negative 40 // end of the Z axis (i.e., anything with Z lower than 0.5 is considered 'in front of' the plane and is kept) 41 CPlane plane(CVector4D(0, 0, -1, 0.5f)); 42 CBrush brush(CBoundingBoxAligned(CVector3D(0,0,0), CVector3D(1,1,1))); 43 44 CBrush result; 45 brush.Slice(plane, result); 46 47 // verify that the resulting brush consists of exactly our 8 expected, unique vertices 48 TS_ASSERT_EQUALS(8, result.GetVertices().size()); 49 size_t LBF = GetUniqueVertexIndex(result, CVector3D(0, 0, 0)); // left-bottom-front <=> XYZ 50 size_t RBF = GetUniqueVertexIndex(result, CVector3D(1, 0, 0)); // right-bottom-front 51 size_t RBB = GetUniqueVertexIndex(result, CVector3D(1, 0, 0.5f)); // right-bottom-back 52 size_t LBB = GetUniqueVertexIndex(result, CVector3D(0, 0, 0.5f)); // etc. 53 size_t LTF = GetUniqueVertexIndex(result, CVector3D(0, 1, 0)); 54 size_t RTF = GetUniqueVertexIndex(result, CVector3D(1, 1, 0)); 55 size_t RTB = GetUniqueVertexIndex(result, CVector3D(1, 1, 0.5f)); 56 size_t LTB = GetUniqueVertexIndex(result, CVector3D(0, 1, 0.5f)); 57 58 // verify that the brush contains the six expected planes (one of which is the slicing plane) 59 VerifyFacePresent(result, 5, LBF, RBF, RBB, LBB, LBF); // bottom face 60 VerifyFacePresent(result, 5, LTF, RTF, RTB, LTB, LTF); // top face 61 VerifyFacePresent(result, 5, LBF, LBB, LTB, LTF, LBF); // left face 62 VerifyFacePresent(result, 5, RBF, RBB, RTB, RTF, RBF); // right face 63 VerifyFacePresent(result, 5, LBF, RBF, RTF, LTF, LBF); // front face 64 VerifyFacePresent(result, 5, LBB, RBB, RTB, LTB, LBB); // back face 65 } 66 67 private: 68 size_t GetUniqueVertexIndex(const CBrush& brush, const CVector3D& vertex, float eps = 1e-6f) 69 { 70 std::vector<CVector3D> vertices = brush.GetVertices(); 71 72 for (size_t i = 0; i < vertices.size(); ++i) 73 { 74 const CVector3D& v = vertices[i]; 75 if (fabs(v.X - vertex.X) < eps 76 && fabs(v.Y - vertex.Y) < eps 77 && fabs(v.Z - vertex.Z) < eps) 78 return i; 79 } 80 81 TS_FAIL("Vertex not found in brush"); 82 return ~0u; 83 } 84 85 void VerifyFacePresent(const CBrush& brush, int count, ...) 86 { 87 std::vector<size_t> face; 88 89 va_list args; 90 va_start(args, count); 91 for (int x = 0; x < count; ++x) 92 face.push_back(va_arg(args, size_t)); 93 va_end(args); 94 95 if (face.size() == 0) 96 return; 97 98 std::vector<std::vector<size_t> > faces; 99 brush.GetFaces(faces); 100 101 // the brush is free to use any starting vertex along the face, and to use any winding order, so have 'face' 102 // cycle through various starting values and see if any of them (or their reverse) matches one found in the brush. 103 104 for (size_t c = 0; c < face.size() - 1; ++c) 105 { 106 std::vector<std::vector<size_t> >::iterator it1 = std::find(faces.begin(), faces.end(), face); 107 if (it1 != faces.end()) 108 return; 109 110 // no match, try the reverse 111 std::vector<size_t> faceReverse = face; 112 std::reverse(faceReverse.begin(), faceReverse.end()); 113 std::vector<std::vector<size_t> >::iterator it2 = std::find(faces.begin(), faces.end(), faceReverse); 114 if (it2 != faces.end()) 115 return; 116 117 // no match, cycle it 118 face.erase(face.begin()); 119 face.push_back(face[0]); 120 } 121 122 TS_FAIL("Face not found in brush"); 123 } 124 }; -
source/renderer/ShadowMap.cpp
diff --git a/source/renderer/ShadowMap.cpp b/source/renderer/ShadowMap.cpp index 779366d..30685ce 100644
a b void ShadowMap::AddShadowedBound(const CBoundingBoxAligned& bounds) 218 218 // projection and transformation matrices 219 219 void ShadowMapInternals::CalcShadowMatrices() 220 220 { 221 CRenderer& renderer = g_Renderer; 222 223 float minZ = ShadowBound[0].Z; 224 225 ShadowBound.IntersectFrustumConservative(LightspaceCamera.GetFrustum()); 226 227 // round off the shadow boundaries to sane increments to help reduce swim effect 228 float boundInc = 16.0f; 229 ShadowBound[0].X = floor(ShadowBound[0].X / boundInc) * boundInc; 230 ShadowBound[0].Y = floor(ShadowBound[0].Y / boundInc) * boundInc; 231 ShadowBound[1].X = ceil(ShadowBound[1].X / boundInc) * boundInc; 232 ShadowBound[1].Y = ceil(ShadowBound[1].Y / boundInc) * boundInc; 233 234 // minimum Z bound must not be clipped too much, because objects that lie outside 235 // the shadow bounds cannot cast shadows either 236 // the 2.0 is rather arbitrary: it should be big enough so that we won't accidently miss 237 // a shadow generator, and small enough not to affect Z precision 238 ShadowBound[0].Z = minZ - 2.0; 239 240 // Setup orthogonal projection (lightspace -> clip space) for shadowmap rendering 241 CVector3D scale = ShadowBound[1] - ShadowBound[0]; 242 CVector3D shift = (ShadowBound[1] + ShadowBound[0]) * -0.5; 243 244 if (scale.X < 1.0) 245 scale.X = 1.0; 246 if (scale.Y < 1.0) 247 scale.Y = 1.0; 248 if (scale.Z < 1.0) 249 scale.Z = 1.0; 250 251 scale.X = 2.0 / scale.X; 252 scale.Y = 2.0 / scale.Y; 253 scale.Z = 2.0 / scale.Z; 254 255 // make sure a given world position falls on a consistent shadowmap texel fractional offset 256 float offsetX = fmod(ShadowBound[0].X - LightTransform._14, 2.0f/(scale.X*EffectiveWidth)); 257 float offsetY = fmod(ShadowBound[0].Y - LightTransform._24, 2.0f/(scale.Y*EffectiveHeight)); 258 259 LightProjection.SetZero(); 260 LightProjection._11 = scale.X; 261 LightProjection._14 = (shift.X + offsetX) * scale.X; 262 LightProjection._22 = scale.Y; 263 LightProjection._24 = (shift.Y + offsetY) * scale.Y; 264 LightProjection._33 = scale.Z; 265 LightProjection._34 = shift.Z * scale.Z + renderer.m_ShadowZBias; 266 LightProjection._44 = 1.0; 267 268 269 // Calculate texture matrix by creating the clip space to texture coordinate matrix 270 // and then concatenating all matrices that have been calculated so far 271 CMatrix3D lightToTex; 272 float texscalex = scale.X * 0.5f * (float)EffectiveWidth / (float)Width; 273 float texscaley = scale.Y * 0.5f * (float)EffectiveHeight / (float)Height; 274 float texscalez = scale.Z * 0.5f; 275 276 lightToTex.SetZero(); 277 lightToTex._11 = texscalex; 278 lightToTex._14 = (offsetX - ShadowBound[0].X) * texscalex; 279 lightToTex._22 = texscaley; 280 lightToTex._24 = (offsetY - ShadowBound[0].Y) * texscaley; 281 lightToTex._33 = texscalez; 282 lightToTex._34 = -ShadowBound[0].Z * texscalez; 283 lightToTex._44 = 1.0; 284 285 TextureMatrix = lightToTex * LightTransform; 221 if (!ShadowBound.IsEmpty()) 222 { 223 CRenderer& renderer = g_Renderer; 224 float minZ = ShadowBound[0].Z; 225 226 ShadowBound.IntersectFrustumConservative(LightspaceCamera.GetFrustum()); 227 228 // round off the shadow boundaries to sane increments to help reduce swim effect 229 float boundInc = 16.0f; 230 ShadowBound[0].X = floor(ShadowBound[0].X / boundInc) * boundInc; 231 ShadowBound[0].Y = floor(ShadowBound[0].Y / boundInc) * boundInc; 232 ShadowBound[1].X = ceil(ShadowBound[1].X / boundInc) * boundInc; 233 ShadowBound[1].Y = ceil(ShadowBound[1].Y / boundInc) * boundInc; 234 235 // minimum Z bound must not be clipped too much, because objects that lie outside 236 // the shadow bounds cannot cast shadows either 237 // the 2.0 is rather arbitrary: it should be big enough so that we won't accidently miss 238 // a shadow generator, and small enough not to affect Z precision 239 ShadowBound[0].Z = minZ - 2.0; 240 241 // Setup orthogonal projection (lightspace -> clip space) for shadowmap rendering 242 CVector3D scale = ShadowBound[1] - ShadowBound[0]; 243 CVector3D shift = (ShadowBound[1] + ShadowBound[0]) * -0.5; 244 245 if (scale.X < 1.0) 246 scale.X = 1.0; 247 if (scale.Y < 1.0) 248 scale.Y = 1.0; 249 if (scale.Z < 1.0) 250 scale.Z = 1.0; 251 252 scale.X = 2.0 / scale.X; 253 scale.Y = 2.0 / scale.Y; 254 scale.Z = 2.0 / scale.Z; 255 256 // make sure a given world position falls on a consistent shadowmap texel fractional offset 257 float offsetX = fmod(ShadowBound[0].X - LightTransform._14, 2.0f/(scale.X*EffectiveWidth)); 258 float offsetY = fmod(ShadowBound[0].Y - LightTransform._24, 2.0f/(scale.Y*EffectiveHeight)); 259 260 LightProjection.SetZero(); 261 LightProjection._11 = scale.X; 262 LightProjection._14 = (shift.X + offsetX) * scale.X; 263 LightProjection._22 = scale.Y; 264 LightProjection._24 = (shift.Y + offsetY) * scale.Y; 265 LightProjection._33 = scale.Z; 266 LightProjection._34 = shift.Z * scale.Z + renderer.m_ShadowZBias; 267 LightProjection._44 = 1.0; 268 269 // Calculate texture matrix by creating the clip space to texture coordinate matrix 270 // and then concatenating all matrices that have been calculated so far 271 272 float texscalex = scale.X * 0.5f * (float)EffectiveWidth / (float)Width; 273 float texscaley = scale.Y * 0.5f * (float)EffectiveHeight / (float)Height; 274 float texscalez = scale.Z * 0.5f; 275 276 CMatrix3D lightToTex; 277 lightToTex.SetZero(); 278 lightToTex._11 = texscalex; 279 lightToTex._14 = (offsetX - ShadowBound[0].X) * texscalex; 280 lightToTex._22 = texscaley; 281 lightToTex._24 = (offsetY - ShadowBound[0].Y) * texscaley; 282 lightToTex._33 = texscalez; 283 lightToTex._34 = -ShadowBound[0].Z * texscalez; 284 lightToTex._44 = 1.0; 285 286 TextureMatrix = lightToTex * LightTransform; 287 } 288 else 289 { 290 // TODO: what are sensible no-op values here? 291 LightProjection.SetIdentity(); 292 TextureMatrix = LightTransform; 293 } 286 294 } 287 295 288 296
