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