/src/ogre/OgreMain/src/OgreMesh.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 | | #include "OgreSkeletonManager.h" |
31 | | #include "OgreEdgeListBuilder.h" |
32 | | #include "OgreAnimation.h" |
33 | | #include "OgreAnimationState.h" |
34 | | #include "OgreAnimationTrack.h" |
35 | | #include "OgreOptimisedUtil.h" |
36 | | #include "OgreTangentSpaceCalc.h" |
37 | | #include "OgreLodStrategyManager.h" |
38 | | #include "OgrePixelCountLodStrategy.h" |
39 | | |
40 | | namespace Ogre { |
41 | | //----------------------------------------------------------------------- |
42 | | Mesh::Mesh(ResourceManager* creator, const String& name, ResourceHandle handle, |
43 | | const String& group, bool isManual, ManualResourceLoader* loader) |
44 | 0 | : Resource(creator, name, handle, group, isManual, loader), |
45 | 0 | mBoundRadius(0.0f), |
46 | 0 | mBoneBoundingRadius(0.0f), |
47 | 0 | mBoneAssignmentsOutOfDate(false), |
48 | 0 | mLodStrategy(LodStrategyManager::getSingleton().getDefaultStrategy()), |
49 | 0 | mHasManualLodLevel(false), |
50 | 0 | mNumLods(1), |
51 | 0 | mBufferManager(0), |
52 | 0 | mVertexBufferUsage(HBU_GPU_ONLY), |
53 | 0 | mIndexBufferUsage(HBU_GPU_ONLY), |
54 | 0 | mVertexBufferShadowBuffer(false), |
55 | 0 | mIndexBufferShadowBuffer(false), |
56 | 0 | mPreparedForShadowVolumes(false), |
57 | 0 | mEdgeListsBuilt(false), |
58 | 0 | mAutoBuildEdgeLists(false), // will be set to true by serializers of 1.20 and below |
59 | 0 | mSharedVertexDataAnimationType(VAT_NONE), |
60 | 0 | mSharedVertexDataAnimationIncludesNormals(false), |
61 | 0 | mAnimationTypesDirty(true), |
62 | 0 | mPosesIncludeNormals(false), |
63 | 0 | sharedVertexData(0) |
64 | 0 | { |
65 | | // Init first (manual) lod |
66 | 0 | MeshLodUsage lod; |
67 | 0 | lod.userValue = 0; // User value not used for base LOD level |
68 | 0 | lod.value = getLodStrategy()->getBaseValue(); |
69 | 0 | lod.edgeData = NULL; |
70 | 0 | lod.manualMesh.reset(); |
71 | 0 | mMeshLodUsageList.push_back(lod); |
72 | 0 | } |
73 | | //----------------------------------------------------------------------- |
74 | | Mesh::~Mesh() |
75 | 0 | { |
76 | 0 | if (!HardwareBufferManager::getSingletonPtr()) // LogManager might be also gone already |
77 | 0 | { |
78 | 0 | printf("ERROR: '%s' is being destroyed after HardwareBufferManager. This is a bug in user code.\n", mName.c_str()); |
79 | 0 | OgreAssertDbg(false, "Mesh destroyed after HardwareBufferManager"); // assert in debug mode |
80 | 0 | return; // try not to crash |
81 | 0 | } |
82 | | // have to call this here reather than in Resource destructor |
83 | | // since calling virtual methods in base destructors causes crash |
84 | 0 | unload(); |
85 | 0 | } |
86 | | //----------------------------------------------------------------------- |
87 | | HardwareBufferManagerBase* Mesh::getHardwareBufferManager() |
88 | 0 | { |
89 | 0 | return mBufferManager ? mBufferManager : HardwareBufferManager::getSingletonPtr(); |
90 | 0 | } |
91 | | //----------------------------------------------------------------------- |
92 | | SubMesh* Mesh::createSubMesh() |
93 | 0 | { |
94 | 0 | SubMesh* sub = OGRE_NEW SubMesh(); |
95 | 0 | sub->parent = this; |
96 | |
|
97 | 0 | mSubMeshList.push_back(sub); |
98 | |
|
99 | 0 | if (isLoaded()) |
100 | 0 | _dirtyState(); |
101 | |
|
102 | 0 | return sub; |
103 | 0 | } |
104 | | //----------------------------------------------------------------------- |
105 | | SubMesh* Mesh::createSubMesh(const String& name) |
106 | 0 | { |
107 | 0 | SubMesh *sub = createSubMesh(); |
108 | 0 | nameSubMesh(name, (ushort)mSubMeshList.size()-1); |
109 | 0 | return sub ; |
110 | 0 | } |
111 | | //----------------------------------------------------------------------- |
112 | | void Mesh::destroySubMesh(unsigned short index) |
113 | 0 | { |
114 | 0 | OgreAssert(index < mSubMeshList.size(), ""); |
115 | 0 | SubMeshList::iterator i = mSubMeshList.begin(); |
116 | 0 | std::advance(i, index); |
117 | 0 | OGRE_DELETE *i; |
118 | 0 | mSubMeshList.erase(i); |
119 | | |
120 | | // Fix up any name/index entries |
121 | 0 | for(SubMeshNameMap::iterator ni = mSubMeshNameMap.begin(); ni != mSubMeshNameMap.end();) |
122 | 0 | { |
123 | 0 | if (ni->second == index) |
124 | 0 | { |
125 | 0 | SubMeshNameMap::iterator eraseIt = ni++; |
126 | 0 | mSubMeshNameMap.erase(eraseIt); |
127 | 0 | } |
128 | 0 | else |
129 | 0 | { |
130 | | // reduce indexes following |
131 | 0 | if (ni->second > index) |
132 | 0 | ni->second = ni->second - 1; |
133 | |
|
134 | 0 | ++ni; |
135 | 0 | } |
136 | 0 | } |
137 | | |
138 | | // fix edge list data by simply recreating all edge lists |
139 | 0 | if( mEdgeListsBuilt) |
140 | 0 | { |
141 | 0 | this->freeEdgeList(); |
142 | 0 | this->buildEdgeList(); |
143 | 0 | } |
144 | |
|
145 | 0 | if (isLoaded()) |
146 | 0 | _dirtyState(); |
147 | |
|
148 | 0 | } |
149 | | //----------------------------------------------------------------------- |
150 | | void Mesh::destroySubMesh(const String& name) |
151 | 0 | { |
152 | 0 | unsigned short index = _getSubMeshIndex(name); |
153 | 0 | destroySubMesh(index); |
154 | 0 | } |
155 | | //--------------------------------------------------------------------- |
156 | | void Mesh::nameSubMesh(const String& name, ushort index) |
157 | 0 | { |
158 | 0 | mSubMeshNameMap[name] = index ; |
159 | 0 | } |
160 | | |
161 | | //--------------------------------------------------------------------- |
162 | | void Mesh::unnameSubMesh(const String& name) |
163 | 0 | { |
164 | 0 | SubMeshNameMap::iterator i = mSubMeshNameMap.find(name); |
165 | 0 | if (i != mSubMeshNameMap.end()) |
166 | 0 | mSubMeshNameMap.erase(i); |
167 | 0 | } |
168 | | //----------------------------------------------------------------------- |
169 | | SubMesh* Mesh::getSubMesh(const String& name) const |
170 | 0 | { |
171 | 0 | ushort index = _getSubMeshIndex(name); |
172 | 0 | return getSubMesh(index); |
173 | 0 | } |
174 | | //----------------------------------------------------------------------- |
175 | | void Mesh::postLoadImpl(void) |
176 | 0 | { |
177 | | // Prepare for shadow volumes? |
178 | 0 | if (MeshManager::getSingleton().getPrepareAllMeshesForShadowVolumes()) |
179 | 0 | { |
180 | 0 | if (mEdgeListsBuilt || mAutoBuildEdgeLists) |
181 | 0 | { |
182 | 0 | prepareForShadowVolume(); |
183 | 0 | } |
184 | |
|
185 | 0 | if (!mEdgeListsBuilt && mAutoBuildEdgeLists) |
186 | 0 | { |
187 | 0 | buildEdgeList(); |
188 | 0 | } |
189 | 0 | } |
190 | 0 | #if !OGRE_NO_MESHLOD |
191 | | // The loading process accesses LOD usages directly, so |
192 | | // transformation of user values must occur after loading is complete. |
193 | | |
194 | | // Transform user LOD values (starting at index 1, no need to transform base value) |
195 | 0 | for (auto& m : mMeshLodUsageList) |
196 | 0 | m.value = mLodStrategy->transformUserValue(m.userValue); |
197 | | // Rewrite first value |
198 | 0 | mMeshLodUsageList[0].value = mLodStrategy->getBaseValue(); |
199 | 0 | #endif |
200 | 0 | } |
201 | | //----------------------------------------------------------------------- |
202 | | void Mesh::prepareImpl() |
203 | 0 | { |
204 | | // Load from specified 'name' |
205 | 0 | if (getCreator()->getVerbose()) |
206 | 0 | LogManager::getSingleton().logMessage("Mesh: Loading "+mName+"."); |
207 | |
|
208 | 0 | mFreshFromDisk = |
209 | 0 | ResourceGroupManager::getSingleton().openResource( |
210 | 0 | mName, mGroup, this); |
211 | | |
212 | | // fully prebuffer into host RAM |
213 | 0 | mFreshFromDisk = DataStreamPtr(OGRE_NEW MemoryDataStream(mName,mFreshFromDisk)); |
214 | 0 | } |
215 | | //----------------------------------------------------------------------- |
216 | | void Mesh::unprepareImpl() |
217 | 0 | { |
218 | 0 | mFreshFromDisk.reset(); |
219 | 0 | } |
220 | | void Mesh::loadImpl() |
221 | 0 | { |
222 | | // If the only copy is local on the stack, it will be cleaned |
223 | | // up reliably in case of exceptions, etc |
224 | 0 | DataStreamPtr data(mFreshFromDisk); |
225 | 0 | mFreshFromDisk.reset(); |
226 | |
|
227 | 0 | if (!data) { |
228 | 0 | OGRE_EXCEPT(Exception::ERR_INVALID_STATE, |
229 | 0 | "Data doesn't appear to have been prepared in " + mName, |
230 | 0 | "Mesh::loadImpl()"); |
231 | 0 | } |
232 | | |
233 | 0 | String baseName, strExt; |
234 | 0 | StringUtil::splitBaseFilename(mName, baseName, strExt); |
235 | 0 | auto codec = Codec::getCodec(strExt); |
236 | 0 | if (!codec) |
237 | 0 | OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "No codec found to load " + mName); |
238 | | |
239 | 0 | codec->decode(data, this); |
240 | 0 | } |
241 | | |
242 | | //----------------------------------------------------------------------- |
243 | | void Mesh::unloadImpl() |
244 | 0 | { |
245 | | // Teardown submeshes |
246 | 0 | for (auto & i : mSubMeshList) |
247 | 0 | { |
248 | 0 | OGRE_DELETE i; |
249 | 0 | } |
250 | 0 | if (sharedVertexData) |
251 | 0 | { |
252 | 0 | resetVertexData(); |
253 | 0 | } |
254 | | // Clear SubMesh lists |
255 | 0 | mSubMeshList.clear(); |
256 | 0 | mSubMeshNameMap.clear(); |
257 | |
|
258 | 0 | freeEdgeList(); |
259 | 0 | #if !OGRE_NO_MESHLOD |
260 | | // Removes all LOD data |
261 | 0 | removeLodLevels(); |
262 | 0 | #endif |
263 | 0 | mPreparedForShadowVolumes = false; |
264 | | |
265 | | // remove all poses & animations |
266 | 0 | removeAllAnimations(); |
267 | 0 | removeAllPoses(); |
268 | | |
269 | | // Clear bone assignments |
270 | 0 | mBoneAssignments.clear(); |
271 | 0 | mBoneAssignmentsOutOfDate = false; |
272 | | |
273 | | // Removes reference to skeleton |
274 | 0 | mSkeleton.reset(); |
275 | 0 | } |
276 | | //----------------------------------------------------------------------- |
277 | | void Mesh::reload(LoadingFlags flags) |
278 | 0 | { |
279 | 0 | bool wasPreparedForShadowVolumes = mPreparedForShadowVolumes; |
280 | 0 | bool wasEdgeListsBuilt = mEdgeListsBuilt; |
281 | 0 | bool wasAutoBuildEdgeLists = mAutoBuildEdgeLists; |
282 | |
|
283 | 0 | Resource::reload(flags); |
284 | |
|
285 | 0 | if(flags & LF_PRESERVE_STATE) |
286 | 0 | { |
287 | 0 | if(wasPreparedForShadowVolumes) |
288 | 0 | prepareForShadowVolume(); |
289 | 0 | if(wasEdgeListsBuilt) |
290 | 0 | buildEdgeList(); |
291 | 0 | setAutoBuildEdgeLists(wasAutoBuildEdgeLists); |
292 | 0 | } |
293 | 0 | } |
294 | | //----------------------------------------------------------------------- |
295 | | MeshPtr Mesh::clone(const String& newName, const String& newGroup) |
296 | 0 | { |
297 | | // This is a bit like a copy constructor, but with the additional aspect of registering the clone with |
298 | | // the MeshManager |
299 | | |
300 | | // New Mesh is assumed to be manually defined rather than loaded since you're cloning it for a reason |
301 | 0 | String theGroup = newGroup.empty() ? this->getGroup() : newGroup; |
302 | 0 | MeshPtr newMesh = MeshManager::getSingleton().createManual(newName, theGroup); |
303 | |
|
304 | 0 | if(!newMesh) // interception by collision handler |
305 | 0 | return newMesh; |
306 | | |
307 | 0 | newMesh->mBufferManager = mBufferManager; |
308 | 0 | newMesh->mVertexBufferUsage = mVertexBufferUsage; |
309 | 0 | newMesh->mIndexBufferUsage = mIndexBufferUsage; |
310 | 0 | newMesh->mVertexBufferShadowBuffer = mVertexBufferShadowBuffer; |
311 | 0 | newMesh->mIndexBufferShadowBuffer = mIndexBufferShadowBuffer; |
312 | | |
313 | | // Copy submeshes first |
314 | 0 | for (auto *subi : mSubMeshList) |
315 | 0 | { |
316 | 0 | subi->clone("", newMesh.get()); |
317 | 0 | } |
318 | | |
319 | | // Copy shared geometry and index map, if any |
320 | 0 | if (sharedVertexData) |
321 | 0 | { |
322 | 0 | newMesh->resetVertexData(sharedVertexData->clone(true, mBufferManager)); |
323 | 0 | newMesh->sharedBlendIndexToBoneIndexMap = sharedBlendIndexToBoneIndexMap; |
324 | 0 | } |
325 | | |
326 | | // Copy submesh names |
327 | 0 | newMesh->mSubMeshNameMap = mSubMeshNameMap ; |
328 | | // Copy any bone assignments |
329 | 0 | newMesh->mBoneAssignments = mBoneAssignments; |
330 | 0 | newMesh->mBoneAssignmentsOutOfDate = mBoneAssignmentsOutOfDate; |
331 | | // Copy bounds |
332 | 0 | newMesh->mAABB = mAABB; |
333 | 0 | newMesh->mBoundRadius = mBoundRadius; |
334 | 0 | newMesh->mBoneBoundingRadius = mBoneBoundingRadius; |
335 | 0 | newMesh->mAutoBuildEdgeLists = mAutoBuildEdgeLists; |
336 | 0 | newMesh->mEdgeListsBuilt = mEdgeListsBuilt; |
337 | |
|
338 | 0 | #if !OGRE_NO_MESHLOD |
339 | 0 | newMesh->mHasManualLodLevel = mHasManualLodLevel; |
340 | 0 | newMesh->mLodStrategy = mLodStrategy; |
341 | 0 | newMesh->mNumLods = mNumLods; |
342 | 0 | newMesh->mMeshLodUsageList = mMeshLodUsageList; |
343 | 0 | #endif |
344 | | // Unreference edge lists, otherwise we'll delete the same lot twice, build on demand |
345 | 0 | MeshLodUsageList::iterator lodOldi; |
346 | 0 | lodOldi = mMeshLodUsageList.begin(); |
347 | 0 | for (auto& lodi : newMesh->mMeshLodUsageList) { |
348 | 0 | MeshLodUsage& lod = *lodOldi; |
349 | 0 | lodi.manualName = lod.manualName; |
350 | 0 | lodi.userValue = lod.userValue; |
351 | 0 | lodi.value = lod.value; |
352 | 0 | if (lod.edgeData) { |
353 | 0 | lodi.edgeData = lod.edgeData->clone(); |
354 | 0 | } |
355 | 0 | ++lodOldi; |
356 | 0 | } |
357 | |
|
358 | 0 | newMesh->mSkeleton = mSkeleton; |
359 | | |
360 | | // Keep prepared shadow volume info (buffers may already be prepared) |
361 | 0 | newMesh->mPreparedForShadowVolumes = mPreparedForShadowVolumes; |
362 | |
|
363 | 0 | newMesh->mEdgeListsBuilt = mEdgeListsBuilt; |
364 | | |
365 | | // Clone vertex animation |
366 | 0 | for (auto & i : mAnimationsList) |
367 | 0 | { |
368 | 0 | Animation *newAnim = i.second->clone(i.second->getName()); |
369 | 0 | newMesh->mAnimationsList[i.second->getName()] = newAnim; |
370 | 0 | } |
371 | | // Clone pose list |
372 | 0 | for (auto & i : mPoseList) |
373 | 0 | { |
374 | 0 | Pose* newPose = i->clone(); |
375 | 0 | newMesh->mPoseList.push_back(newPose); |
376 | 0 | } |
377 | 0 | newMesh->mSharedVertexDataAnimationType = mSharedVertexDataAnimationType; |
378 | 0 | newMesh->mAnimationTypesDirty = true; |
379 | |
|
380 | 0 | newMesh->load(); |
381 | 0 | newMesh->touch(); |
382 | |
|
383 | 0 | return newMesh; |
384 | 0 | } |
385 | | //----------------------------------------------------------------------- |
386 | | const AxisAlignedBox& Mesh::getBounds(void) const |
387 | 0 | { |
388 | 0 | return mAABB; |
389 | 0 | } |
390 | | //----------------------------------------------------------------------- |
391 | | void Mesh::_setBounds(const AxisAlignedBox& bounds, bool pad) |
392 | 0 | { |
393 | 0 | mAABB = bounds; |
394 | 0 | mBoundRadius = Math::boundingRadiusFromAABB(mAABB); |
395 | |
|
396 | 0 | if( mAABB.isFinite() ) |
397 | 0 | { |
398 | 0 | Vector3 max = mAABB.getMaximum(); |
399 | 0 | Vector3 min = mAABB.getMinimum(); |
400 | |
|
401 | 0 | if (pad) |
402 | 0 | { |
403 | | // Pad out the AABB a little, helps with most bounds tests |
404 | 0 | Vector3 scaler = (max - min) * MeshManager::getSingleton().getBoundsPaddingFactor(); |
405 | 0 | mAABB.setExtents(min - scaler, max + scaler); |
406 | | // Pad out the sphere a little too |
407 | 0 | mBoundRadius = mBoundRadius + (mBoundRadius * MeshManager::getSingleton().getBoundsPaddingFactor()); |
408 | 0 | } |
409 | 0 | } |
410 | 0 | } |
411 | | //----------------------------------------------------------------------- |
412 | | void Mesh::_setBoundingSphereRadius(Real radius) |
413 | 0 | { |
414 | 0 | mBoundRadius = radius; |
415 | 0 | } |
416 | | //----------------------------------------------------------------------- |
417 | | void Mesh::_setBoneBoundingRadius(Real radius) |
418 | 0 | { |
419 | 0 | mBoneBoundingRadius = radius; |
420 | 0 | } |
421 | | //----------------------------------------------------------------------- |
422 | | void Mesh::_updateBoundsFromVertexBuffers(bool pad) |
423 | 0 | { |
424 | 0 | bool extendOnly = false; // First time we need full AABB of the given submesh, but on the second call just extend that one. |
425 | 0 | if (sharedVertexData){ |
426 | 0 | _calcBoundsFromVertexBuffer(sharedVertexData, mAABB, mBoundRadius, extendOnly); |
427 | 0 | extendOnly = true; |
428 | 0 | } |
429 | 0 | for (auto & i : mSubMeshList){ |
430 | 0 | if (i->vertexData){ |
431 | 0 | _calcBoundsFromVertexBuffer(i->vertexData, mAABB, mBoundRadius, extendOnly); |
432 | 0 | extendOnly = true; |
433 | 0 | } |
434 | 0 | } |
435 | 0 | if (pad) |
436 | 0 | { |
437 | 0 | Vector3 max = mAABB.getMaximum(); |
438 | 0 | Vector3 min = mAABB.getMinimum(); |
439 | | // Pad out the AABB a little, helps with most bounds tests |
440 | 0 | Vector3 scaler = (max - min) * MeshManager::getSingleton().getBoundsPaddingFactor(); |
441 | 0 | mAABB.setExtents(min - scaler, max + scaler); |
442 | | // Pad out the sphere a little too |
443 | 0 | mBoundRadius = mBoundRadius + (mBoundRadius * MeshManager::getSingleton().getBoundsPaddingFactor()); |
444 | 0 | } |
445 | 0 | } |
446 | | void Mesh::_calcBoundsFromVertexBuffer(VertexData* vertexData, AxisAlignedBox& outAABB, Real& outRadius, bool extendOnly /*= false*/) |
447 | 0 | { |
448 | 0 | if (vertexData->vertexCount == 0) { |
449 | 0 | if (!extendOnly) { |
450 | 0 | outAABB = AxisAlignedBox(Vector3::ZERO, Vector3::ZERO); |
451 | 0 | outRadius = 0; |
452 | 0 | } |
453 | 0 | return; |
454 | 0 | } |
455 | 0 | const VertexElement* elemPos = vertexData->vertexDeclaration->findElementBySemantic(VES_POSITION); |
456 | 0 | HardwareVertexBufferSharedPtr vbuf = vertexData->vertexBufferBinding->getBuffer(elemPos->getSource()); |
457 | 0 | HardwareBufferLockGuard vertexLock(vbuf, HardwareBuffer::HBL_READ_ONLY); |
458 | 0 | unsigned char* vertex = static_cast<unsigned char*>(vertexLock.pData); |
459 | |
|
460 | 0 | if (!extendOnly){ |
461 | | // init values |
462 | 0 | outRadius = 0; |
463 | 0 | float* pFloat; |
464 | 0 | elemPos->baseVertexPointerToElement(vertex, &pFloat); |
465 | 0 | Vector3 basePos(pFloat[0], pFloat[1], pFloat[2]); |
466 | 0 | outAABB.setExtents(basePos, basePos); |
467 | 0 | } |
468 | 0 | size_t vSize = vbuf->getVertexSize(); |
469 | 0 | unsigned char* vEnd = vertex + vertexData->vertexCount * vSize; |
470 | 0 | Real radiusSqr = outRadius * outRadius; |
471 | | // Loop through all vertices. |
472 | 0 | for (; vertex < vEnd; vertex += vSize) { |
473 | 0 | float* pFloat; |
474 | 0 | elemPos->baseVertexPointerToElement(vertex, &pFloat); |
475 | 0 | Vector3 pos(pFloat[0], pFloat[1], pFloat[2]); |
476 | 0 | outAABB.getMinimum().makeFloor(pos); |
477 | 0 | outAABB.getMaximum().makeCeil(pos); |
478 | 0 | radiusSqr = std::max<Real>(radiusSqr, pos.squaredLength()); |
479 | 0 | } |
480 | 0 | outRadius = std::sqrt(radiusSqr); |
481 | 0 | } |
482 | | //----------------------------------------------------------------------- |
483 | | void Mesh::setSkeletonName(const String& skelName) |
484 | 0 | { |
485 | 0 | if (skelName != getSkeletonName()) |
486 | 0 | { |
487 | 0 | if (skelName.empty()) |
488 | 0 | { |
489 | | // No skeleton |
490 | 0 | mSkeleton.reset(); |
491 | 0 | } |
492 | 0 | else |
493 | 0 | { |
494 | | // Load skeleton |
495 | 0 | try { |
496 | 0 | mSkeleton = static_pointer_cast<Skeleton>(SkeletonManager::getSingleton().load(skelName, mGroup)); |
497 | 0 | } |
498 | 0 | catch (...) |
499 | 0 | { |
500 | 0 | mSkeleton.reset(); |
501 | | // Log this error |
502 | 0 | String msg = "Unable to load skeleton '"; |
503 | 0 | msg += skelName + "' for Mesh '" + mName + "'. This Mesh will not be animated."; |
504 | 0 | LogManager::getSingleton().logError(msg); |
505 | |
|
506 | 0 | } |
507 | | |
508 | |
|
509 | 0 | } |
510 | 0 | if (isLoaded()) |
511 | 0 | _dirtyState(); |
512 | 0 | } |
513 | 0 | } |
514 | | //----------------------------------------------------------------------- |
515 | | void Mesh::addBoneAssignment(const VertexBoneAssignment& vertBoneAssign) |
516 | 0 | { |
517 | 0 | mBoneAssignments.emplace(vertBoneAssign.vertexIndex, vertBoneAssign); |
518 | 0 | mBoneAssignmentsOutOfDate = true; |
519 | 0 | } |
520 | | //----------------------------------------------------------------------- |
521 | | void Mesh::clearBoneAssignments(void) |
522 | 0 | { |
523 | 0 | mBoneAssignments.clear(); |
524 | 0 | mBoneAssignmentsOutOfDate = true; |
525 | 0 | } |
526 | | //----------------------------------------------------------------------- |
527 | | void Mesh::_initAnimationState(AnimationStateSet* animSet) |
528 | 0 | { |
529 | | // Animation states for skeletal animation |
530 | 0 | if (mSkeleton) |
531 | 0 | { |
532 | | // Delegate to Skeleton |
533 | 0 | mSkeleton->_initAnimationState(animSet); |
534 | | |
535 | | // Take the opportunity to update the compiled bone assignments |
536 | 0 | _updateCompiledBoneAssignments(); |
537 | |
|
538 | 0 | } |
539 | | |
540 | | // Animation states for vertex animation |
541 | 0 | for (auto & i : mAnimationsList) |
542 | 0 | { |
543 | | // Only create a new animation state if it doesn't exist |
544 | | // We can have the same named animation in both skeletal and vertex |
545 | | // with a shared animation state affecting both, for combined effects |
546 | | // The animations should be the same length if this feature is used! |
547 | 0 | if (!animSet->hasAnimationState(i.second->getName())) |
548 | 0 | { |
549 | 0 | animSet->createAnimationState(i.second->getName(), 0.0, |
550 | 0 | i.second->getLength()); |
551 | 0 | } |
552 | |
|
553 | 0 | } |
554 | |
|
555 | 0 | } |
556 | | //--------------------------------------------------------------------- |
557 | | void Mesh::_refreshAnimationState(AnimationStateSet* animSet) |
558 | 0 | { |
559 | 0 | if (mSkeleton) |
560 | 0 | { |
561 | 0 | mSkeleton->_refreshAnimationState(animSet); |
562 | 0 | } |
563 | | |
564 | | // Merge in any new vertex animations |
565 | 0 | for (auto& i : mAnimationsList) |
566 | 0 | { |
567 | | // Create animation at time index 0, default params mean this has weight 1 and is disabled |
568 | 0 | auto anim = i.second; |
569 | 0 | const String& animName = anim->getName(); |
570 | 0 | if (!animSet->hasAnimationState(animName)) |
571 | 0 | { |
572 | 0 | animSet->createAnimationState(animName, 0.0, anim->getLength()); |
573 | 0 | } |
574 | 0 | else |
575 | 0 | { |
576 | | // Update length incase changed |
577 | 0 | AnimationState* animState = animSet->getAnimationState(animName); |
578 | 0 | animState->setLength(anim->getLength()); |
579 | 0 | animState->setTimePosition(std::min(anim->getLength(), animState->getTimePosition())); |
580 | 0 | } |
581 | 0 | } |
582 | 0 | } |
583 | | //----------------------------------------------------------------------- |
584 | | void Mesh::_updateCompiledBoneAssignments(void) |
585 | 0 | { |
586 | 0 | if (mBoneAssignmentsOutOfDate) |
587 | 0 | _compileBoneAssignments(); |
588 | |
|
589 | 0 | for (auto *m : mSubMeshList) |
590 | 0 | { |
591 | 0 | if (m->mBoneAssignmentsOutOfDate) |
592 | 0 | { |
593 | 0 | m->_compileBoneAssignments(); |
594 | 0 | } |
595 | 0 | } |
596 | 0 | } |
597 | | //----------------------------------------------------------------------- |
598 | | typedef std::multimap<Real, Mesh::VertexBoneAssignmentList::iterator> WeightIteratorMap; |
599 | | unsigned short Mesh::_rationaliseBoneAssignments(size_t vertexCount, Mesh::VertexBoneAssignmentList& assignments) |
600 | 0 | { |
601 | | // Iterate through, finding the largest # bones per vertex |
602 | 0 | unsigned short maxBones = 0; |
603 | 0 | bool existsNonSkinnedVertices = false; |
604 | 0 | VertexBoneAssignmentList::iterator i; |
605 | |
|
606 | 0 | for (size_t v = 0; v < vertexCount; ++v) |
607 | 0 | { |
608 | | // Get number of entries for this vertex |
609 | 0 | short currBones = static_cast<unsigned short>(assignments.count(v)); |
610 | 0 | if (currBones <= 0) |
611 | 0 | existsNonSkinnedVertices = true; |
612 | | |
613 | | // Deal with max bones update |
614 | | // (note this will record maxBones even if they exceed limit) |
615 | 0 | if (maxBones < currBones) |
616 | 0 | maxBones = currBones; |
617 | | // does the number of bone assignments exceed limit? |
618 | 0 | if (currBones > OGRE_MAX_BLEND_WEIGHTS) |
619 | 0 | { |
620 | | // To many bone assignments on this vertex |
621 | | // Find the start & end (end is in iterator terms ie exclusive) |
622 | 0 | std::pair<VertexBoneAssignmentList::iterator, VertexBoneAssignmentList::iterator> range; |
623 | | // map to sort by weight |
624 | 0 | WeightIteratorMap weightToAssignmentMap; |
625 | 0 | range = assignments.equal_range(v); |
626 | | // Add all the assignments to map |
627 | 0 | for (i = range.first; i != range.second; ++i) |
628 | 0 | { |
629 | | // insert value weight->iterator |
630 | 0 | weightToAssignmentMap.emplace(i->second.weight, i); |
631 | 0 | } |
632 | | // Reverse iterate over weight map, remove lowest n |
633 | 0 | unsigned short numToRemove = currBones - OGRE_MAX_BLEND_WEIGHTS; |
634 | 0 | WeightIteratorMap::iterator remIt = weightToAssignmentMap.begin(); |
635 | |
|
636 | 0 | while (numToRemove--) |
637 | 0 | { |
638 | | // Erase this one |
639 | 0 | assignments.erase(remIt->second); |
640 | 0 | ++remIt; |
641 | 0 | } |
642 | 0 | } // if (currBones > OGRE_MAX_BLEND_WEIGHTS) |
643 | | |
644 | | // Make sure the weights are normalised |
645 | | // Do this irrespective of whether we had to remove assignments or not |
646 | | // since it gives us a guarantee that weights are normalised |
647 | | // We assume this, so it's a good idea since some modellers may not |
648 | 0 | std::pair<VertexBoneAssignmentList::iterator, VertexBoneAssignmentList::iterator> normalise_range = assignments.equal_range(v); |
649 | 0 | Real totalWeight = 0; |
650 | | // Find total first |
651 | 0 | for (i = normalise_range.first; i != normalise_range.second; ++i) |
652 | 0 | { |
653 | 0 | totalWeight += i->second.weight; |
654 | 0 | } |
655 | | // Now normalise if total weight is outside tolerance |
656 | 0 | if (!Math::RealEqual(totalWeight, 1.0f)) |
657 | 0 | { |
658 | 0 | for (i = normalise_range.first; i != normalise_range.second; ++i) |
659 | 0 | { |
660 | 0 | i->second.weight = i->second.weight / totalWeight; |
661 | 0 | } |
662 | 0 | } |
663 | |
|
664 | 0 | } |
665 | |
|
666 | 0 | if (maxBones > OGRE_MAX_BLEND_WEIGHTS) |
667 | 0 | { |
668 | | // Warn that we've reduced bone assignments |
669 | 0 | LogManager::getSingleton().logWarning("the mesh '" + mName + "' " |
670 | 0 | "includes vertices with more than " + |
671 | 0 | StringConverter::toString(OGRE_MAX_BLEND_WEIGHTS) + " bone assignments. " |
672 | 0 | "The lowest weighted assignments beyond this limit have been removed, so " |
673 | 0 | "your animation may look slightly different. To eliminate this, reduce " |
674 | 0 | "the number of bone assignments per vertex on your mesh to " + |
675 | 0 | StringConverter::toString(OGRE_MAX_BLEND_WEIGHTS) + "."); |
676 | | // we've adjusted them down to the max |
677 | 0 | maxBones = OGRE_MAX_BLEND_WEIGHTS; |
678 | |
|
679 | 0 | } |
680 | |
|
681 | 0 | if (existsNonSkinnedVertices) |
682 | 0 | { |
683 | | // Warn that we've non-skinned vertices |
684 | 0 | LogManager::getSingleton().logWarning("the mesh '" + mName + "' " |
685 | 0 | "includes vertices without bone assignments. Those vertices will " |
686 | 0 | "transform to wrong position when skeletal animation enabled. " |
687 | 0 | "To eliminate this, assign at least one bone assignment per vertex " |
688 | 0 | "on your mesh."); |
689 | 0 | } |
690 | |
|
691 | 0 | return maxBones; |
692 | 0 | } |
693 | | //----------------------------------------------------------------------- |
694 | | void Mesh::_compileBoneAssignments(void) |
695 | 0 | { |
696 | 0 | if (sharedVertexData) |
697 | 0 | { |
698 | 0 | unsigned short maxBones = _rationaliseBoneAssignments(sharedVertexData->vertexCount, mBoneAssignments); |
699 | |
|
700 | 0 | if (maxBones != 0) |
701 | 0 | { |
702 | 0 | compileBoneAssignments(mBoneAssignments, maxBones, |
703 | 0 | sharedBlendIndexToBoneIndexMap, sharedVertexData); |
704 | 0 | } |
705 | 0 | } |
706 | 0 | mBoneAssignmentsOutOfDate = false; |
707 | 0 | } |
708 | | //--------------------------------------------------------------------- |
709 | | void Mesh::buildIndexMap(const VertexBoneAssignmentList& boneAssignments, |
710 | | IndexMap& boneIndexToBlendIndexMap, IndexMap& blendIndexToBoneIndexMap) |
711 | 0 | { |
712 | 0 | if (boneAssignments.empty()) |
713 | 0 | { |
714 | | // Just in case |
715 | 0 | boneIndexToBlendIndexMap.clear(); |
716 | 0 | blendIndexToBoneIndexMap.clear(); |
717 | 0 | return; |
718 | 0 | } |
719 | | |
720 | 0 | typedef std::set<unsigned short> BoneIndexSet; |
721 | 0 | BoneIndexSet usedBoneIndices; |
722 | | |
723 | | // Collect actually used bones |
724 | 0 | for (auto& itVBA : boneAssignments) |
725 | 0 | { |
726 | 0 | usedBoneIndices.insert(itVBA.second.boneIndex); |
727 | 0 | } |
728 | | |
729 | | // Allocate space for index map |
730 | 0 | blendIndexToBoneIndexMap.resize(usedBoneIndices.size()); |
731 | 0 | boneIndexToBlendIndexMap.resize(*usedBoneIndices.rbegin() + 1); |
732 | | |
733 | | // Make index map between bone index and blend index |
734 | 0 | unsigned short blendIndex = 0; |
735 | 0 | for (auto& itBoneIndex : usedBoneIndices) |
736 | 0 | { |
737 | 0 | boneIndexToBlendIndexMap[itBoneIndex] = blendIndex; |
738 | 0 | blendIndexToBoneIndexMap[blendIndex] = itBoneIndex; |
739 | 0 | ++blendIndex; |
740 | 0 | } |
741 | 0 | } |
742 | | //--------------------------------------------------------------------- |
743 | | void Mesh::compileBoneAssignments( |
744 | | const VertexBoneAssignmentList& boneAssignments, |
745 | | unsigned short numBlendWeightsPerVertex, |
746 | | IndexMap& blendIndexToBoneIndexMap, |
747 | | VertexData* targetVertexData) |
748 | 0 | { |
749 | | // Create or reuse blend weight / indexes buffer |
750 | | // Indices are always a UBYTE4 no matter how many weights per vertex |
751 | 0 | VertexDeclaration* decl = targetVertexData->vertexDeclaration; |
752 | 0 | VertexBufferBinding* bind = targetVertexData->vertexBufferBinding; |
753 | 0 | unsigned short bindIndex; |
754 | | |
755 | | // Build the index map brute-force. It's possible to store the index map |
756 | | // in .mesh, but maybe trivial. |
757 | 0 | IndexMap boneIndexToBlendIndexMap; |
758 | 0 | buildIndexMap(boneAssignments, boneIndexToBlendIndexMap, blendIndexToBoneIndexMap); |
759 | |
|
760 | 0 | const VertexElement* testElem = |
761 | 0 | decl->findElementBySemantic(VES_BLEND_INDICES); |
762 | 0 | if (testElem) |
763 | 0 | { |
764 | | // Already have a buffer, unset it & delete elements |
765 | 0 | bindIndex = testElem->getSource(); |
766 | | // unset will cause deletion of buffer |
767 | 0 | bind->unsetBinding(bindIndex); |
768 | 0 | decl->removeElement(VES_BLEND_INDICES); |
769 | 0 | decl->removeElement(VES_BLEND_WEIGHTS); |
770 | 0 | } |
771 | 0 | else |
772 | 0 | { |
773 | | // Get new binding |
774 | 0 | bindIndex = bind->getNextIndex(); |
775 | 0 | } |
776 | | // type of Weights is settable on the MeshManager. |
777 | 0 | VertexElementType weightsBaseType = MeshManager::getSingleton().getBlendWeightsBaseElementType(); |
778 | 0 | VertexElementType weightsVertexElemType = VertexElement::multiplyTypeCount( weightsBaseType, numBlendWeightsPerVertex ); |
779 | 0 | HardwareVertexBufferSharedPtr vbuf = getHardwareBufferManager()->createVertexBuffer( |
780 | 0 | sizeof( unsigned char ) * 4 + VertexElement::getTypeSize( weightsVertexElemType ), |
781 | 0 | targetVertexData->vertexCount, |
782 | 0 | HardwareBuffer::HBU_STATIC_WRITE_ONLY, |
783 | 0 | true // use shadow buffer |
784 | 0 | ); |
785 | | // bind new buffer |
786 | 0 | bind->setBinding(bindIndex, vbuf); |
787 | 0 | const VertexElement *pIdxElem, *pWeightElem; |
788 | | |
789 | | // add new vertex elements |
790 | | // Note, insert directly after all elements using the same source as |
791 | | // position to abide by pre-Dx9 format restrictions |
792 | 0 | const VertexElement* firstElem = decl->getElement(0); |
793 | 0 | if(firstElem->getSemantic() == VES_POSITION) |
794 | 0 | { |
795 | 0 | unsigned short insertPoint = 1; |
796 | 0 | while (insertPoint < decl->getElementCount() && |
797 | 0 | decl->getElement(insertPoint)->getSource() == firstElem->getSource()) |
798 | 0 | { |
799 | 0 | ++insertPoint; |
800 | 0 | } |
801 | 0 | const VertexElement& idxElem = |
802 | 0 | decl->insertElement(insertPoint, bindIndex, 0, VET_UBYTE4, VES_BLEND_INDICES); |
803 | 0 | const VertexElement& wtElem = |
804 | 0 | decl->insertElement(insertPoint+1, bindIndex, sizeof(unsigned char)*4, weightsVertexElemType, VES_BLEND_WEIGHTS); |
805 | 0 | pIdxElem = &idxElem; |
806 | 0 | pWeightElem = &wtElem; |
807 | 0 | } |
808 | 0 | else |
809 | 0 | { |
810 | | // Position is not the first semantic, therefore this declaration is |
811 | | // not pre-Dx9 compatible anyway, so just tack it on the end |
812 | 0 | const VertexElement& idxElem = |
813 | 0 | decl->addElement(bindIndex, 0, VET_UBYTE4, VES_BLEND_INDICES); |
814 | 0 | const VertexElement& wtElem = |
815 | 0 | decl->addElement(bindIndex, sizeof(unsigned char)*4, weightsVertexElemType, VES_BLEND_WEIGHTS ); |
816 | 0 | pIdxElem = &idxElem; |
817 | 0 | pWeightElem = &wtElem; |
818 | 0 | } |
819 | |
|
820 | 0 | unsigned int maxIntWt = 0; |
821 | | // keeping a switch out of the loop |
822 | 0 | switch ( weightsBaseType ) |
823 | 0 | { |
824 | 0 | default: |
825 | 0 | OgreAssert(false, "Invalid BlendWeightsBaseElementType"); |
826 | 0 | break; |
827 | 0 | case VET_FLOAT1: |
828 | 0 | break; |
829 | 0 | case VET_UBYTE4_NORM: |
830 | 0 | maxIntWt = 0xff; |
831 | 0 | break; |
832 | 0 | case VET_USHORT2_NORM: |
833 | 0 | maxIntWt = 0xffff; |
834 | 0 | break; |
835 | 0 | case VET_SHORT2_NORM: |
836 | 0 | maxIntWt = 0x7fff; |
837 | 0 | break; |
838 | 0 | } |
839 | | // Assign data |
840 | 0 | size_t v; |
841 | 0 | VertexBoneAssignmentList::const_iterator i, iend; |
842 | 0 | i = boneAssignments.begin(); |
843 | 0 | iend = boneAssignments.end(); |
844 | 0 | HardwareBufferLockGuard vertexLock(vbuf, HardwareBuffer::HBL_DISCARD); |
845 | 0 | unsigned char *pBase = static_cast<unsigned char*>(vertexLock.pData); |
846 | | // Iterate by vertex |
847 | 0 | for (v = 0; v < targetVertexData->vertexCount; ++v) |
848 | 0 | { |
849 | | // collect the indices/weights in these arrays |
850 | 0 | unsigned char indices[ 4 ] = { 0, 0, 0, 0 }; |
851 | 0 | float weights[ 4 ] = { 1.0f, 0.0f, 0.0f, 0.0f }; |
852 | 0 | for (unsigned short bone = 0; bone < numBlendWeightsPerVertex; ++bone) |
853 | 0 | { |
854 | | // Do we still have data for this vertex? |
855 | 0 | if (i != iend && i->second.vertexIndex == v) |
856 | 0 | { |
857 | | // If so, grab weight and index |
858 | 0 | weights[ bone ] = i->second.weight; |
859 | 0 | indices[ bone ] = static_cast<unsigned char>( boneIndexToBlendIndexMap[ i->second.boneIndex ] ); |
860 | 0 | ++i; |
861 | 0 | } |
862 | 0 | } |
863 | | // if weights are integers, |
864 | 0 | if ( weightsBaseType != VET_FLOAT1 ) |
865 | 0 | { |
866 | | // pack the float weights into shorts/bytes |
867 | 0 | unsigned int intWeights[ 4 ]; |
868 | 0 | unsigned int sum = 0; |
869 | 0 | const unsigned int wtScale = maxIntWt; // this value corresponds to a weight of 1.0 |
870 | 0 | for ( int ii = 0; ii < 4; ++ii ) |
871 | 0 | { |
872 | 0 | unsigned int bw = static_cast<unsigned int>( weights[ ii ] * wtScale ); |
873 | 0 | intWeights[ ii ] = bw; |
874 | 0 | sum += bw; |
875 | 0 | } |
876 | | // if the sum doesn't add up due to roundoff error, we need to adjust the intWeights so that the sum is wtScale |
877 | 0 | if ( sum != maxIntWt ) |
878 | 0 | { |
879 | | // find the largest weight (it isn't necessarily the first one...) |
880 | 0 | int iMaxWeight = 0; |
881 | 0 | unsigned int maxWeight = 0; |
882 | 0 | for ( int ii = 0; ii < 4; ++ii ) |
883 | 0 | { |
884 | 0 | unsigned int bw = intWeights[ ii ]; |
885 | 0 | if ( bw > maxWeight ) |
886 | 0 | { |
887 | 0 | iMaxWeight = ii; |
888 | 0 | maxWeight = bw; |
889 | 0 | } |
890 | 0 | } |
891 | | // Adjust the largest weight to make sure the sum is correct. |
892 | | // The idea is that changing the largest weight will have the smallest effect |
893 | | // on the ratio of weights. This works best when there is one dominant weight, |
894 | | // and worst when 2 or more weights are similar in magnitude. |
895 | | // A better method could be used to reduce the quantization error, but this is |
896 | | // being done at run-time so it needs to be quick. |
897 | 0 | intWeights[ iMaxWeight ] += maxIntWt - sum; |
898 | 0 | } |
899 | | |
900 | | // now write the weights |
901 | 0 | if ( weightsBaseType == VET_UBYTE4_NORM ) |
902 | 0 | { |
903 | | // write out the weights as bytes |
904 | 0 | unsigned char* pWeight; |
905 | 0 | pWeightElem->baseVertexPointerToElement( pBase, &pWeight ); |
906 | | // NOTE: always writes out 4 regardless of numBlendWeightsPerVertex |
907 | 0 | for (unsigned int intWeight : intWeights) |
908 | 0 | { |
909 | 0 | *pWeight++ = static_cast<unsigned char>( intWeight ); |
910 | 0 | } |
911 | 0 | } |
912 | 0 | else |
913 | 0 | { |
914 | | // write out the weights as shorts |
915 | 0 | unsigned short* pWeight; |
916 | 0 | pWeightElem->baseVertexPointerToElement( pBase, &pWeight ); |
917 | 0 | for ( int ii = 0; ii < numBlendWeightsPerVertex; ++ii ) |
918 | 0 | { |
919 | 0 | *pWeight++ = static_cast<unsigned short>( intWeights[ ii ] ); |
920 | 0 | } |
921 | 0 | } |
922 | 0 | } |
923 | 0 | else |
924 | 0 | { |
925 | | // write out the weights as floats |
926 | 0 | float* pWeight; |
927 | 0 | pWeightElem->baseVertexPointerToElement( pBase, &pWeight ); |
928 | 0 | for ( int ii = 0; ii < numBlendWeightsPerVertex; ++ii ) |
929 | 0 | { |
930 | 0 | *pWeight++ = weights[ ii ]; |
931 | 0 | } |
932 | 0 | } |
933 | 0 | unsigned char* pIndex; |
934 | 0 | pIdxElem->baseVertexPointerToElement( pBase, &pIndex ); |
935 | 0 | for (unsigned char indice : indices) |
936 | 0 | { |
937 | 0 | *pIndex++ = indice; |
938 | 0 | } |
939 | 0 | pBase += vbuf->getVertexSize(); |
940 | 0 | } |
941 | 0 | } |
942 | | //--------------------------------------------------------------------- |
943 | | static Real distLineSegToPoint( const Vector3& line0, const Vector3& line1, const Vector3& pt ) |
944 | 0 | { |
945 | 0 | Vector3 v01 = line1 - line0; |
946 | 0 | Real tt = v01.dotProduct( pt - line0 ) / std::max( v01.dotProduct(v01), std::numeric_limits<Real>::epsilon() ); |
947 | 0 | tt = Math::Clamp( tt, Real(0.0f), Real(1.0f) ); |
948 | 0 | Vector3 onLine = line0 + tt * v01; |
949 | 0 | return pt.distance( onLine ); |
950 | 0 | } |
951 | | //--------------------------------------------------------------------- |
952 | | static Real _computeBoneBoundingRadiusHelper( VertexData* vertexData, |
953 | | const Mesh::VertexBoneAssignmentList& boneAssignments, |
954 | | const std::vector<Vector3>& bonePositions, |
955 | | const std::vector< std::vector<ushort> >& boneChildren |
956 | | ) |
957 | 0 | { |
958 | 0 | std::vector<Vector3> vertexPositions; |
959 | 0 | { |
960 | | // extract vertex positions |
961 | 0 | const VertexElement* posElem = vertexData->vertexDeclaration->findElementBySemantic(VES_POSITION); |
962 | 0 | HardwareVertexBufferSharedPtr vbuf = vertexData->vertexBufferBinding->getBuffer(posElem->getSource()); |
963 | | // if usage is write only, |
964 | 0 | if ( !vbuf->hasShadowBuffer() && (vbuf->getUsage() & HBU_DETAIL_WRITE_ONLY) ) |
965 | 0 | { |
966 | | // can't do it |
967 | 0 | return Real(0.0f); |
968 | 0 | } |
969 | 0 | vertexPositions.resize( vertexData->vertexCount ); |
970 | 0 | HardwareBufferLockGuard vertexLock(vbuf, HardwareBuffer::HBL_READ_ONLY); |
971 | 0 | unsigned char* vertex = static_cast<unsigned char*>(vertexLock.pData); |
972 | 0 | float* pFloat; |
973 | |
|
974 | 0 | for(size_t i = 0; i < vertexData->vertexCount; ++i) |
975 | 0 | { |
976 | 0 | posElem->baseVertexPointerToElement(vertex, &pFloat); |
977 | 0 | vertexPositions[ i ] = Vector3( pFloat[0], pFloat[1], pFloat[2] ); |
978 | 0 | vertex += vbuf->getVertexSize(); |
979 | 0 | } |
980 | 0 | } |
981 | 0 | Real maxRadius = Real(0); |
982 | 0 | Real minWeight = Real(0.01); |
983 | | // for each vertex-bone assignment, |
984 | 0 | for (const auto & boneAssignment : boneAssignments) |
985 | 0 | { |
986 | | // if weight is close to zero, ignore |
987 | 0 | if (boneAssignment.second.weight > minWeight) |
988 | 0 | { |
989 | | // if we have a bounding box around all bone origins, we consider how far outside this box the |
990 | | // current vertex could ever get (assuming it is only attached to the given bone, and the bones all have unity scale) |
991 | 0 | size_t iBone = boneAssignment.second.boneIndex; |
992 | 0 | const Vector3& v = vertexPositions[ boneAssignment.second.vertexIndex ]; |
993 | 0 | Vector3 diff = v - bonePositions[ iBone ]; |
994 | 0 | Real dist = diff.length(); // max distance of vertex v outside of bounding box |
995 | | // if this bone has children, we can reduce the dist under the assumption that the children may rotate wrt their parent, but don't translate |
996 | 0 | for (size_t iChild = 0; iChild < boneChildren[iBone].size(); ++iChild) |
997 | 0 | { |
998 | | // given this assumption, we know that the bounding box will enclose both the bone origin as well as the origin of the child bone, |
999 | | // and therefore everything on a line segment between the bone origin and the child bone will be inside the bounding box as well |
1000 | 0 | size_t iChildBone = boneChildren[ iBone ][ iChild ]; |
1001 | | // compute distance from vertex to line segment between bones |
1002 | 0 | Real distChild = distLineSegToPoint( bonePositions[ iBone ], bonePositions[ iChildBone ], v ); |
1003 | 0 | dist = std::min( dist, distChild ); |
1004 | 0 | } |
1005 | | // scale the distance by the weight, this prevents the radius from being over-inflated because of a vertex that is lightly influenced by a faraway bone |
1006 | 0 | dist *= boneAssignment.second.weight; |
1007 | 0 | maxRadius = std::max( maxRadius, dist ); |
1008 | 0 | } |
1009 | 0 | } |
1010 | 0 | return maxRadius; |
1011 | 0 | } |
1012 | | //--------------------------------------------------------------------- |
1013 | | void Mesh::_computeBoneBoundingRadius() |
1014 | 0 | { |
1015 | 0 | if (mBoneBoundingRadius == Real(0) && mSkeleton) |
1016 | 0 | { |
1017 | 0 | Real radius = Real(0); |
1018 | 0 | std::vector<Vector3> bonePositions; |
1019 | 0 | std::vector< std::vector<ushort> > boneChildren; // for each bone, a list of children |
1020 | 0 | { |
1021 | | // extract binding pose bone positions, and also indices for child bones |
1022 | 0 | uint16 numBones = mSkeleton->getNumBones(); |
1023 | 0 | mSkeleton->setBindingPose(); |
1024 | 0 | mSkeleton->_updateTransforms(); |
1025 | 0 | bonePositions.resize( numBones ); |
1026 | 0 | boneChildren.resize( numBones ); |
1027 | | // for each bone, |
1028 | 0 | for (uint16 iBone = 0; iBone < numBones; ++iBone) |
1029 | 0 | { |
1030 | 0 | Bone* bone = mSkeleton->getBone( iBone ); |
1031 | 0 | bonePositions[ iBone ] = bone->_getDerivedPosition(); |
1032 | 0 | boneChildren[ iBone ].reserve( bone->numChildren() ); |
1033 | 0 | for (uint16 iChild = 0; iChild < bone->numChildren(); ++iChild) |
1034 | 0 | { |
1035 | 0 | Bone* child = static_cast<Bone*>( bone->getChild( iChild ) ); |
1036 | 0 | boneChildren[ iBone ].push_back( child->getHandle() ); |
1037 | 0 | } |
1038 | 0 | } |
1039 | 0 | } |
1040 | 0 | if (sharedVertexData) |
1041 | 0 | { |
1042 | | // check shared vertices |
1043 | 0 | radius = _computeBoneBoundingRadiusHelper(sharedVertexData, mBoneAssignments, bonePositions, boneChildren); |
1044 | 0 | } |
1045 | | |
1046 | | // check submesh vertices |
1047 | 0 | for(auto *submesh : mSubMeshList) |
1048 | 0 | { |
1049 | 0 | if (!submesh->useSharedVertices && submesh->vertexData) |
1050 | 0 | { |
1051 | 0 | Real r = _computeBoneBoundingRadiusHelper(submesh->vertexData, submesh->mBoneAssignments, bonePositions, boneChildren); |
1052 | 0 | radius = std::max( radius, r ); |
1053 | 0 | } |
1054 | 0 | } |
1055 | 0 | if (radius > Real(0)) |
1056 | 0 | { |
1057 | 0 | mBoneBoundingRadius = radius; |
1058 | 0 | } |
1059 | 0 | else |
1060 | 0 | { |
1061 | | // fallback if we failed to find the vertices |
1062 | 0 | mBoneBoundingRadius = mBoundRadius; |
1063 | 0 | } |
1064 | 0 | } |
1065 | 0 | } |
1066 | | //--------------------------------------------------------------------- |
1067 | | void Mesh::_notifySkeleton(const SkeletonPtr& pSkel) |
1068 | 0 | { |
1069 | 0 | mSkeleton = pSkel; |
1070 | 0 | } |
1071 | | //--------------------------------------------------------------------- |
1072 | | Mesh::BoneAssignmentIterator Mesh::getBoneAssignmentIterator(void) |
1073 | 0 | { |
1074 | 0 | return BoneAssignmentIterator(mBoneAssignments.begin(), |
1075 | 0 | mBoneAssignments.end()); |
1076 | 0 | } |
1077 | | //--------------------------------------------------------------------- |
1078 | | const String& Mesh::getSkeletonName(void) const |
1079 | 0 | { |
1080 | 0 | return mSkeleton ? mSkeleton->getName() : BLANKSTRING; |
1081 | 0 | } |
1082 | | //--------------------------------------------------------------------- |
1083 | | const MeshLodUsage& Mesh::getLodLevel(ushort index) const |
1084 | 0 | { |
1085 | 0 | #if !OGRE_NO_MESHLOD |
1086 | 0 | index = std::min(index, (ushort)(mMeshLodUsageList.size() - 1)); |
1087 | 0 | if (this->_isManualLodLevel(index) && index > 0 && !mMeshLodUsageList[index].manualMesh) |
1088 | 0 | { |
1089 | | // Load the mesh now |
1090 | 0 | try { |
1091 | 0 | mMeshLodUsageList[index].manualMesh = |
1092 | 0 | MeshManager::getSingleton().load( |
1093 | 0 | mMeshLodUsageList[index].manualName, |
1094 | 0 | getGroup()); |
1095 | | // get the edge data, if required |
1096 | 0 | if (!mMeshLodUsageList[index].edgeData) |
1097 | 0 | { |
1098 | 0 | mMeshLodUsageList[index].edgeData = |
1099 | 0 | mMeshLodUsageList[index].manualMesh->getEdgeList(0); |
1100 | 0 | } |
1101 | 0 | } |
1102 | 0 | catch (Exception& ) |
1103 | 0 | { |
1104 | 0 | LogManager::getSingleton().stream() |
1105 | 0 | << "Error while loading manual LOD level " |
1106 | 0 | << mMeshLodUsageList[index].manualName |
1107 | 0 | << " - this LOD level will not be rendered. You can " |
1108 | 0 | << "ignore this error in offline mesh tools."; |
1109 | 0 | } |
1110 | |
|
1111 | 0 | } |
1112 | 0 | return mMeshLodUsageList[index]; |
1113 | | #else |
1114 | | return mMeshLodUsageList[0]; |
1115 | | #endif |
1116 | 0 | } |
1117 | | //--------------------------------------------------------------------- |
1118 | | ushort Mesh::getLodIndex(Real value) const |
1119 | 0 | { |
1120 | 0 | #if !OGRE_NO_MESHLOD |
1121 | | // Get index from strategy |
1122 | 0 | return mLodStrategy->getIndex(value, mMeshLodUsageList); |
1123 | | #else |
1124 | | return 0; |
1125 | | #endif |
1126 | 0 | } |
1127 | | //--------------------------------------------------------------------- |
1128 | | #if !OGRE_NO_MESHLOD |
1129 | | void Mesh::updateManualLodLevel(ushort index, const String& meshName) |
1130 | 0 | { |
1131 | | |
1132 | | // Basic prerequisites |
1133 | 0 | assert(index != 0 && "Can't modify first LOD level (full detail)"); |
1134 | 0 | assert(index < mMeshLodUsageList.size() && "Idndex out of bounds"); |
1135 | | // get lod |
1136 | 0 | MeshLodUsage* lod = &(mMeshLodUsageList[index]); |
1137 | |
|
1138 | 0 | lod->manualName = meshName; |
1139 | 0 | lod->manualMesh.reset(); |
1140 | 0 | OGRE_DELETE lod->edgeData; |
1141 | 0 | lod->edgeData = 0; |
1142 | 0 | } |
1143 | | //--------------------------------------------------------------------- |
1144 | | void Mesh::_setLodInfo(unsigned short numLevels) |
1145 | 0 | { |
1146 | 0 | assert(!mEdgeListsBuilt && "Can't modify LOD after edge lists built"); |
1147 | | |
1148 | | // Basic prerequisites |
1149 | 0 | assert(numLevels > 0 && "Must be at least one level (full detail level must exist)"); |
1150 | |
|
1151 | 0 | mNumLods = numLevels; |
1152 | 0 | mMeshLodUsageList.resize(numLevels); |
1153 | | // Resize submesh face data lists too |
1154 | 0 | for (auto & i : mSubMeshList) |
1155 | 0 | { |
1156 | 0 | i->mLodFaceList.resize(numLevels - 1); |
1157 | 0 | } |
1158 | 0 | } |
1159 | | //--------------------------------------------------------------------- |
1160 | | void Mesh::_setLodUsage(unsigned short level, const MeshLodUsage& usage) |
1161 | 0 | { |
1162 | 0 | assert(!mEdgeListsBuilt && "Can't modify LOD after edge lists built"); |
1163 | | |
1164 | | // Basic prerequisites |
1165 | 0 | assert(level != 0 && "Can't modify first LOD level (full detail)"); |
1166 | 0 | assert(level < mMeshLodUsageList.size() && "Index out of bounds"); |
1167 | |
|
1168 | 0 | mMeshLodUsageList[level] = usage; |
1169 | |
|
1170 | 0 | if(!mMeshLodUsageList[level].manualName.empty()){ |
1171 | 0 | mHasManualLodLevel = true; |
1172 | 0 | } |
1173 | 0 | } |
1174 | | //--------------------------------------------------------------------- |
1175 | | void Mesh::_setSubMeshLodFaceList(unsigned short subIdx, unsigned short level, |
1176 | | IndexData* facedata) |
1177 | 0 | { |
1178 | 0 | assert(!mEdgeListsBuilt && "Can't modify LOD after edge lists built"); |
1179 | | |
1180 | | // Basic prerequisites |
1181 | 0 | assert(mMeshLodUsageList[level].manualName.empty() && "Not using generated LODs!"); |
1182 | 0 | assert(subIdx < mSubMeshList.size() && "Index out of bounds"); |
1183 | 0 | assert(level != 0 && "Can't modify first LOD level (full detail)"); |
1184 | 0 | assert(level-1 < (unsigned short)mSubMeshList[subIdx]->mLodFaceList.size() && "Index out of bounds"); |
1185 | |
|
1186 | 0 | SubMesh* sm = mSubMeshList[subIdx]; |
1187 | 0 | sm->mLodFaceList[level - 1] = facedata; |
1188 | 0 | } |
1189 | | #endif |
1190 | | //--------------------------------------------------------------------- |
1191 | | bool Mesh::_isManualLodLevel( unsigned short level ) const |
1192 | 0 | { |
1193 | 0 | #if !OGRE_NO_MESHLOD |
1194 | 0 | return !mMeshLodUsageList[level].manualName.empty(); |
1195 | | #else |
1196 | | return false; |
1197 | | #endif |
1198 | 0 | } |
1199 | | //--------------------------------------------------------------------- |
1200 | | ushort Mesh::_getSubMeshIndex(const String& name) const |
1201 | 0 | { |
1202 | 0 | SubMeshNameMap::const_iterator i = mSubMeshNameMap.find(name) ; |
1203 | 0 | if (i == mSubMeshNameMap.end()) |
1204 | 0 | OGRE_EXCEPT(Exception::ERR_ITEM_NOT_FOUND, "No SubMesh named " + name + " found.", |
1205 | 0 | "Mesh::_getSubMeshIndex"); |
1206 | | |
1207 | 0 | return i->second; |
1208 | 0 | } |
1209 | | //-------------------------------------------------------------------- |
1210 | | void Mesh::removeLodLevels(void) |
1211 | 0 | { |
1212 | 0 | #if !OGRE_NO_MESHLOD |
1213 | | // Remove data from SubMeshes |
1214 | 0 | for (auto *isub : mSubMeshList) |
1215 | 0 | { |
1216 | 0 | isub->removeLodLevels(); |
1217 | 0 | } |
1218 | |
|
1219 | 0 | bool edgeListWasBuilt = isEdgeListBuilt(); |
1220 | 0 | freeEdgeList(); |
1221 | | |
1222 | | // Reinitialise |
1223 | 0 | mNumLods = 1; |
1224 | 0 | mMeshLodUsageList.resize(1); |
1225 | 0 | mMeshLodUsageList[0].edgeData = NULL; |
1226 | |
|
1227 | 0 | if(edgeListWasBuilt) |
1228 | 0 | buildEdgeList(); |
1229 | 0 | #endif |
1230 | 0 | } |
1231 | | |
1232 | | //--------------------------------------------------------------------- |
1233 | | Real Mesh::getBoundingSphereRadius(void) const |
1234 | 0 | { |
1235 | 0 | return mBoundRadius; |
1236 | 0 | } |
1237 | | //--------------------------------------------------------------------- |
1238 | | Real Mesh::getBoneBoundingRadius(void) const |
1239 | 0 | { |
1240 | 0 | return mBoneBoundingRadius; |
1241 | 0 | } |
1242 | | //--------------------------------------------------------------------- |
1243 | | void Mesh::setVertexBufferPolicy(HardwareBuffer::Usage vbUsage, bool shadowBuffer) |
1244 | 0 | { |
1245 | 0 | mVertexBufferUsage = (HardwareBufferUsage)vbUsage; |
1246 | 0 | mVertexBufferShadowBuffer = shadowBuffer; |
1247 | 0 | } |
1248 | | //--------------------------------------------------------------------- |
1249 | | void Mesh::setIndexBufferPolicy(HardwareBuffer::Usage vbUsage, bool shadowBuffer) |
1250 | 0 | { |
1251 | 0 | mIndexBufferUsage = (HardwareBufferUsage)vbUsage; |
1252 | 0 | mIndexBufferShadowBuffer = shadowBuffer; |
1253 | 0 | } |
1254 | | //--------------------------------------------------------------------- |
1255 | | void Mesh::mergeAdjacentTexcoords( unsigned short finalTexCoordSet, |
1256 | | unsigned short texCoordSetToDestroy ) |
1257 | 0 | { |
1258 | 0 | if( sharedVertexData ) |
1259 | 0 | mergeAdjacentTexcoords( finalTexCoordSet, texCoordSetToDestroy, sharedVertexData ); |
1260 | |
|
1261 | 0 | for (auto *s : mSubMeshList) |
1262 | 0 | { |
1263 | 0 | if(!s->useSharedVertices) |
1264 | 0 | mergeAdjacentTexcoords (finalTexCoordSet, texCoordSetToDestroy, s->vertexData ); |
1265 | 0 | } |
1266 | 0 | } |
1267 | | //--------------------------------------------------------------------- |
1268 | | void Mesh::mergeAdjacentTexcoords( unsigned short finalTexCoordSet, |
1269 | | unsigned short texCoordSetToDestroy, |
1270 | | VertexData *vertexData ) |
1271 | 0 | { |
1272 | 0 | VertexDeclaration *vDecl = vertexData->vertexDeclaration; |
1273 | |
|
1274 | 0 | const VertexElement *uv0 = vDecl->findElementBySemantic( VES_TEXTURE_COORDINATES, |
1275 | 0 | finalTexCoordSet ); |
1276 | 0 | const VertexElement *uv1 = vDecl->findElementBySemantic( VES_TEXTURE_COORDINATES, |
1277 | 0 | texCoordSetToDestroy ); |
1278 | |
|
1279 | 0 | if( uv0 && uv1 ) |
1280 | 0 | { |
1281 | | //Check that both base types are compatible (mix floats w/ shorts) and there's enough space |
1282 | 0 | VertexElementType baseType0 = VertexElement::getBaseType( uv0->getType() ); |
1283 | 0 | VertexElementType baseType1 = VertexElement::getBaseType( uv1->getType() ); |
1284 | |
|
1285 | 0 | unsigned short totalTypeCount = VertexElement::getTypeCount( uv0->getType() ) + |
1286 | 0 | VertexElement::getTypeCount( uv1->getType() ); |
1287 | 0 | if( baseType0 == baseType1 && totalTypeCount <= 4 ) |
1288 | 0 | { |
1289 | 0 | const VertexDeclaration::VertexElementList &veList = vDecl->getElements(); |
1290 | 0 | VertexDeclaration::VertexElementList::const_iterator uv0Itor = std::find( veList.begin(), |
1291 | 0 | veList.end(), *uv0 ); |
1292 | 0 | unsigned short elem_idx = std::distance( veList.begin(), uv0Itor ); |
1293 | 0 | VertexElementType newType = VertexElement::multiplyTypeCount( baseType0, |
1294 | 0 | totalTypeCount ); |
1295 | |
|
1296 | 0 | if( ( uv0->getOffset() + uv0->getSize() == uv1->getOffset() || |
1297 | 0 | uv1->getOffset() + uv1->getSize() == uv0->getOffset() ) && |
1298 | 0 | uv0->getSource() == uv1->getSource() ) |
1299 | 0 | { |
1300 | | //Special case where they adjacent, just change the declaration & we're done. |
1301 | 0 | size_t newOffset = std::min( uv0->getOffset(), uv1->getOffset() ); |
1302 | 0 | unsigned short newIdx = std::min( uv0->getIndex(), uv1->getIndex() ); |
1303 | |
|
1304 | 0 | vDecl->modifyElement( elem_idx, uv0->getSource(), newOffset, newType, |
1305 | 0 | VES_TEXTURE_COORDINATES, newIdx ); |
1306 | 0 | vDecl->removeElement( VES_TEXTURE_COORDINATES, texCoordSetToDestroy ); |
1307 | 0 | uv1 = 0; |
1308 | 0 | } |
1309 | |
|
1310 | 0 | vDecl->closeGapsInSource(); |
1311 | 0 | } |
1312 | 0 | } |
1313 | 0 | } |
1314 | | //--------------------------------------------------------------------- |
1315 | | void Mesh::organiseTangentsBuffer(VertexData *vertexData, |
1316 | | VertexElementSemantic targetSemantic, unsigned short index, |
1317 | | unsigned short sourceTexCoordSet) |
1318 | 0 | { |
1319 | 0 | VertexDeclaration *vDecl = vertexData->vertexDeclaration ; |
1320 | 0 | VertexBufferBinding *vBind = vertexData->vertexBufferBinding ; |
1321 | |
|
1322 | 0 | const VertexElement *tangentsElem = vDecl->findElementBySemantic(targetSemantic, index); |
1323 | 0 | bool needsToBeCreated = false; |
1324 | |
|
1325 | 0 | if (!tangentsElem) |
1326 | 0 | { // no tex coords with index 1 |
1327 | 0 | needsToBeCreated = true ; |
1328 | 0 | } |
1329 | 0 | else if (tangentsElem->getType() != VET_FLOAT3) |
1330 | 0 | { |
1331 | | // buffer exists, but not 3D |
1332 | 0 | OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, |
1333 | 0 | "Target semantic set already exists but is not 3D, therefore " |
1334 | 0 | "cannot contain tangents. Pick an alternative destination semantic. ", |
1335 | 0 | "Mesh::organiseTangentsBuffer"); |
1336 | 0 | } |
1337 | | |
1338 | 0 | HardwareVertexBufferSharedPtr newBuffer; |
1339 | 0 | if (needsToBeCreated) |
1340 | 0 | { |
1341 | | // To be most efficient with our vertex streams, |
1342 | | // tack the new tangents onto the same buffer as the |
1343 | | // source texture coord set |
1344 | 0 | const VertexElement* prevTexCoordElem = |
1345 | 0 | vertexData->vertexDeclaration->findElementBySemantic( |
1346 | 0 | VES_TEXTURE_COORDINATES, sourceTexCoordSet); |
1347 | 0 | if (!prevTexCoordElem) |
1348 | 0 | { |
1349 | 0 | OGRE_EXCEPT(Exception::ERR_ITEM_NOT_FOUND, |
1350 | 0 | "Cannot locate the first texture coordinate element to " |
1351 | 0 | "which to append the new tangents.", |
1352 | 0 | "Mesh::orgagniseTangentsBuffer"); |
1353 | 0 | } |
1354 | | // Find the buffer associated with this element |
1355 | 0 | HardwareVertexBufferSharedPtr origBuffer = |
1356 | 0 | vertexData->vertexBufferBinding->getBuffer( |
1357 | 0 | prevTexCoordElem->getSource()); |
1358 | | // Now create a new buffer, which includes the previous contents |
1359 | | // plus extra space for the 3D coords |
1360 | 0 | newBuffer = getHardwareBufferManager()->createVertexBuffer( |
1361 | 0 | origBuffer->getVertexSize() + 3*sizeof(float), |
1362 | 0 | vertexData->vertexCount, |
1363 | 0 | origBuffer->getUsage(), |
1364 | 0 | origBuffer->hasShadowBuffer() ); |
1365 | | // Add the new element |
1366 | 0 | vDecl->addElement( |
1367 | 0 | prevTexCoordElem->getSource(), |
1368 | 0 | origBuffer->getVertexSize(), |
1369 | 0 | VET_FLOAT3, |
1370 | 0 | targetSemantic, |
1371 | 0 | index); |
1372 | | // Now copy the original data across |
1373 | 0 | HardwareBufferLockGuard srcLock(origBuffer, HardwareBuffer::HBL_READ_ONLY); |
1374 | 0 | HardwareBufferLockGuard dstLock(newBuffer, HardwareBuffer::HBL_DISCARD); |
1375 | 0 | unsigned char* pSrc = static_cast<unsigned char*>(srcLock.pData); |
1376 | 0 | unsigned char* pDest = static_cast<unsigned char*>(dstLock.pData); |
1377 | 0 | size_t vertSize = origBuffer->getVertexSize(); |
1378 | 0 | for (size_t v = 0; v < vertexData->vertexCount; ++v) |
1379 | 0 | { |
1380 | | // Copy original vertex data |
1381 | 0 | memcpy(pDest, pSrc, vertSize); |
1382 | 0 | pSrc += vertSize; |
1383 | 0 | pDest += vertSize; |
1384 | | // Set the new part to 0 since we'll accumulate in this |
1385 | 0 | memset(pDest, 0, sizeof(float)*3); |
1386 | 0 | pDest += sizeof(float)*3; |
1387 | 0 | } |
1388 | | |
1389 | | // Rebind the new buffer |
1390 | 0 | vBind->setBinding(prevTexCoordElem->getSource(), newBuffer); |
1391 | 0 | } |
1392 | 0 | } |
1393 | | //--------------------------------------------------------------------- |
1394 | | void Mesh::buildTangentVectors(unsigned short sourceTexCoordSet, bool splitMirrored, bool splitRotated, |
1395 | | bool storeParityInW) |
1396 | 0 | { |
1397 | |
|
1398 | 0 | TangentSpaceCalc tangentsCalc; |
1399 | 0 | tangentsCalc.setSplitMirrored(splitMirrored); |
1400 | 0 | tangentsCalc.setSplitRotated(splitRotated); |
1401 | 0 | tangentsCalc.setStoreParityInW(storeParityInW); |
1402 | | |
1403 | | // shared geometry first |
1404 | 0 | if (sharedVertexData) |
1405 | 0 | { |
1406 | 0 | tangentsCalc.setVertexData(sharedVertexData); |
1407 | 0 | bool found = false; |
1408 | 0 | for (auto sm : mSubMeshList) |
1409 | 0 | { |
1410 | 0 | if (sm->useSharedVertices) |
1411 | 0 | { |
1412 | 0 | tangentsCalc.addIndexData(sm->indexData); |
1413 | 0 | found = true; |
1414 | 0 | } |
1415 | 0 | } |
1416 | 0 | if (found) |
1417 | 0 | { |
1418 | 0 | TangentSpaceCalc::Result res = tangentsCalc.build(sourceTexCoordSet); |
1419 | | |
1420 | | // If any vertex splitting happened, we have to give them bone assignments |
1421 | 0 | if (mSkeleton) |
1422 | 0 | { |
1423 | 0 | for (auto & remap : res.indexesRemapped) |
1424 | 0 | { |
1425 | | // Copy all bone assignments from the split vertex |
1426 | 0 | VertexBoneAssignmentList::iterator vbstart = mBoneAssignments.lower_bound(remap.splitVertex.first); |
1427 | 0 | VertexBoneAssignmentList::iterator vbend = mBoneAssignments.upper_bound(remap.splitVertex.first); |
1428 | 0 | for (VertexBoneAssignmentList::iterator vba = vbstart; vba != vbend; ++vba) |
1429 | 0 | { |
1430 | 0 | VertexBoneAssignment newAsgn = vba->second; |
1431 | 0 | newAsgn.vertexIndex = static_cast<unsigned int>(remap.splitVertex.second); |
1432 | | // multimap insert doesn't invalidate iterators |
1433 | 0 | addBoneAssignment(newAsgn); |
1434 | 0 | } |
1435 | |
|
1436 | 0 | } |
1437 | 0 | } |
1438 | | |
1439 | | // Update poses (some vertices might have been duplicated) |
1440 | | // we will just check which vertices have been split and copy |
1441 | | // the offset for the original vertex to the corresponding new vertex |
1442 | 0 | for (auto *current_pose : mPoseList) |
1443 | 0 | { |
1444 | 0 | const Pose::VertexOffsetMap& offset_map = current_pose->getVertexOffsets(); |
1445 | 0 | for(auto& split : res.vertexSplits) |
1446 | 0 | { |
1447 | 0 | Pose::VertexOffsetMap::const_iterator found_offset = offset_map.find(split.first); |
1448 | | |
1449 | | // copy the offset |
1450 | 0 | if( found_offset != offset_map.end() ) |
1451 | 0 | { |
1452 | 0 | current_pose->addVertex (split.second, found_offset->second ); |
1453 | 0 | } |
1454 | 0 | } |
1455 | 0 | } |
1456 | 0 | } |
1457 | 0 | } |
1458 | | |
1459 | | // Dedicated geometry |
1460 | 0 | for (auto sm : mSubMeshList) |
1461 | 0 | { |
1462 | 0 | if (!sm->useSharedVertices) |
1463 | 0 | { |
1464 | 0 | tangentsCalc.clear(); |
1465 | 0 | tangentsCalc.setVertexData(sm->vertexData); |
1466 | 0 | tangentsCalc.addIndexData(sm->indexData, sm->operationType); |
1467 | 0 | TangentSpaceCalc::Result res = tangentsCalc.build(sourceTexCoordSet); |
1468 | | |
1469 | | // If any vertex splitting happened, we have to give them bone assignments |
1470 | 0 | if (mSkeleton) |
1471 | 0 | { |
1472 | 0 | for (auto & remap : res.indexesRemapped) |
1473 | 0 | { |
1474 | | // Copy all bone assignments from the split vertex |
1475 | 0 | VertexBoneAssignmentList::const_iterator vbstart = |
1476 | 0 | sm->getBoneAssignments().lower_bound(remap.splitVertex.first); |
1477 | 0 | VertexBoneAssignmentList::const_iterator vbend = |
1478 | 0 | sm->getBoneAssignments().upper_bound(remap.splitVertex.first); |
1479 | 0 | for (VertexBoneAssignmentList::const_iterator vba = vbstart; vba != vbend; ++vba) |
1480 | 0 | { |
1481 | 0 | VertexBoneAssignment newAsgn = vba->second; |
1482 | 0 | newAsgn.vertexIndex = static_cast<unsigned int>(remap.splitVertex.second); |
1483 | | // multimap insert doesn't invalidate iterators |
1484 | 0 | sm->addBoneAssignment(newAsgn); |
1485 | 0 | } |
1486 | |
|
1487 | 0 | } |
1488 | |
|
1489 | 0 | } |
1490 | 0 | } |
1491 | 0 | } |
1492 | |
|
1493 | 0 | } |
1494 | | //--------------------------------------------------------------------- |
1495 | | bool Mesh::suggestTangentVectorBuildParams(unsigned short& outSourceCoordSet) |
1496 | 0 | { |
1497 | | // Go through all the vertex data and locate source and dest (must agree) |
1498 | 0 | bool sharedGeometryDone = false; |
1499 | 0 | bool foundExisting = false; |
1500 | 0 | bool firstOne = true; |
1501 | 0 | for (auto *sm : mSubMeshList) |
1502 | 0 | { |
1503 | 0 | VertexData* vertexData; |
1504 | 0 | if (sm->useSharedVertices) |
1505 | 0 | { |
1506 | 0 | if (sharedGeometryDone) |
1507 | 0 | continue; |
1508 | 0 | vertexData = sharedVertexData; |
1509 | 0 | sharedGeometryDone = true; |
1510 | 0 | } |
1511 | 0 | else |
1512 | 0 | { |
1513 | 0 | vertexData = sm->vertexData; |
1514 | 0 | } |
1515 | | |
1516 | 0 | const VertexElement *sourceElem = 0; |
1517 | 0 | unsigned short targetIndex = 0; |
1518 | 0 | for (targetIndex = 0; targetIndex < OGRE_MAX_TEXTURE_COORD_SETS; ++targetIndex) |
1519 | 0 | { |
1520 | 0 | auto testElem = |
1521 | 0 | vertexData->vertexDeclaration->findElementBySemantic(VES_TEXTURE_COORDINATES, targetIndex); |
1522 | 0 | if (!testElem) |
1523 | 0 | break; // finish if we've run out, t will be the target |
1524 | | |
1525 | | // We're still looking for the source texture coords |
1526 | 0 | if (testElem->getType() == VET_FLOAT2) |
1527 | 0 | { |
1528 | | // Ok, we found it |
1529 | 0 | sourceElem = testElem; |
1530 | 0 | break; |
1531 | 0 | } |
1532 | 0 | } |
1533 | | |
1534 | | // After iterating, we should have a source and a possible destination (t) |
1535 | 0 | if (!sourceElem) |
1536 | 0 | { |
1537 | 0 | OGRE_EXCEPT(Exception::ERR_ITEM_NOT_FOUND, |
1538 | 0 | "Cannot locate an appropriate 2D texture coordinate set for " |
1539 | 0 | "all the vertex data in this mesh to create tangents from. "); |
1540 | 0 | } |
1541 | | |
1542 | | // Look for existing semantic |
1543 | 0 | foundExisting = vertexData->vertexDeclaration->findElementBySemantic(VES_TANGENT); |
1544 | | |
1545 | | // Check that we agree with previous decisions, if this is not the |
1546 | | // first one, and if we're not just using the existing one |
1547 | 0 | if (!firstOne && !foundExisting) |
1548 | 0 | { |
1549 | 0 | if (sourceElem->getIndex() != outSourceCoordSet) |
1550 | 0 | { |
1551 | 0 | OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, |
1552 | 0 | "Multiple sets of vertex data in this mesh disagree on " |
1553 | 0 | "the appropriate index to use for the texture coordinates. " |
1554 | 0 | "This ambiguity must be rectified before tangents can be generated."); |
1555 | 0 | } |
1556 | 0 | } |
1557 | | |
1558 | | // Otherwise, save this result |
1559 | 0 | outSourceCoordSet = sourceElem->getIndex(); |
1560 | |
|
1561 | 0 | firstOne = false; |
1562 | |
|
1563 | 0 | } |
1564 | | |
1565 | 0 | return foundExisting; |
1566 | |
|
1567 | 0 | } |
1568 | | //--------------------------------------------------------------------- |
1569 | | void Mesh::buildEdgeList(void) |
1570 | 0 | { |
1571 | 0 | if (mEdgeListsBuilt) |
1572 | 0 | return; |
1573 | 0 | #if !OGRE_NO_MESHLOD |
1574 | | // Loop over LODs |
1575 | 0 | for (unsigned short lodIndex = 0; lodIndex < (unsigned short)mMeshLodUsageList.size(); ++lodIndex) |
1576 | 0 | { |
1577 | | // use getLodLevel to enforce loading of manual mesh lods |
1578 | 0 | const MeshLodUsage& usage = getLodLevel(lodIndex); |
1579 | |
|
1580 | 0 | if (!usage.manualName.empty() && lodIndex != 0) |
1581 | 0 | { |
1582 | | // Delegate edge building to manual mesh |
1583 | | // It should have already built it's own edge list while loading |
1584 | 0 | if (usage.manualMesh) |
1585 | 0 | { |
1586 | 0 | usage.edgeData = usage.manualMesh->getEdgeList(0); |
1587 | 0 | } |
1588 | 0 | } |
1589 | 0 | else |
1590 | 0 | { |
1591 | | // Build |
1592 | 0 | EdgeListBuilder eb; |
1593 | 0 | size_t vertexSetCount = 0; |
1594 | 0 | bool atLeastOneIndexSet = false; |
1595 | |
|
1596 | 0 | if (sharedVertexData) |
1597 | 0 | { |
1598 | 0 | eb.addVertexData(sharedVertexData); |
1599 | 0 | vertexSetCount++; |
1600 | 0 | } |
1601 | | |
1602 | | // Prepare the builder using the submesh information |
1603 | 0 | for (auto *s : mSubMeshList) |
1604 | 0 | { |
1605 | 0 | if (s->operationType != RenderOperation::OT_TRIANGLE_FAN && |
1606 | 0 | s->operationType != RenderOperation::OT_TRIANGLE_LIST && |
1607 | 0 | s->operationType != RenderOperation::OT_TRIANGLE_STRIP) |
1608 | 0 | { |
1609 | 0 | continue; |
1610 | 0 | } |
1611 | 0 | if (s->useSharedVertices) |
1612 | 0 | { |
1613 | | // Use shared vertex data, index as set 0 |
1614 | 0 | if (lodIndex == 0) |
1615 | 0 | { |
1616 | 0 | eb.addIndexData(s->indexData, 0, s->operationType); |
1617 | 0 | } |
1618 | 0 | else |
1619 | 0 | { |
1620 | 0 | eb.addIndexData(s->mLodFaceList[lodIndex-1], 0, |
1621 | 0 | s->operationType); |
1622 | 0 | } |
1623 | 0 | } |
1624 | 0 | else if(s->isBuildEdgesEnabled()) |
1625 | 0 | { |
1626 | | // own vertex data, add it and reference it directly |
1627 | 0 | eb.addVertexData(s->vertexData); |
1628 | 0 | if (lodIndex == 0) |
1629 | 0 | { |
1630 | | // Base index data |
1631 | 0 | eb.addIndexData(s->indexData, vertexSetCount++, |
1632 | 0 | s->operationType); |
1633 | 0 | } |
1634 | 0 | else |
1635 | 0 | { |
1636 | | // LOD index data |
1637 | 0 | eb.addIndexData(s->mLodFaceList[lodIndex-1], |
1638 | 0 | vertexSetCount++, s->operationType); |
1639 | 0 | } |
1640 | |
|
1641 | 0 | } |
1642 | 0 | atLeastOneIndexSet = true; |
1643 | 0 | } |
1644 | |
|
1645 | 0 | if (atLeastOneIndexSet) |
1646 | 0 | { |
1647 | 0 | usage.edgeData = eb.build(); |
1648 | |
|
1649 | | #if OGRE_DEBUG_MODE |
1650 | | // Override default log |
1651 | | Log* log = LogManager::getSingleton().createLog( |
1652 | | mName + "_lod" + StringConverter::toString(lodIndex) + |
1653 | | "_prepshadow.log", false, false); |
1654 | | usage.edgeData->log(log); |
1655 | | // clean up log & close file handle |
1656 | | LogManager::getSingleton().destroyLog(log); |
1657 | | #endif |
1658 | 0 | } |
1659 | 0 | else |
1660 | 0 | { |
1661 | | // create empty edge data |
1662 | 0 | usage.edgeData = OGRE_NEW EdgeData(); |
1663 | 0 | } |
1664 | 0 | } |
1665 | 0 | } |
1666 | | #else |
1667 | | // Build |
1668 | | EdgeListBuilder eb; |
1669 | | size_t vertexSetCount = 0; |
1670 | | if (sharedVertexData) |
1671 | | { |
1672 | | eb.addVertexData(sharedVertexData); |
1673 | | vertexSetCount++; |
1674 | | } |
1675 | | |
1676 | | // Prepare the builder using the submesh information |
1677 | | for (auto *s : mSubMeshList) |
1678 | | { |
1679 | | if (s->operationType != RenderOperation::OT_TRIANGLE_FAN && |
1680 | | s->operationType != RenderOperation::OT_TRIANGLE_LIST && |
1681 | | s->operationType != RenderOperation::OT_TRIANGLE_STRIP) |
1682 | | { |
1683 | | continue; |
1684 | | } |
1685 | | if (s->useSharedVertices) |
1686 | | { |
1687 | | eb.addIndexData(s->indexData, 0, s->operationType); |
1688 | | } |
1689 | | else if(s->isBuildEdgesEnabled()) |
1690 | | { |
1691 | | // own vertex data, add it and reference it directly |
1692 | | eb.addVertexData(s->vertexData); |
1693 | | // Base index data |
1694 | | eb.addIndexData(s->indexData, vertexSetCount++, |
1695 | | s->operationType); |
1696 | | } |
1697 | | } |
1698 | | |
1699 | | mMeshLodUsageList[0].edgeData = eb.build(); |
1700 | | |
1701 | | #if OGRE_DEBUG_MODE |
1702 | | // Override default log |
1703 | | Log* log = LogManager::getSingleton().createLog( |
1704 | | mName + "_lod0"+ |
1705 | | "_prepshadow.log", false, false); |
1706 | | mMeshLodUsageList[0].edgeData->log(log); |
1707 | | // clean up log & close file handle |
1708 | | LogManager::getSingleton().destroyLog(log); |
1709 | | #endif |
1710 | | #endif |
1711 | 0 | mEdgeListsBuilt = true; |
1712 | 0 | } |
1713 | | //--------------------------------------------------------------------- |
1714 | | void Mesh::freeEdgeList(void) |
1715 | 0 | { |
1716 | 0 | if (!mEdgeListsBuilt) |
1717 | 0 | return; |
1718 | 0 | #if !OGRE_NO_MESHLOD |
1719 | | // Loop over LODs |
1720 | 0 | unsigned short index = 0; |
1721 | 0 | for (auto& usage : mMeshLodUsageList) |
1722 | 0 | { |
1723 | 0 | if (usage.manualName.empty() || index == 0) |
1724 | 0 | { |
1725 | | // Only delete if we own this data |
1726 | | // Manual LODs > 0 own their own |
1727 | 0 | OGRE_DELETE usage.edgeData; |
1728 | 0 | } |
1729 | 0 | usage.edgeData = NULL; |
1730 | 0 | ++index; |
1731 | 0 | } |
1732 | | #else |
1733 | | OGRE_DELETE mMeshLodUsageList[0].edgeData; |
1734 | | mMeshLodUsageList[0].edgeData = NULL; |
1735 | | #endif |
1736 | 0 | mEdgeListsBuilt = false; |
1737 | 0 | } |
1738 | | //--------------------------------------------------------------------- |
1739 | | void Mesh::prepareForShadowVolume(void) |
1740 | 0 | { |
1741 | 0 | if (mPreparedForShadowVolumes) |
1742 | 0 | return; |
1743 | | |
1744 | 0 | if (sharedVertexData) |
1745 | 0 | { |
1746 | 0 | sharedVertexData->prepareForShadowVolume(); |
1747 | 0 | } |
1748 | 0 | for (auto *s : mSubMeshList) |
1749 | 0 | { |
1750 | 0 | if (!s->useSharedVertices && |
1751 | 0 | (s->operationType == RenderOperation::OT_TRIANGLE_FAN || |
1752 | 0 | s->operationType == RenderOperation::OT_TRIANGLE_LIST || |
1753 | 0 | s->operationType == RenderOperation::OT_TRIANGLE_STRIP)) |
1754 | 0 | { |
1755 | 0 | s->vertexData->prepareForShadowVolume(); |
1756 | 0 | } |
1757 | 0 | } |
1758 | 0 | mPreparedForShadowVolumes = true; |
1759 | 0 | } |
1760 | | //--------------------------------------------------------------------- |
1761 | | EdgeData* Mesh::getEdgeList(unsigned short lodIndex) |
1762 | 0 | { |
1763 | | // Build edge list on demand |
1764 | 0 | if (!mEdgeListsBuilt && mAutoBuildEdgeLists) |
1765 | 0 | { |
1766 | 0 | buildEdgeList(); |
1767 | 0 | } |
1768 | 0 | #if !OGRE_NO_MESHLOD |
1769 | 0 | return getLodLevel(lodIndex).edgeData; |
1770 | | #else |
1771 | | assert(lodIndex == 0); |
1772 | | return mMeshLodUsageList[0].edgeData; |
1773 | | #endif |
1774 | 0 | } |
1775 | | //--------------------------------------------------------------------- |
1776 | | const EdgeData* Mesh::getEdgeList(unsigned short lodIndex) const |
1777 | 0 | { |
1778 | 0 | #if !OGRE_NO_MESHLOD |
1779 | 0 | return getLodLevel(lodIndex).edgeData; |
1780 | | #else |
1781 | | assert(lodIndex == 0); |
1782 | | return mMeshLodUsageList[0].edgeData; |
1783 | | #endif |
1784 | 0 | } |
1785 | | //--------------------------------------------------------------------- |
1786 | | void Mesh::prepareMatricesForVertexBlend(const Affine3** blendMatrices, |
1787 | | const Affine3* boneMatrices, const IndexMap& indexMap) |
1788 | 0 | { |
1789 | 0 | assert(indexMap.size() <= OGRE_MAX_NUM_BONES); |
1790 | 0 | for (auto& i : indexMap) |
1791 | 0 | { |
1792 | 0 | *blendMatrices++ = boneMatrices + i; |
1793 | 0 | } |
1794 | 0 | } |
1795 | | //--------------------------------------------------------------------- |
1796 | | void Mesh::softwareVertexBlend(const VertexData* sourceVertexData, |
1797 | | const VertexData* targetVertexData, |
1798 | | const Affine3* const* blendMatrices, size_t numMatrices, |
1799 | | bool blendNormals) |
1800 | 0 | { |
1801 | 0 | float *pSrcPos = 0; |
1802 | 0 | float *pSrcNorm = 0; |
1803 | 0 | float *pDestPos = 0; |
1804 | 0 | float *pDestNorm = 0; |
1805 | 0 | float *pBlendWeight = 0; |
1806 | 0 | unsigned char* pBlendIdx = 0; |
1807 | 0 | size_t srcNormStride = 0; |
1808 | 0 | size_t destNormStride = 0; |
1809 | | |
1810 | | // Get elements for source |
1811 | 0 | auto srcElemPos = sourceVertexData->vertexDeclaration->findElementBySemantic(VES_POSITION); |
1812 | 0 | auto srcElemNorm = sourceVertexData->vertexDeclaration->findElementBySemantic(VES_NORMAL); |
1813 | 0 | auto srcElemBlendIndices = sourceVertexData->vertexDeclaration->findElementBySemantic(VES_BLEND_INDICES); |
1814 | 0 | auto srcElemBlendWeights = sourceVertexData->vertexDeclaration->findElementBySemantic(VES_BLEND_WEIGHTS); |
1815 | 0 | OgreAssert(srcElemPos && srcElemBlendIndices && srcElemBlendWeights, |
1816 | 0 | "You must supply at least positions, blend indices and blend weights"); |
1817 | | // Get elements for target |
1818 | 0 | auto destElemPos = targetVertexData->vertexDeclaration->findElementBySemantic(VES_POSITION); |
1819 | 0 | auto destElemNorm = targetVertexData->vertexDeclaration->findElementBySemantic(VES_NORMAL); |
1820 | | |
1821 | | // Get buffers for source |
1822 | 0 | HardwareVertexBufferSharedPtr srcPosBuf = sourceVertexData->vertexBufferBinding->getBuffer(srcElemPos->getSource()); |
1823 | 0 | HardwareVertexBufferSharedPtr srcIdxBuf = sourceVertexData->vertexBufferBinding->getBuffer(srcElemBlendIndices->getSource()); |
1824 | 0 | HardwareVertexBufferSharedPtr srcWeightBuf = sourceVertexData->vertexBufferBinding->getBuffer(srcElemBlendWeights->getSource()); |
1825 | 0 | HardwareVertexBufferSharedPtr srcNormBuf; |
1826 | | |
1827 | | // Get buffers for target |
1828 | 0 | HardwareVertexBufferSharedPtr destPosBuf = targetVertexData->vertexBufferBinding->getBuffer(destElemPos->getSource()); |
1829 | | |
1830 | | // Lock source buffers for reading |
1831 | 0 | HardwareBufferLockGuard srcPosLock(srcPosBuf, HardwareBuffer::HBL_READ_ONLY); |
1832 | 0 | srcElemPos->baseVertexPointerToElement(srcPosLock.pData, &pSrcPos); |
1833 | | |
1834 | | // Do we have normals and want to blend them? |
1835 | 0 | bool includeNormals = blendNormals && srcElemNorm && destElemNorm; |
1836 | 0 | HardwareVertexBufferSharedPtr destNormBuf; |
1837 | 0 | HardwareBufferLockGuard srcNormLock; |
1838 | 0 | if (includeNormals) |
1839 | 0 | { |
1840 | | // Get buffers for source |
1841 | 0 | srcNormBuf = sourceVertexData->vertexBufferBinding->getBuffer(srcElemNorm->getSource()); |
1842 | 0 | srcNormStride = srcNormBuf->getVertexSize(); |
1843 | | // Get buffers for target |
1844 | 0 | destNormBuf = targetVertexData->vertexBufferBinding->getBuffer(destElemNorm->getSource()); |
1845 | 0 | destNormStride = destNormBuf->getVertexSize(); |
1846 | |
|
1847 | 0 | if (srcNormBuf != srcPosBuf) |
1848 | 0 | { |
1849 | | // Different buffer |
1850 | 0 | srcNormLock.lock(srcNormBuf, HardwareBuffer::HBL_READ_ONLY); |
1851 | 0 | } |
1852 | 0 | srcElemNorm->baseVertexPointerToElement(srcNormLock.pData ? srcNormLock.pData : srcPosLock.pData, &pSrcNorm); |
1853 | 0 | } |
1854 | | |
1855 | | // Indices must be 4 bytes |
1856 | 0 | assert(srcElemBlendIndices->getType() == VET_UBYTE4 && "Blend indices must be VET_UBYTE4"); |
1857 | 0 | HardwareBufferLockGuard srcIdxLock(srcIdxBuf, HardwareBuffer::HBL_READ_ONLY); |
1858 | 0 | srcElemBlendIndices->baseVertexPointerToElement(srcIdxLock.pData, &pBlendIdx); |
1859 | 0 | HardwareBufferLockGuard srcWeightLock; |
1860 | 0 | if (srcWeightBuf != srcIdxBuf) |
1861 | 0 | { |
1862 | | // Lock buffer |
1863 | 0 | srcWeightLock.lock(srcWeightBuf, HardwareBuffer::HBL_READ_ONLY); |
1864 | 0 | } |
1865 | 0 | srcElemBlendWeights->baseVertexPointerToElement(srcWeightLock.pData ? srcWeightLock.pData : srcIdxLock.pData, &pBlendWeight); |
1866 | 0 | unsigned short numWeightsPerVertex = VertexElement::getTypeCount(srcElemBlendWeights->getType()); |
1867 | | |
1868 | | // Lock destination buffers for writing |
1869 | 0 | HardwareBufferLockGuard destPosLock(destPosBuf, |
1870 | 0 | (destNormBuf != destPosBuf && destPosBuf->getVertexSize() == destElemPos->getSize()) || |
1871 | 0 | (destNormBuf == destPosBuf && destPosBuf->getVertexSize() == destElemPos->getSize() + destElemNorm->getSize()) ? |
1872 | 0 | HardwareBuffer::HBL_DISCARD : HardwareBuffer::HBL_NORMAL); |
1873 | 0 | destElemPos->baseVertexPointerToElement(destPosLock.pData, &pDestPos); |
1874 | 0 | HardwareBufferLockGuard destNormLock; |
1875 | 0 | if (includeNormals) |
1876 | 0 | { |
1877 | 0 | if (destNormBuf != destPosBuf) |
1878 | 0 | { |
1879 | 0 | destNormLock.lock(destNormBuf, destNormBuf->getVertexSize() == destElemNorm->getSize() |
1880 | 0 | ? HardwareBuffer::HBL_DISCARD |
1881 | 0 | : HardwareBuffer::HBL_NORMAL); |
1882 | 0 | } |
1883 | 0 | destElemNorm->baseVertexPointerToElement(destNormLock.pData ? destNormLock.pData : destPosLock.pData, &pDestNorm); |
1884 | 0 | } |
1885 | |
|
1886 | 0 | auto srcPosStride = srcPosBuf->getVertexSize(); |
1887 | 0 | auto destPosStride = destPosBuf->getVertexSize(); |
1888 | 0 | auto blendIdxStride = srcIdxBuf->getVertexSize(); |
1889 | 0 | auto blendWeightStride = srcWeightBuf->getVertexSize(); |
1890 | |
|
1891 | 0 | OptimisedUtil::getImplementation()->softwareVertexSkinning( |
1892 | 0 | pSrcPos, pDestPos, |
1893 | 0 | pSrcNorm, pDestNorm, |
1894 | 0 | pBlendWeight, pBlendIdx, |
1895 | 0 | blendMatrices, |
1896 | 0 | srcPosStride, destPosStride, |
1897 | 0 | srcNormStride, destNormStride, |
1898 | 0 | blendWeightStride, blendIdxStride, |
1899 | 0 | numWeightsPerVertex, |
1900 | 0 | targetVertexData->vertexCount); |
1901 | 0 | } |
1902 | | //--------------------------------------------------------------------- |
1903 | | void Mesh::softwareVertexMorph(float t, |
1904 | | const HardwareVertexBufferSharedPtr& b1, |
1905 | | const HardwareVertexBufferSharedPtr& b2, |
1906 | | VertexData* targetVertexData) |
1907 | 0 | { |
1908 | 0 | HardwareBufferLockGuard b1Lock(b1, HardwareBuffer::HBL_READ_ONLY); |
1909 | 0 | float* pb1 = static_cast<float*>(b1Lock.pData); |
1910 | 0 | HardwareBufferLockGuard b2Lock; |
1911 | 0 | float* pb2; |
1912 | 0 | if (b1.get() != b2.get()) |
1913 | 0 | { |
1914 | 0 | b2Lock.lock(b2, HardwareBuffer::HBL_READ_ONLY); |
1915 | 0 | pb2 = static_cast<float*>(b2Lock.pData); |
1916 | 0 | } |
1917 | 0 | else |
1918 | 0 | { |
1919 | | // Same buffer - track with only one entry or time index exactly matching |
1920 | | // one keyframe |
1921 | | // For simplicity of main code, interpolate still but with same val |
1922 | 0 | pb2 = pb1; |
1923 | 0 | } |
1924 | |
|
1925 | 0 | const VertexElement* posElem = |
1926 | 0 | targetVertexData->vertexDeclaration->findElementBySemantic(VES_POSITION); |
1927 | 0 | assert(posElem); |
1928 | 0 | const VertexElement* normElem = |
1929 | 0 | targetVertexData->vertexDeclaration->findElementBySemantic(VES_NORMAL); |
1930 | |
|
1931 | 0 | bool morphNormals = false; |
1932 | 0 | if (normElem && normElem->getSource() == posElem->getSource() && |
1933 | 0 | b1->getVertexSize() == 24 && b2->getVertexSize() == 24) |
1934 | 0 | morphNormals = true; |
1935 | |
|
1936 | 0 | HardwareVertexBufferSharedPtr destBuf = |
1937 | 0 | targetVertexData->vertexBufferBinding->getBuffer( |
1938 | 0 | posElem->getSource()); |
1939 | 0 | assert((posElem->getSize() == destBuf->getVertexSize() |
1940 | 0 | || (morphNormals && posElem->getSize() + normElem->getSize() == destBuf->getVertexSize())) && |
1941 | 0 | "Positions (or positions & normals) must be in a buffer on their own for morphing"); |
1942 | 0 | HardwareBufferLockGuard destLock(destBuf, HardwareBuffer::HBL_DISCARD); |
1943 | 0 | float* pdst = static_cast<float*>(destLock.pData); |
1944 | |
|
1945 | 0 | OptimisedUtil::getImplementation()->softwareVertexMorph( |
1946 | 0 | t, pb1, pb2, pdst, |
1947 | 0 | b1->getVertexSize(), b2->getVertexSize(), destBuf->getVertexSize(), |
1948 | 0 | targetVertexData->vertexCount, |
1949 | 0 | morphNormals); |
1950 | 0 | } |
1951 | | //--------------------------------------------------------------------- |
1952 | | void Mesh::softwareVertexPoseBlend(float weight, |
1953 | | const std::map<uint32, Vector3f>& vertexOffsetMap, |
1954 | | const std::map<uint32, Vector3f>& normalsMap, |
1955 | | VertexData* targetVertexData) |
1956 | 0 | { |
1957 | | // Do nothing if no weight |
1958 | 0 | if (weight == 0.0f) |
1959 | 0 | return; |
1960 | | |
1961 | 0 | const VertexElement* posElem = |
1962 | 0 | targetVertexData->vertexDeclaration->findElementBySemantic(VES_POSITION); |
1963 | 0 | const VertexElement* normElem = |
1964 | 0 | targetVertexData->vertexDeclaration->findElementBySemantic(VES_NORMAL); |
1965 | 0 | assert(posElem); |
1966 | | // Support normals if they're in the same buffer as positions and pose includes them |
1967 | 0 | bool normals = normElem && !normalsMap.empty() && posElem->getSource() == normElem->getSource(); |
1968 | 0 | HardwareVertexBufferSharedPtr destBuf = |
1969 | 0 | targetVertexData->vertexBufferBinding->getBuffer( |
1970 | 0 | posElem->getSource()); |
1971 | |
|
1972 | 0 | size_t elemsPerVertex = destBuf->getVertexSize()/sizeof(float); |
1973 | | |
1974 | | // Have to lock in normal mode since this is incremental |
1975 | 0 | HardwareBufferLockGuard destLock(destBuf, HardwareBuffer::HBL_NORMAL); |
1976 | 0 | float* pBase = static_cast<float*>(destLock.pData); |
1977 | | |
1978 | | // Iterate over affected vertices |
1979 | 0 | for (const auto & i : vertexOffsetMap) |
1980 | 0 | { |
1981 | | // Adjust pointer |
1982 | 0 | float *pdst = pBase + i.first*elemsPerVertex; |
1983 | |
|
1984 | 0 | *pdst = *pdst + (i.second[0] * weight); |
1985 | 0 | ++pdst; |
1986 | 0 | *pdst = *pdst + (i.second[01] * weight); |
1987 | 0 | ++pdst; |
1988 | 0 | *pdst = *pdst + (i.second[2] * weight); |
1989 | 0 | ++pdst; |
1990 | |
|
1991 | 0 | } |
1992 | |
|
1993 | 0 | if (normals) |
1994 | 0 | { |
1995 | 0 | float* pNormBase; |
1996 | 0 | normElem->baseVertexPointerToElement((void*)pBase, &pNormBase); |
1997 | 0 | for (const auto & i : normalsMap) |
1998 | 0 | { |
1999 | | // Adjust pointer |
2000 | 0 | float *pdst = pNormBase + i.first*elemsPerVertex; |
2001 | |
|
2002 | 0 | *pdst = *pdst + (i.second[0] * weight); |
2003 | 0 | ++pdst; |
2004 | 0 | *pdst = *pdst + (i.second[1] * weight); |
2005 | 0 | ++pdst; |
2006 | 0 | *pdst = *pdst + (i.second[2] * weight); |
2007 | 0 | ++pdst; |
2008 | |
|
2009 | 0 | } |
2010 | 0 | } |
2011 | 0 | } |
2012 | | //--------------------------------------------------------------------- |
2013 | | size_t Mesh::calculateSize(void) const |
2014 | 0 | { |
2015 | | // calculate GPU size |
2016 | 0 | size_t ret = 0; |
2017 | 0 | unsigned short i; |
2018 | | // Shared vertices |
2019 | 0 | if (sharedVertexData) |
2020 | 0 | { |
2021 | 0 | for (i = 0; |
2022 | 0 | i < sharedVertexData->vertexBufferBinding->getBufferCount(); |
2023 | 0 | ++i) |
2024 | 0 | { |
2025 | 0 | ret += sharedVertexData->vertexBufferBinding |
2026 | 0 | ->getBuffer(i)->getSizeInBytes(); |
2027 | 0 | } |
2028 | 0 | } |
2029 | |
|
2030 | 0 | for (auto *s : mSubMeshList) |
2031 | 0 | { |
2032 | | // Dedicated vertices |
2033 | 0 | if (!s->useSharedVertices) |
2034 | 0 | { |
2035 | 0 | for (i = 0; |
2036 | 0 | i < s->vertexData->vertexBufferBinding->getBufferCount(); |
2037 | 0 | ++i) |
2038 | 0 | { |
2039 | 0 | ret += s->vertexData->vertexBufferBinding |
2040 | 0 | ->getBuffer(i)->getSizeInBytes(); |
2041 | 0 | } |
2042 | 0 | } |
2043 | 0 | if (s->indexData->indexBuffer) |
2044 | 0 | { |
2045 | | // Index data |
2046 | 0 | ret += s->indexData->indexBuffer->getSizeInBytes(); |
2047 | 0 | } |
2048 | |
|
2049 | 0 | } |
2050 | 0 | return ret; |
2051 | 0 | } |
2052 | | //----------------------------------------------------------------------------- |
2053 | | bool Mesh::hasVertexAnimation(void) const |
2054 | 0 | { |
2055 | 0 | return !mAnimationsList.empty(); |
2056 | 0 | } |
2057 | | //--------------------------------------------------------------------- |
2058 | | VertexAnimationType Mesh::getSharedVertexDataAnimationType(void) const |
2059 | 0 | { |
2060 | 0 | if (mAnimationTypesDirty) |
2061 | 0 | { |
2062 | 0 | _determineAnimationTypes(); |
2063 | 0 | } |
2064 | |
|
2065 | 0 | return mSharedVertexDataAnimationType; |
2066 | 0 | } |
2067 | | //--------------------------------------------------------------------- |
2068 | | void Mesh::_determineAnimationTypes(void) const |
2069 | 0 | { |
2070 | | // Don't check flag here; since detail checks on track changes are not |
2071 | | // done, allow caller to force if they need to |
2072 | | |
2073 | | // Initialise all types to nothing |
2074 | 0 | mSharedVertexDataAnimationType = VAT_NONE; |
2075 | 0 | mSharedVertexDataAnimationIncludesNormals = false; |
2076 | 0 | for (auto i : mSubMeshList) |
2077 | 0 | { |
2078 | 0 | i->mVertexAnimationType = VAT_NONE; |
2079 | 0 | i->mVertexAnimationIncludesNormals = false; |
2080 | 0 | } |
2081 | |
|
2082 | 0 | mPosesIncludeNormals = false; |
2083 | 0 | for (PoseList::const_iterator i = mPoseList.begin(); i != mPoseList.end(); ++i) |
2084 | 0 | { |
2085 | 0 | if (i == mPoseList.begin()) |
2086 | 0 | mPosesIncludeNormals = (*i)->getIncludesNormals(); |
2087 | 0 | else if (mPosesIncludeNormals != (*i)->getIncludesNormals()) |
2088 | | // only support normals if consistently included |
2089 | 0 | mPosesIncludeNormals = mPosesIncludeNormals && (*i)->getIncludesNormals(); |
2090 | 0 | } |
2091 | | |
2092 | | // Scan all animations and determine the type of animation tracks |
2093 | | // relating to each vertex data |
2094 | 0 | for(const auto& ai : mAnimationsList) |
2095 | 0 | { |
2096 | 0 | for (const auto& vit : ai.second->_getVertexTrackList()) |
2097 | 0 | { |
2098 | 0 | VertexAnimationTrack* track = vit.second; |
2099 | 0 | ushort handle = vit.first; |
2100 | 0 | if (handle == 0) |
2101 | 0 | { |
2102 | | // shared data |
2103 | 0 | if (mSharedVertexDataAnimationType != VAT_NONE && |
2104 | 0 | mSharedVertexDataAnimationType != track->getAnimationType()) |
2105 | 0 | { |
2106 | | // Mixing of morph and pose animation on same data is not allowed |
2107 | 0 | OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, |
2108 | 0 | "Animation tracks for shared vertex data on mesh " |
2109 | 0 | + mName + " try to mix vertex animation types, which is " |
2110 | 0 | "not allowed.", |
2111 | 0 | "Mesh::_determineAnimationTypes"); |
2112 | 0 | } |
2113 | 0 | mSharedVertexDataAnimationType = track->getAnimationType(); |
2114 | 0 | if (track->getAnimationType() == VAT_MORPH) |
2115 | 0 | mSharedVertexDataAnimationIncludesNormals = track->getVertexAnimationIncludesNormals(); |
2116 | 0 | else |
2117 | 0 | mSharedVertexDataAnimationIncludesNormals = mPosesIncludeNormals; |
2118 | |
|
2119 | 0 | } |
2120 | 0 | else |
2121 | 0 | { |
2122 | | // submesh index (-1) |
2123 | 0 | SubMesh* sm = getSubMesh(handle-1); |
2124 | 0 | if (sm->mVertexAnimationType != VAT_NONE && |
2125 | 0 | sm->mVertexAnimationType != track->getAnimationType()) |
2126 | 0 | { |
2127 | | // Mixing of morph and pose animation on same data is not allowed |
2128 | 0 | OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, |
2129 | 0 | "Animation tracks for dedicated vertex data " |
2130 | 0 | + StringConverter::toString(handle-1) + " on mesh " |
2131 | 0 | + mName + " try to mix vertex animation types, which is " |
2132 | 0 | "not allowed.", |
2133 | 0 | "Mesh::_determineAnimationTypes"); |
2134 | 0 | } |
2135 | 0 | sm->mVertexAnimationType = track->getAnimationType(); |
2136 | 0 | if (track->getAnimationType() == VAT_MORPH) |
2137 | 0 | sm->mVertexAnimationIncludesNormals = track->getVertexAnimationIncludesNormals(); |
2138 | 0 | else |
2139 | 0 | sm->mVertexAnimationIncludesNormals = mPosesIncludeNormals; |
2140 | |
|
2141 | 0 | } |
2142 | 0 | } |
2143 | 0 | } |
2144 | | |
2145 | 0 | mAnimationTypesDirty = false; |
2146 | 0 | } |
2147 | | //--------------------------------------------------------------------- |
2148 | | Animation* Mesh::createAnimation(const String& name, Real length) |
2149 | 0 | { |
2150 | | // Check name not used |
2151 | 0 | if (mAnimationsList.find(name) != mAnimationsList.end()) |
2152 | 0 | { |
2153 | 0 | OGRE_EXCEPT( |
2154 | 0 | Exception::ERR_DUPLICATE_ITEM, |
2155 | 0 | "An animation with the name " + name + " already exists", |
2156 | 0 | "Mesh::createAnimation"); |
2157 | 0 | } |
2158 | | |
2159 | 0 | Animation* ret = OGRE_NEW Animation(name, length); |
2160 | 0 | ret->_notifyContainer(this); |
2161 | | |
2162 | | // Add to list |
2163 | 0 | mAnimationsList[name] = ret; |
2164 | | |
2165 | | // Mark animation types dirty |
2166 | 0 | mAnimationTypesDirty = true; |
2167 | |
|
2168 | 0 | return ret; |
2169 | |
|
2170 | 0 | } |
2171 | | //--------------------------------------------------------------------- |
2172 | | Animation* Mesh::getAnimation(const String& name) const |
2173 | 0 | { |
2174 | 0 | Animation* ret = _getAnimationImpl(name); |
2175 | 0 | if (!ret) |
2176 | 0 | { |
2177 | 0 | OGRE_EXCEPT(Exception::ERR_ITEM_NOT_FOUND, |
2178 | 0 | "No animation entry found named " + name, |
2179 | 0 | "Mesh::getAnimation"); |
2180 | 0 | } |
2181 | | |
2182 | 0 | return ret; |
2183 | 0 | } |
2184 | | //--------------------------------------------------------------------- |
2185 | | Animation* Mesh::getAnimation(unsigned short index) const |
2186 | 0 | { |
2187 | | // If you hit this assert, then the index is out of bounds. |
2188 | 0 | assert( index < mAnimationsList.size() ); |
2189 | |
|
2190 | 0 | AnimationList::const_iterator i = mAnimationsList.begin(); |
2191 | |
|
2192 | 0 | std::advance(i, index); |
2193 | |
|
2194 | 0 | return i->second; |
2195 | |
|
2196 | 0 | } |
2197 | | //--------------------------------------------------------------------- |
2198 | | unsigned short Mesh::getNumAnimations(void) const |
2199 | 0 | { |
2200 | 0 | return static_cast<unsigned short>(mAnimationsList.size()); |
2201 | 0 | } |
2202 | | //--------------------------------------------------------------------- |
2203 | | bool Mesh::hasAnimation(const String& name) const |
2204 | 0 | { |
2205 | 0 | return _getAnimationImpl(name) != 0; |
2206 | 0 | } |
2207 | | //--------------------------------------------------------------------- |
2208 | | Animation* Mesh::_getAnimationImpl(const String& name) const |
2209 | 0 | { |
2210 | 0 | Animation* ret = 0; |
2211 | 0 | AnimationList::const_iterator i = mAnimationsList.find(name); |
2212 | |
|
2213 | 0 | if (i != mAnimationsList.end()) |
2214 | 0 | { |
2215 | 0 | ret = i->second; |
2216 | 0 | } |
2217 | |
|
2218 | 0 | return ret; |
2219 | |
|
2220 | 0 | } |
2221 | | //--------------------------------------------------------------------- |
2222 | | void Mesh::removeAnimation(const String& name) |
2223 | 0 | { |
2224 | 0 | AnimationList::iterator i = mAnimationsList.find(name); |
2225 | |
|
2226 | 0 | if (i == mAnimationsList.end()) |
2227 | 0 | { |
2228 | 0 | OGRE_EXCEPT(Exception::ERR_ITEM_NOT_FOUND, "No animation entry found named " + name, |
2229 | 0 | "Mesh::getAnimation"); |
2230 | 0 | } |
2231 | | |
2232 | 0 | OGRE_DELETE i->second; |
2233 | |
|
2234 | 0 | mAnimationsList.erase(i); |
2235 | |
|
2236 | 0 | mAnimationTypesDirty = true; |
2237 | 0 | } |
2238 | | //--------------------------------------------------------------------- |
2239 | | void Mesh::removeAllAnimations(void) |
2240 | 0 | { |
2241 | 0 | for (auto& a : mAnimationsList) |
2242 | 0 | { |
2243 | 0 | OGRE_DELETE a.second; |
2244 | 0 | } |
2245 | 0 | mAnimationsList.clear(); |
2246 | 0 | mAnimationTypesDirty = true; |
2247 | 0 | } |
2248 | | //--------------------------------------------------------------------- |
2249 | | VertexData* Mesh::getVertexDataByTrackHandle(unsigned short handle) |
2250 | 0 | { |
2251 | 0 | if (handle == 0) |
2252 | 0 | { |
2253 | 0 | return sharedVertexData; |
2254 | 0 | } |
2255 | 0 | else |
2256 | 0 | { |
2257 | 0 | return getSubMesh(handle-1)->vertexData; |
2258 | 0 | } |
2259 | 0 | } |
2260 | | //--------------------------------------------------------------------- |
2261 | | Pose* Mesh::createPose(ushort target, const String& name) |
2262 | 0 | { |
2263 | 0 | Pose* retPose = OGRE_NEW Pose(target, name); |
2264 | 0 | mPoseList.push_back(retPose); |
2265 | 0 | return retPose; |
2266 | 0 | } |
2267 | | //--------------------------------------------------------------------- |
2268 | | Pose* Mesh::getPose(const String& name) const |
2269 | 0 | { |
2270 | 0 | for (auto i : mPoseList) |
2271 | 0 | { |
2272 | 0 | if (i->getName() == name) |
2273 | 0 | return i; |
2274 | 0 | } |
2275 | 0 | StringStream str; |
2276 | 0 | str << "No pose called " << name << " found in Mesh " << mName; |
2277 | 0 | OGRE_EXCEPT(Exception::ERR_ITEM_NOT_FOUND, |
2278 | 0 | str.str(), |
2279 | 0 | "Mesh::getPose"); |
2280 | |
|
2281 | 0 | } |
2282 | | //--------------------------------------------------------------------- |
2283 | | void Mesh::removePose(ushort index) |
2284 | 0 | { |
2285 | 0 | OgreAssert(index < mPoseList.size(), ""); |
2286 | 0 | PoseList::iterator i = mPoseList.begin(); |
2287 | 0 | std::advance(i, index); |
2288 | 0 | OGRE_DELETE *i; |
2289 | 0 | mPoseList.erase(i); |
2290 | |
|
2291 | 0 | } |
2292 | | //--------------------------------------------------------------------- |
2293 | | void Mesh::removePose(const String& name) |
2294 | 0 | { |
2295 | 0 | for (PoseList::iterator i = mPoseList.begin(); i != mPoseList.end(); ++i) |
2296 | 0 | { |
2297 | 0 | if ((*i)->getName() == name) |
2298 | 0 | { |
2299 | 0 | OGRE_DELETE *i; |
2300 | 0 | mPoseList.erase(i); |
2301 | 0 | return; |
2302 | 0 | } |
2303 | 0 | } |
2304 | 0 | StringStream str; |
2305 | 0 | str << "No pose called " << name << " found in Mesh " << mName; |
2306 | 0 | OGRE_EXCEPT(Exception::ERR_ITEM_NOT_FOUND, |
2307 | 0 | str.str(), |
2308 | 0 | "Mesh::removePose"); |
2309 | 0 | } |
2310 | | //--------------------------------------------------------------------- |
2311 | | void Mesh::removeAllPoses(void) |
2312 | 0 | { |
2313 | 0 | for (auto & i : mPoseList) |
2314 | 0 | { |
2315 | 0 | OGRE_DELETE i; |
2316 | 0 | } |
2317 | 0 | mPoseList.clear(); |
2318 | 0 | } |
2319 | | //--------------------------------------------------------------------- |
2320 | | Mesh::PoseIterator Mesh::getPoseIterator(void) |
2321 | 0 | { |
2322 | 0 | return PoseIterator(mPoseList.begin(), mPoseList.end()); |
2323 | 0 | } |
2324 | | //--------------------------------------------------------------------- |
2325 | | Mesh::ConstPoseIterator Mesh::getPoseIterator(void) const |
2326 | 0 | { |
2327 | 0 | return ConstPoseIterator(mPoseList.begin(), mPoseList.end()); |
2328 | 0 | } |
2329 | | //----------------------------------------------------------------------------- |
2330 | | const PoseList& Mesh::getPoseList(void) const |
2331 | 0 | { |
2332 | 0 | return mPoseList; |
2333 | 0 | } |
2334 | | //--------------------------------------------------------------------- |
2335 | | const LodStrategy *Mesh::getLodStrategy() const |
2336 | 0 | { |
2337 | 0 | return mLodStrategy; |
2338 | 0 | } |
2339 | | #if !OGRE_NO_MESHLOD |
2340 | | //--------------------------------------------------------------------- |
2341 | | void Mesh::setLodStrategy(LodStrategy *lodStrategy) |
2342 | 0 | { |
2343 | 0 | mLodStrategy = lodStrategy; |
2344 | |
|
2345 | 0 | assert(mMeshLodUsageList.size()); |
2346 | | |
2347 | | // Re-transform user LOD values (starting at index 1, no need to transform base value) |
2348 | 0 | for (auto& m : mMeshLodUsageList) |
2349 | 0 | m.value = mLodStrategy->transformUserValue(m.userValue); |
2350 | | |
2351 | | // Rewrite first value |
2352 | 0 | mMeshLodUsageList[0].value = mLodStrategy->getBaseValue(); |
2353 | 0 | } |
2354 | | #endif |
2355 | | |
2356 | | void Mesh::_convertVertexElement(VertexElementSemantic semantic, VertexElementType dstType) |
2357 | 0 | { |
2358 | 0 | if (sharedVertexData) |
2359 | 0 | sharedVertexData->convertVertexElement(semantic, dstType); |
2360 | |
|
2361 | 0 | for (auto s : getSubMeshes()) |
2362 | 0 | if (s->vertexData) |
2363 | 0 | s->vertexData->convertVertexElement(semantic, dstType); |
2364 | 0 | } |
2365 | | } |
2366 | | |