/src/ogre/OgreMain/src/OgreSubMesh.cpp
Line | Count | Source |
1 | | /* |
2 | | ----------------------------------------------------------------------------- |
3 | | This source file is part of OGRE |
4 | | (Object-oriented Graphics Rendering Engine) |
5 | | For the latest info, see http://www.ogre3d.org/ |
6 | | |
7 | | Copyright (c) 2000-2014 Torus Knot Software Ltd |
8 | | |
9 | | Permission is hereby granted, free of charge, to any person obtaining a copy |
10 | | of this software and associated documentation files (the "Software"), to deal |
11 | | in the Software without restriction, including without limitation the rights |
12 | | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
13 | | copies of the Software, and to permit persons to whom the Software is |
14 | | furnished to do so, subject to the following conditions: |
15 | | |
16 | | The above copyright notice and this permission notice shall be included in |
17 | | all copies or substantial portions of the Software. |
18 | | |
19 | | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
20 | | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
21 | | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
22 | | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
23 | | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
24 | | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
25 | | THE SOFTWARE. |
26 | | ----------------------------------------------------------------------------- |
27 | | */ |
28 | | #include "OgreStableHeaders.h" |
29 | | |
30 | | namespace Ogre { |
31 | | //----------------------------------------------------------------------- |
32 | | SubMesh::SubMesh() |
33 | 0 | : vertexData(0) |
34 | 0 | , parent(0) |
35 | 0 | , useSharedVertices(true) |
36 | 0 | , operationType(RenderOperation::OT_TRIANGLE_LIST) |
37 | 0 | , mBoneAssignmentsOutOfDate(false) |
38 | 0 | , mVertexAnimationType(VAT_NONE) |
39 | 0 | , mVertexAnimationIncludesNormals(false) |
40 | 0 | , mBuildEdgesEnabled(true) |
41 | 0 | { |
42 | 0 | indexData = OGRE_NEW IndexData(); |
43 | 0 | } |
44 | | //----------------------------------------------------------------------- |
45 | | SubMesh::~SubMesh() |
46 | 0 | { |
47 | 0 | removeLodLevels(); |
48 | 0 | OGRE_DELETE vertexData; |
49 | 0 | OGRE_DELETE indexData; |
50 | 0 | } |
51 | | |
52 | | //----------------------------------------------------------------------- |
53 | | void SubMesh::setMaterialName( const String& name, const String& groupName /* = ResourceGroupManager::AUTODETECT_RESOURCE_GROUP_NAME */) |
54 | 0 | { |
55 | 0 | mMaterial = MaterialManager::getSingleton().getByName(name, groupName); |
56 | 0 | } |
57 | | //----------------------------------------------------------------------- |
58 | | const String& SubMesh::getMaterialName() const |
59 | 0 | { |
60 | 0 | return mMaterial ? mMaterial->getName() : BLANKSTRING; |
61 | 0 | } |
62 | | //----------------------------------------------------------------------- |
63 | | void SubMesh::_getRenderOperation(RenderOperation& ro, ushort lodIndex) |
64 | 0 | { |
65 | 0 | if (lodIndex > 0 && static_cast< size_t >( lodIndex - 1 ) < mLodFaceList.size()) |
66 | 0 | { |
67 | | // lodIndex - 1 because we don't store full detail version in mLodFaceList |
68 | 0 | ro.indexData = mLodFaceList[lodIndex-1]; |
69 | 0 | } |
70 | 0 | else |
71 | 0 | { |
72 | 0 | ro.indexData = indexData; |
73 | 0 | } |
74 | 0 | ro.useIndexes = ro.indexData->indexCount != 0; |
75 | 0 | ro.operationType = operationType; |
76 | 0 | ro.vertexData = useSharedVertices? parent->sharedVertexData : vertexData; |
77 | |
|
78 | 0 | } |
79 | | //----------------------------------------------------------------------- |
80 | | void SubMesh::addBoneAssignment(const VertexBoneAssignment& vertBoneAssign) |
81 | 0 | { |
82 | 0 | OgreAssert(!useSharedVertices, |
83 | 0 | "This SubMesh uses shared geometry, you must assign bones to the Mesh, not the SubMesh"); |
84 | 0 | mBoneAssignments.emplace(vertBoneAssign.vertexIndex, vertBoneAssign); |
85 | 0 | mBoneAssignmentsOutOfDate = true; |
86 | 0 | } |
87 | | //----------------------------------------------------------------------- |
88 | | void SubMesh::clearBoneAssignments(void) |
89 | 0 | { |
90 | 0 | mBoneAssignments.clear(); |
91 | 0 | mBoneAssignmentsOutOfDate = true; |
92 | 0 | } |
93 | | |
94 | | //----------------------------------------------------------------------- |
95 | | void SubMesh::_compileBoneAssignments(void) |
96 | 0 | { |
97 | 0 | unsigned short maxBones = |
98 | 0 | parent->_rationaliseBoneAssignments(vertexData->vertexCount, mBoneAssignments); |
99 | |
|
100 | 0 | if (maxBones != 0) |
101 | 0 | { |
102 | 0 | parent->compileBoneAssignments(mBoneAssignments, maxBones, |
103 | 0 | blendIndexToBoneIndexMap, vertexData); |
104 | 0 | } |
105 | |
|
106 | 0 | mBoneAssignmentsOutOfDate = false; |
107 | 0 | } |
108 | | //--------------------------------------------------------------------- |
109 | | SubMesh::BoneAssignmentIterator SubMesh::getBoneAssignmentIterator(void) |
110 | 0 | { |
111 | 0 | return BoneAssignmentIterator(mBoneAssignments.begin(), |
112 | 0 | mBoneAssignments.end()); |
113 | 0 | } |
114 | | //--------------------------------------------------------------------- |
115 | | void SubMesh::removeLodLevels(void) |
116 | 0 | { |
117 | 0 | LODFaceList::iterator lodi, lodend; |
118 | 0 | lodend = mLodFaceList.end(); |
119 | 0 | for (lodi = mLodFaceList.begin(); lodi != lodend; ++lodi) |
120 | 0 | { |
121 | 0 | OGRE_DELETE *lodi; |
122 | 0 | } |
123 | |
|
124 | 0 | mLodFaceList.clear(); |
125 | |
|
126 | 0 | } |
127 | | //--------------------------------------------------------------------- |
128 | | VertexAnimationType SubMesh::getVertexAnimationType(void) const |
129 | 0 | { |
130 | 0 | if(parent->_getAnimationTypesDirty()) |
131 | 0 | { |
132 | 0 | parent->_determineAnimationTypes(); |
133 | 0 | } |
134 | 0 | return mVertexAnimationType; |
135 | 0 | } |
136 | | //--------------------------------------------------------------------- |
137 | | /* To find as many points from different domains as we need, |
138 | | * such that those domains are from different parts of the mesh, |
139 | | * we implement a simplified Heckbert quantization algorithm. |
140 | | * |
141 | | * This struct is like AxisAlignedBox with some specialized methods |
142 | | * for doing quantization. |
143 | | */ |
144 | | struct Cluster |
145 | | { |
146 | | Vector3 mMin, mMax; |
147 | | std::set<uint32> mIndices; |
148 | | |
149 | | Cluster () |
150 | 0 | { } |
151 | | |
152 | | bool empty () const |
153 | 0 | { |
154 | 0 | if (mIndices.empty ()) |
155 | 0 | return true; |
156 | 0 | if (mMin == mMax) |
157 | 0 | return true; |
158 | 0 | return false; |
159 | 0 | } |
160 | | |
161 | | float volume () const |
162 | 0 | { |
163 | 0 | return (mMax.x - mMin.x) * (mMax.y - mMin.y) * (mMax.z - mMin.z); |
164 | 0 | } |
165 | | |
166 | | void extend (float *v) |
167 | 0 | { |
168 | 0 | if (v [0] < mMin.x) mMin.x = v [0]; |
169 | 0 | if (v [1] < mMin.y) mMin.y = v [1]; |
170 | 0 | if (v [2] < mMin.z) mMin.z = v [2]; |
171 | 0 | if (v [0] > mMax.x) mMax.x = v [0]; |
172 | 0 | if (v [1] > mMax.y) mMax.y = v [1]; |
173 | 0 | if (v [2] > mMax.z) mMax.z = v [2]; |
174 | 0 | } |
175 | | |
176 | | void computeBBox (const VertexElement *poselem, uint8 *vdata, size_t vsz) |
177 | 0 | { |
178 | 0 | mMin.x = mMin.y = mMin.z = Math::POS_INFINITY; |
179 | 0 | mMax.x = mMax.y = mMax.z = Math::NEG_INFINITY; |
180 | |
|
181 | 0 | for (unsigned int idx : mIndices) |
182 | 0 | { |
183 | 0 | float *v; |
184 | 0 | poselem->baseVertexPointerToElement (vdata + idx * vsz, &v); |
185 | 0 | extend (v); |
186 | 0 | } |
187 | 0 | } |
188 | | |
189 | | Cluster split (int split_axis, const VertexElement *poselem, |
190 | | uint8 *vdata, size_t vsz) |
191 | 0 | { |
192 | 0 | Real r = (mMin [split_axis] + mMax [split_axis]) * 0.5f; |
193 | 0 | Cluster newbox; |
194 | | |
195 | | // Separate all points that are inside the new bbox |
196 | 0 | for (std::set<uint32>::iterator i = mIndices.begin (); |
197 | 0 | i != mIndices.end (); ) |
198 | 0 | { |
199 | 0 | float *v; |
200 | 0 | poselem->baseVertexPointerToElement (vdata + *i * vsz, &v); |
201 | 0 | if (v [split_axis] > r) |
202 | 0 | { |
203 | 0 | newbox.mIndices.insert (*i); |
204 | 0 | std::set<uint32>::iterator x = i++; |
205 | 0 | mIndices.erase(x); |
206 | 0 | } |
207 | 0 | else |
208 | 0 | ++i; |
209 | 0 | } |
210 | |
|
211 | 0 | computeBBox (poselem, vdata, vsz); |
212 | 0 | newbox.computeBBox (poselem, vdata, vsz); |
213 | |
|
214 | 0 | return newbox; |
215 | 0 | } |
216 | | }; |
217 | | //--------------------------------------------------------------------- |
218 | | void SubMesh::generateExtremes(size_t count) |
219 | 0 | { |
220 | 0 | extremityPoints.clear(); |
221 | |
|
222 | 0 | if (count == 0) |
223 | 0 | return; |
224 | | |
225 | | /* Currently this uses just one criteria: the points must be |
226 | | * as far as possible from each other. This at least ensures |
227 | | * that the extreme points characterise the submesh as |
228 | | * detailed as it's possible. |
229 | | */ |
230 | | |
231 | 0 | VertexData *vert = useSharedVertices ? |
232 | 0 | parent->sharedVertexData : vertexData; |
233 | 0 | const VertexElement *poselem = vert->vertexDeclaration-> |
234 | 0 | findElementBySemantic (VES_POSITION); |
235 | 0 | HardwareVertexBufferSharedPtr vbuf = vert->vertexBufferBinding-> |
236 | 0 | getBuffer (poselem->getSource ()); |
237 | 0 | uint8 *vdata = (uint8 *)vbuf->lock (HardwareBuffer::HBL_READ_ONLY); |
238 | 0 | size_t vsz = vbuf->getVertexSize (); |
239 | |
|
240 | 0 | std::vector<Cluster> boxes; |
241 | 0 | boxes.reserve (count); |
242 | | |
243 | | // First of all, find min and max bounding box of the submesh |
244 | 0 | boxes.push_back (Cluster ()); |
245 | |
|
246 | 0 | if (indexData->indexCount > 0) |
247 | 0 | { |
248 | |
|
249 | 0 | uint elsz = indexData->indexBuffer->getType () == HardwareIndexBuffer::IT_32BIT ? |
250 | 0 | 4 : 2; |
251 | 0 | uint8 *idata = (uint8 *)indexData->indexBuffer->lock ( |
252 | 0 | indexData->indexStart * elsz, indexData->indexCount * elsz, |
253 | 0 | HardwareIndexBuffer::HBL_READ_ONLY); |
254 | |
|
255 | 0 | for (size_t i = 0; i < indexData->indexCount; i++) |
256 | 0 | { |
257 | 0 | int idx = (elsz == 2) ? ((uint16 *)idata) [i] : ((uint32 *)idata) [i]; |
258 | 0 | boxes [0].mIndices.insert (idx); |
259 | 0 | } |
260 | 0 | indexData->indexBuffer->unlock (); |
261 | |
|
262 | 0 | } |
263 | 0 | else |
264 | 0 | { |
265 | | // just insert all indexes |
266 | 0 | for (size_t i = vertexData->vertexStart; i < vertexData->vertexCount; i++) |
267 | 0 | { |
268 | 0 | boxes [0].mIndices.insert (static_cast<int>(i)); |
269 | 0 | } |
270 | |
|
271 | 0 | } |
272 | |
|
273 | 0 | boxes [0].computeBBox (poselem, vdata, vsz); |
274 | | |
275 | | // Remember the geometrical center of the submesh |
276 | 0 | Vector3 center = (boxes [0].mMax + boxes [0].mMin) * 0.5; |
277 | | |
278 | | // Ok, now loop until we have as many boxes, as we need extremes |
279 | 0 | while (boxes.size () < count) |
280 | 0 | { |
281 | | // Find the largest box with more than one vertex :) |
282 | 0 | Cluster *split_box = NULL; |
283 | 0 | Real split_volume = -1; |
284 | 0 | for (auto & boxe : boxes) |
285 | 0 | { |
286 | 0 | if (boxe.empty ()) |
287 | 0 | continue; |
288 | 0 | Real v = boxe.volume (); |
289 | 0 | if (v > split_volume) |
290 | 0 | { |
291 | 0 | split_volume = v; |
292 | 0 | split_box = &boxe; |
293 | 0 | } |
294 | 0 | } |
295 | | |
296 | | // If we don't have what to split, break |
297 | 0 | if (!split_box) |
298 | 0 | break; |
299 | | |
300 | | // Find the coordinate axis to split the box into two |
301 | 0 | int split_axis = 0; |
302 | 0 | Real split_length = split_box->mMax.x - split_box->mMin.x; |
303 | 0 | for (int i = 1; i < 3; i++) |
304 | 0 | { |
305 | 0 | Real l = split_box->mMax [i] - split_box->mMin [i]; |
306 | 0 | if (l > split_length) |
307 | 0 | { |
308 | 0 | split_length = l; |
309 | 0 | split_axis = i; |
310 | 0 | } |
311 | 0 | } |
312 | | |
313 | | // Now split the box into halves |
314 | 0 | boxes.push_back (split_box->split (split_axis, poselem, vdata, vsz)); |
315 | 0 | } |
316 | | |
317 | | // Fine, now from every cluster choose the vertex that is most |
318 | | // distant from the geometrical center and from other extremes. |
319 | 0 | for (const auto & boxe : boxes) |
320 | 0 | { |
321 | 0 | Real rating = 0; |
322 | 0 | Vector3 best_vertex; |
323 | |
|
324 | 0 | for (unsigned int i : boxe.mIndices) |
325 | 0 | { |
326 | 0 | float *v; |
327 | 0 | poselem->baseVertexPointerToElement (vdata + i * vsz, &v); |
328 | |
|
329 | 0 | Vector3 vv (v [0], v [1], v [2]); |
330 | 0 | Real r = (vv - center).squaredLength (); |
331 | |
|
332 | 0 | for (auto extremityPoint : extremityPoints) |
333 | 0 | r += (extremityPoint - vv).squaredLength (); |
334 | 0 | if (r > rating) |
335 | 0 | { |
336 | 0 | rating = r; |
337 | 0 | best_vertex = vv; |
338 | 0 | } |
339 | 0 | } |
340 | |
|
341 | 0 | if (rating > 0) |
342 | 0 | extremityPoints.push_back (best_vertex); |
343 | 0 | } |
344 | |
|
345 | 0 | vbuf->unlock (); |
346 | 0 | } |
347 | | //--------------------------------------------------------------------- |
348 | | void SubMesh::setBuildEdgesEnabled(bool b) |
349 | 0 | { |
350 | 0 | mBuildEdgesEnabled = b; |
351 | 0 | if(parent) |
352 | 0 | { |
353 | 0 | parent->freeEdgeList(); |
354 | 0 | parent->setAutoBuildEdgeLists(true); |
355 | 0 | } |
356 | 0 | } |
357 | | //--------------------------------------------------------------------- |
358 | | SubMesh * SubMesh::clone(const String& newName, Mesh *parentMesh) |
359 | 0 | { |
360 | | // This is a bit like a copy constructor, but with the additional aspect of registering the clone with |
361 | | // the MeshManager |
362 | |
|
363 | 0 | if(parentMesh == NULL) |
364 | 0 | parentMesh = parent; |
365 | |
|
366 | 0 | HardwareBufferManagerBase* bufferManager = parentMesh->getHardwareBufferManager(); |
367 | 0 | SubMesh* newSub = parentMesh->createSubMesh(newName); |
368 | |
|
369 | 0 | newSub->mMaterial = this->mMaterial; |
370 | 0 | newSub->operationType = this->operationType; |
371 | 0 | newSub->useSharedVertices = this->useSharedVertices; |
372 | 0 | newSub->extremityPoints = this->extremityPoints; |
373 | |
|
374 | 0 | if (!this->useSharedVertices) |
375 | 0 | { |
376 | | // Copy unique vertex data |
377 | 0 | newSub->vertexData = this->vertexData->clone(true, bufferManager); |
378 | | // Copy unique index map |
379 | 0 | newSub->blendIndexToBoneIndexMap = this->blendIndexToBoneIndexMap; |
380 | 0 | } |
381 | | |
382 | | // Copy index data |
383 | 0 | OGRE_DELETE newSub->indexData; |
384 | 0 | newSub->indexData = this->indexData->clone(true, bufferManager); |
385 | | // Copy any bone assignments |
386 | 0 | newSub->mBoneAssignments = this->mBoneAssignments; |
387 | 0 | newSub->mBoneAssignmentsOutOfDate = this->mBoneAssignmentsOutOfDate; |
388 | | |
389 | | // Copy lod face lists |
390 | 0 | newSub->mLodFaceList.reserve(this->mLodFaceList.size()); |
391 | 0 | SubMesh::LODFaceList::const_iterator facei; |
392 | 0 | for (facei = this->mLodFaceList.begin(); facei != this->mLodFaceList.end(); ++facei) { |
393 | 0 | IndexData* newIndexData = (*facei)->clone(true, bufferManager); |
394 | 0 | newSub->mLodFaceList.push_back(newIndexData); |
395 | 0 | } |
396 | 0 | return newSub; |
397 | 0 | } |
398 | | } |
399 | | |
400 | | |