PolyVox  0.3.0-dev
Open source voxel management library
CubicSurfaceExtractor.inl
Go to the documentation of this file.
1 /*******************************************************************************
2 Copyright (c) 2005-2009 David Williams
3 
4 This software is provided 'as-is', without any express or implied
5 warranty. In no event will the authors be held liable for any damages
6 arising from the use of this software.
7 
8 Permission is granted to anyone to use this software for any purpose,
9 including commercial applications, and to alter it and redistribute it
10 freely, subject to the following restrictions:
11 
12  1. The origin of this software must not be misrepresented; you must not
13  claim that you wrote the original software. If you use this software
14  in a product, an acknowledgment in the product documentation would be
15  appreciated but is not required.
16 
17  2. Altered source versions must be plainly marked as such, and must not be
18  misrepresented as being the original software.
19 
20  3. This notice may not be removed or altered from any source
21  distribution.
22 *******************************************************************************/
23 
24 namespace PolyVox
25 {
26  // We try to avoid duplicate vertices by checking whether a vertex has already been added at a given position.
27  // However, it is possible that vertices have the same position but different materials. In this case, the
28  // vertices are not true duplicates and both must be added to the mesh. As far as I can tell, it is possible to have
29  // at most eight vertices with the same position but different materials. For example, this worst-case scenario
30  // happens when we have a 2x2x2 group of voxels, all with different materials and some/all partially transparent.
31  // The vertex position at the center of this group is then going to be used by all eight voxels all with different
32  // materials.
33  template<typename VolumeType, typename IsQuadNeeded>
34  const uint32_t CubicSurfaceExtractor<VolumeType, IsQuadNeeded>::MaxVerticesPerPosition = 8;
35 
36  template<typename VolumeType, typename IsQuadNeeded>
37  CubicSurfaceExtractor<VolumeType, IsQuadNeeded>::CubicSurfaceExtractor(VolumeType* volData, Region region, SurfaceMesh<PositionMaterial>* result, WrapMode eWrapMode, typename VolumeType::VoxelType tBorderValue, bool bMergeQuads, IsQuadNeeded isQuadNeeded)
38  :m_volData(volData)
39  ,m_regSizeInVoxels(region)
40  ,m_meshCurrent(result)
41  ,m_bMergeQuads(bMergeQuads)
42  ,m_eWrapMode(eWrapMode)
43  ,m_tBorderValue(tBorderValue)
44  {
45  m_funcIsQuadNeededCallback = isQuadNeeded;
46  }
47 
48  template<typename VolumeType, typename IsQuadNeeded>
50  {
51  m_meshCurrent->clear();
52 
53  uint32_t uArrayWidth = m_regSizeInVoxels.getUpperX() - m_regSizeInVoxels.getLowerX() + 2;
54  uint32_t uArrayHeight = m_regSizeInVoxels.getUpperY() - m_regSizeInVoxels.getLowerY() + 2;
55 
56  uint32_t arraySize[3]= {uArrayWidth, uArrayHeight, MaxVerticesPerPosition};
57  m_previousSliceVertices.resize(arraySize);
58  m_currentSliceVertices.resize(arraySize);
59  memset(m_previousSliceVertices.getRawData(), 0xff, m_previousSliceVertices.getNoOfElements() * sizeof(IndexAndMaterial));
60  memset(m_currentSliceVertices.getRawData(), 0xff, m_currentSliceVertices.getNoOfElements() * sizeof(IndexAndMaterial));
61 
62  m_vecQuads[NegativeX].resize(m_regSizeInVoxels.getUpperX() - m_regSizeInVoxels.getLowerX() + 2);
63  m_vecQuads[PositiveX].resize(m_regSizeInVoxels.getUpperX() - m_regSizeInVoxels.getLowerX() + 2);
64 
65  m_vecQuads[NegativeY].resize(m_regSizeInVoxels.getUpperY() - m_regSizeInVoxels.getLowerY() + 2);
66  m_vecQuads[PositiveY].resize(m_regSizeInVoxels.getUpperY() - m_regSizeInVoxels.getLowerY() + 2);
67 
68  m_vecQuads[NegativeZ].resize(m_regSizeInVoxels.getUpperZ() - m_regSizeInVoxels.getLowerZ() + 2);
69  m_vecQuads[PositiveZ].resize(m_regSizeInVoxels.getUpperZ() - m_regSizeInVoxels.getLowerZ() + 2);
70 
71  typename VolumeType::Sampler volumeSampler(m_volData);
72  volumeSampler.setWrapMode(m_eWrapMode, m_tBorderValue);
73 
74  for(int32_t z = m_regSizeInVoxels.getLowerZ(); z <= m_regSizeInVoxels.getUpperZ(); z++)
75  {
76  uint32_t regZ = z - m_regSizeInVoxels.getLowerZ();
77 
78  for(int32_t y = m_regSizeInVoxels.getLowerY(); y <= m_regSizeInVoxels.getUpperY(); y++)
79  {
80  uint32_t regY = y - m_regSizeInVoxels.getLowerY();
81 
82  volumeSampler.setPosition(m_regSizeInVoxels.getLowerX(),y,z);
83 
84  for(int32_t x = m_regSizeInVoxels.getLowerX(); x <= m_regSizeInVoxels.getUpperX(); x++)
85  {
86  uint32_t regX = x - m_regSizeInVoxels.getLowerX();
87 
88  uint32_t material; //Filled in by callback
89  typename VolumeType::VoxelType currentVoxel = volumeSampler.getVoxel();
90  typename VolumeType::VoxelType negXVoxel = volumeSampler.peekVoxel1nx0py0pz();
91  typename VolumeType::VoxelType negYVoxel = volumeSampler.peekVoxel0px1ny0pz();
92  typename VolumeType::VoxelType negZVoxel = volumeSampler.peekVoxel0px0py1nz();
93 
94  // X
95  if(m_funcIsQuadNeededCallback(currentVoxel, negXVoxel, material))
96  {
97  uint32_t v0 = addVertex(regX , regY , regZ , material, m_previousSliceVertices);
98  uint32_t v1 = addVertex(regX , regY , regZ + 1, material, m_currentSliceVertices);
99  uint32_t v2 = addVertex(regX , regY + 1, regZ + 1, material, m_currentSliceVertices);
100  uint32_t v3 = addVertex(regX , regY + 1, regZ , material, m_previousSliceVertices);
101 
102  m_vecQuads[NegativeX][regX].push_back(Quad(v0, v1, v2, v3));
103  }
104 
105  if(m_funcIsQuadNeededCallback(negXVoxel, currentVoxel, material))
106  {
107  uint32_t v0 = addVertex(regX , regY , regZ , material, m_previousSliceVertices);
108  uint32_t v1 = addVertex(regX , regY , regZ + 1, material, m_currentSliceVertices);
109  uint32_t v2 = addVertex(regX , regY + 1, regZ + 1, material, m_currentSliceVertices);
110  uint32_t v3 = addVertex(regX , regY + 1, regZ , material, m_previousSliceVertices);
111 
112  m_vecQuads[PositiveX][regX].push_back(Quad(v0, v3, v2, v1));
113  }
114 
115  // Y
116  if(m_funcIsQuadNeededCallback(currentVoxel, negYVoxel, material))
117  {
118  uint32_t v0 = addVertex(regX , regY , regZ , material, m_previousSliceVertices);
119  uint32_t v1 = addVertex(regX + 1, regY , regZ , material, m_previousSliceVertices);
120  uint32_t v2 = addVertex(regX + 1, regY , regZ + 1, material, m_currentSliceVertices);
121  uint32_t v3 = addVertex(regX , regY , regZ + 1, material, m_currentSliceVertices);
122 
123  m_vecQuads[NegativeY][regY].push_back(Quad(v0, v1, v2, v3));
124  }
125 
126  if(m_funcIsQuadNeededCallback(negYVoxel, currentVoxel, material))
127  {
128  uint32_t v0 = addVertex(regX , regY , regZ , material, m_previousSliceVertices);
129  uint32_t v1 = addVertex(regX + 1, regY , regZ , material, m_previousSliceVertices);
130  uint32_t v2 = addVertex(regX + 1, regY , regZ + 1, material, m_currentSliceVertices);
131  uint32_t v3 = addVertex(regX , regY , regZ + 1, material, m_currentSliceVertices);
132 
133  m_vecQuads[PositiveY][regY].push_back(Quad(v0, v3, v2, v1));
134  }
135 
136  // Z
137  if(m_funcIsQuadNeededCallback(currentVoxel, negZVoxel, material))
138  {
139  uint32_t v0 = addVertex(regX , regY , regZ , material, m_previousSliceVertices);
140  uint32_t v1 = addVertex(regX , regY + 1, regZ , material, m_previousSliceVertices);
141  uint32_t v2 = addVertex(regX + 1, regY + 1, regZ , material, m_previousSliceVertices);
142  uint32_t v3 = addVertex(regX + 1, regY , regZ , material, m_previousSliceVertices);
143 
144  m_vecQuads[NegativeZ][regZ].push_back(Quad(v0, v1, v2, v3));
145  }
146 
147  if(m_funcIsQuadNeededCallback(negZVoxel, currentVoxel, material))
148  {
149  uint32_t v0 = addVertex(regX , regY , regZ , material, m_previousSliceVertices);
150  uint32_t v1 = addVertex(regX , regY + 1, regZ , material, m_previousSliceVertices);
151  uint32_t v2 = addVertex(regX + 1, regY + 1, regZ , material, m_previousSliceVertices);
152  uint32_t v3 = addVertex(regX + 1, regY , regZ , material, m_previousSliceVertices);
153 
154  m_vecQuads[PositiveZ][regZ].push_back(Quad(v0, v3, v2, v1));
155  }
156 
157  volumeSampler.movePositiveX();
158  }
159  }
160 
161  m_previousSliceVertices.swap(m_currentSliceVertices);
162  memset(m_currentSliceVertices.getRawData(), 0xff, m_currentSliceVertices.getNoOfElements() * sizeof(IndexAndMaterial));
163  }
164 
165  for(uint32_t uFace = 0; uFace < NoOfFaces; uFace++)
166  {
167  std::vector< std::list<Quad> >& vecListQuads = m_vecQuads[uFace];
168 
169  for(uint32_t slice = 0; slice < vecListQuads.size(); slice++)
170  {
171  std::list<Quad>& listQuads = vecListQuads[slice];
172 
173  if(m_bMergeQuads)
174  {
175  //Repeatedly call this function until it returns
176  //false to indicate nothing more can be done.
177  while(performQuadMerging(listQuads)){}
178  }
179 
180  typename std::list<Quad>::iterator iterEnd = listQuads.end();
181  for(typename std::list<Quad>::iterator quadIter = listQuads.begin(); quadIter != iterEnd; quadIter++)
182  {
183  Quad& quad = *quadIter;
184  m_meshCurrent->addTriangleCubic(quad.vertices[0], quad.vertices[1],quad.vertices[2]);
185  m_meshCurrent->addTriangleCubic(quad.vertices[0], quad.vertices[2],quad.vertices[3]);
186  }
187  }
188  }
189 
190  m_meshCurrent->m_Region = m_regSizeInVoxels;
191  m_meshCurrent->removeUnusedVertices();
192 
193  m_meshCurrent->m_vecLodRecords.clear();
194  LodRecord lodRecord;
195  lodRecord.beginIndex = 0;
196  lodRecord.endIndex = m_meshCurrent->getNoOfIndices();
197  m_meshCurrent->m_vecLodRecords.push_back(lodRecord);
198  }
199 
200  template<typename VolumeType, typename IsQuadNeeded>
202  {
203  for(uint32_t ct = 0; ct < MaxVerticesPerPosition; ct++)
204  {
205  IndexAndMaterial& rEntry = existingVertices[uX][uY][ct];
206 
207  if(rEntry.iIndex == -1)
208  {
209  //No vertices matched and we've now hit an empty space. Fill it by creating a vertex. The 0.5f offset is because vertices set between voxels in order to build cubes around them.
210  rEntry.iIndex = m_meshCurrent->addVertex(PositionMaterial(Vector3DFloat(static_cast<float>(uX) - 0.5f, static_cast<float>(uY) - 0.5f, static_cast<float>(uZ) - 0.5f), uMaterialIn));
211  rEntry.uMaterial = uMaterialIn;
212 
213  return rEntry.iIndex;
214  }
215 
216  //If we have an existing vertex and the material matches then we can return it.
217  if(rEntry.uMaterial == static_cast<int32_t>(uMaterialIn))
218  {
219  return rEntry.iIndex;
220  }
221  }
222 
223  // If we exit the loop here then apparently all the slots were full but none of them matched. I don't think
224  // this can happen so let's put an assert to make sure. If you hit this assert then please report it to us!
225  POLYVOX_ASSERT(false, "All slots full but no matches.");
226  return -1; //Should never happen.
227  }
228 
229  template<typename VolumeType, typename IsQuadNeeded>
230  bool CubicSurfaceExtractor<VolumeType, IsQuadNeeded>::performQuadMerging(std::list<Quad>& quads)
231  {
232  bool bDidMerge = false;
233  for(typename std::list<Quad>::iterator outerIter = quads.begin(); outerIter != quads.end(); outerIter++)
234  {
235  typename std::list<Quad>::iterator innerIter = outerIter;
236  innerIter++;
237  while(innerIter != quads.end())
238  {
239  Quad& q1 = *outerIter;
240  Quad& q2 = *innerIter;
241 
242  bool result = mergeQuads(q1,q2);
243 
244  if(result)
245  {
246  bDidMerge = true;
247  innerIter = quads.erase(innerIter);
248  }
249  else
250  {
251  innerIter++;
252  }
253  }
254  }
255 
256  return bDidMerge;
257  }
258 
259  template<typename VolumeType, typename IsQuadNeeded>
260  bool CubicSurfaceExtractor<VolumeType, IsQuadNeeded>::mergeQuads(Quad& q1, Quad& q2)
261  {
262  //All four vertices of a given quad have the same material,
263  //so just check that the first pair of vertices match.
264  if(std::abs(m_meshCurrent->getVertices()[q1.vertices[0]].getMaterial() - m_meshCurrent->getVertices()[q2.vertices[0]].getMaterial()) < 0.001)
265  {
266  //Now check whether quad 2 is adjacent to quad one by comparing vertices.
267  //Adjacent quads must share two vertices, and the second quad could be to the
268  //top, bottom, left, of right of the first one. This gives four combinations to test.
269  if((q1.vertices[0] == q2.vertices[1]) && ((q1.vertices[3] == q2.vertices[2])))
270  {
271  q1.vertices[0] = q2.vertices[0];
272  q1.vertices[3] = q2.vertices[3];
273  return true;
274  }
275  else if((q1.vertices[3] == q2.vertices[0]) && ((q1.vertices[2] == q2.vertices[1])))
276  {
277  q1.vertices[3] = q2.vertices[3];
278  q1.vertices[2] = q2.vertices[2];
279  return true;
280  }
281  else if((q1.vertices[1] == q2.vertices[0]) && ((q1.vertices[2] == q2.vertices[3])))
282  {
283  q1.vertices[1] = q2.vertices[1];
284  q1.vertices[2] = q2.vertices[2];
285  return true;
286  }
287  else if((q1.vertices[0] == q2.vertices[3]) && ((q1.vertices[1] == q2.vertices[2])))
288  {
289  q1.vertices[0] = q2.vertices[0];
290  q1.vertices[1] = q2.vertices[1];
291  return true;
292  }
293  }
294 
295  //Quads cannot be merged.
296  return false;
297  }
298 }