/src/ogre/OgreMain/src/OgreSceneNode.cpp
Line | Count | Source |
1 | | /* |
2 | | ----------------------------------------------------------------------------- |
3 | | This source file is part of OGRE |
4 | | (Object-oriented Graphics Rendering Engine) |
5 | | For the latest info, see http://www.ogre3d.org/ |
6 | | |
7 | | Copyright (c) 2000-2014 Torus Knot Software Ltd |
8 | | |
9 | | Permission is hereby granted, free of charge, to any person obtaining a copy |
10 | | of this software and associated documentation files (the "Software"), to deal |
11 | | in the Software without restriction, including without limitation the rights |
12 | | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
13 | | copies of the Software, and to permit persons to whom the Software is |
14 | | furnished to do so, subject to the following conditions: |
15 | | |
16 | | The above copyright notice and this permission notice shall be included in |
17 | | all copies or substantial portions of the Software. |
18 | | |
19 | | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
20 | | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
21 | | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
22 | | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
23 | | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
24 | | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
25 | | THE SOFTWARE. |
26 | | ----------------------------------------------------------------------------- |
27 | | */ |
28 | | #include "OgreStableHeaders.h" |
29 | | |
30 | | namespace Ogre { |
31 | | //----------------------------------------------------------------------- |
32 | | SceneNode::SceneNode(SceneManager* creator, const String& name) |
33 | 0 | : Node(name) |
34 | 0 | , mCreator(creator) |
35 | 0 | , mAutoTrackTarget(0) |
36 | 0 | , mGlobalIndex(-1) |
37 | 0 | , mYawFixed(false) |
38 | 0 | , mIsInSceneGraph(false) |
39 | 0 | , mShowBoundingBox(false) |
40 | 0 | { |
41 | 0 | needUpdate(); |
42 | 0 | } |
43 | | //----------------------------------------------------------------------- |
44 | | SceneNode::~SceneNode() |
45 | 0 | { |
46 | | // Detach all objects, do this manually to avoid needUpdate() call |
47 | | // which can fail because of deleted items |
48 | 0 | for (auto & itr : mObjectsByName) |
49 | 0 | { |
50 | 0 | itr->_notifyAttached((SceneNode*)0); |
51 | 0 | } |
52 | 0 | mObjectsByName.clear(); |
53 | 0 | } |
54 | | //----------------------------------------------------------------------- |
55 | | void SceneNode::_update(bool updateChildren, bool parentHasChanged) |
56 | 0 | { |
57 | 0 | Node::_update(updateChildren, parentHasChanged); |
58 | 0 | _updateBounds(); |
59 | 0 | } |
60 | | //----------------------------------------------------------------------- |
61 | | void SceneNode::setParent(Node* parent) |
62 | 0 | { |
63 | 0 | Node::setParent(parent); |
64 | |
|
65 | 0 | if (parent) |
66 | 0 | { |
67 | 0 | SceneNode* sceneParent = static_cast<SceneNode*>(parent); |
68 | 0 | setInSceneGraph(sceneParent->isInSceneGraph()); |
69 | 0 | } |
70 | 0 | else |
71 | 0 | { |
72 | 0 | setInSceneGraph(false); |
73 | 0 | } |
74 | 0 | } |
75 | | //----------------------------------------------------------------------- |
76 | | void SceneNode::setInSceneGraph(bool inGraph) |
77 | 0 | { |
78 | 0 | if (inGraph != mIsInSceneGraph) |
79 | 0 | { |
80 | 0 | mIsInSceneGraph = inGraph; |
81 | | // Tell children |
82 | 0 | for (auto child : getChildren()) |
83 | 0 | { |
84 | 0 | SceneNode* sceneChild = static_cast<SceneNode*>(child); |
85 | 0 | sceneChild->setInSceneGraph(inGraph); |
86 | 0 | } |
87 | 0 | } |
88 | 0 | } |
89 | | //----------------------------------------------------------------------- |
90 | | struct MovableObjectNameExists { |
91 | | const String& name; |
92 | 0 | bool operator()(const MovableObject* mo) { |
93 | 0 | return mo->getName() == name; |
94 | 0 | } |
95 | | }; |
96 | | void SceneNode::attachObject(MovableObject* obj) |
97 | 0 | { |
98 | 0 | OgreAssert(!obj->isAttached(), "Object already attached to a SceneNode or a Bone"); |
99 | | |
100 | 0 | obj->_notifyAttached(this); |
101 | | |
102 | | // Also add to name index |
103 | 0 | MovableObjectNameExists pred = {obj->getName()}; |
104 | 0 | ObjectMap::iterator it = std::find_if(mObjectsByName.begin(), mObjectsByName.end(), pred); |
105 | 0 | if (it != mObjectsByName.end()) |
106 | 0 | OGRE_EXCEPT(Exception::ERR_DUPLICATE_ITEM, |
107 | 0 | "An object named '" + obj->getName() + "' already attached to this SceneNode"); |
108 | 0 | mObjectsByName.push_back(obj); |
109 | | |
110 | | // Make sure bounds get updated (must go right to the top) |
111 | 0 | needUpdate(); |
112 | 0 | } |
113 | | //----------------------------------------------------------------------- |
114 | | MovableObject* SceneNode::getAttachedObject(const String& name) const |
115 | 0 | { |
116 | | // Look up |
117 | 0 | MovableObjectNameExists pred = {name}; |
118 | 0 | auto i = std::find_if(mObjectsByName.begin(), mObjectsByName.end(), pred); |
119 | |
|
120 | 0 | if (i == mObjectsByName.end()) |
121 | 0 | { |
122 | 0 | OGRE_EXCEPT(Exception::ERR_ITEM_NOT_FOUND, "Attached object " + |
123 | 0 | name + " not found.", "SceneNode::getAttachedObject"); |
124 | 0 | } |
125 | | |
126 | 0 | return *i; |
127 | 0 | } |
128 | | //----------------------------------------------------------------------- |
129 | | MovableObject* SceneNode::detachObject(unsigned short index) |
130 | 0 | { |
131 | 0 | OgreAssert(index < mObjectsByName.size(), "out of bounds"); |
132 | 0 | ObjectMap::iterator i = mObjectsByName.begin(); |
133 | 0 | i += index; |
134 | |
|
135 | 0 | MovableObject* ret = *i; |
136 | 0 | std::swap(*i, mObjectsByName.back()); |
137 | 0 | mObjectsByName.pop_back(); |
138 | |
|
139 | 0 | ret->_notifyAttached((SceneNode*)0); |
140 | | |
141 | | // Make sure bounds get updated (must go right to the top) |
142 | 0 | needUpdate(); |
143 | |
|
144 | 0 | return ret; |
145 | 0 | } |
146 | | //----------------------------------------------------------------------- |
147 | | MovableObject* SceneNode::detachObject(const String& name) |
148 | 0 | { |
149 | 0 | MovableObjectNameExists pred = {name}; |
150 | 0 | ObjectMap::iterator it = std::find_if(mObjectsByName.begin(), mObjectsByName.end(), pred); |
151 | |
|
152 | 0 | if (it == mObjectsByName.end()) |
153 | 0 | { |
154 | 0 | OGRE_EXCEPT(Exception::ERR_ITEM_NOT_FOUND, "Object " + name + " is not attached " |
155 | 0 | "to this node.", "SceneNode::detachObject"); |
156 | 0 | } |
157 | | |
158 | 0 | MovableObject* ret = *it; |
159 | 0 | std::swap(*it, mObjectsByName.back()); |
160 | 0 | mObjectsByName.pop_back(); |
161 | |
|
162 | 0 | ret->_notifyAttached((SceneNode*)0); |
163 | | // Make sure bounds get updated (must go right to the top) |
164 | 0 | needUpdate(); |
165 | | |
166 | 0 | return ret; |
167 | |
|
168 | 0 | } |
169 | | //----------------------------------------------------------------------- |
170 | | void SceneNode::detachObject(MovableObject* obj) |
171 | 0 | { |
172 | 0 | auto it = std::find(mObjectsByName.begin(), mObjectsByName.end(), obj); |
173 | 0 | OgreAssert(it != mObjectsByName.end(), "Object is not attached to this node"); |
174 | 0 | std::swap(*it, mObjectsByName.back()); |
175 | 0 | mObjectsByName.pop_back(); |
176 | 0 | obj->_notifyAttached((SceneNode*)0); |
177 | | |
178 | | // Make sure bounds get updated (must go right to the top) |
179 | 0 | needUpdate(); |
180 | 0 | } |
181 | | //----------------------------------------------------------------------- |
182 | | void SceneNode::detachAllObjects(void) |
183 | 0 | { |
184 | 0 | for (auto & itr : mObjectsByName) |
185 | 0 | { |
186 | 0 | itr->_notifyAttached((SceneNode*)0); |
187 | 0 | } |
188 | 0 | mObjectsByName.clear(); |
189 | | // Make sure bounds get updated (must go right to the top) |
190 | 0 | needUpdate(); |
191 | 0 | } |
192 | | //----------------------------------------------------------------------- |
193 | | void SceneNode::destroyAllObjects(void) |
194 | 0 | { |
195 | 0 | while (!getAttachedObjects().empty()) { |
196 | 0 | auto obj = getAttachedObjects().front(); |
197 | 0 | getCreator()->destroyMovableObject(obj); |
198 | 0 | } |
199 | 0 | needUpdate(); |
200 | 0 | } |
201 | | //----------------------------------------------------------------------- |
202 | | void SceneNode::_updateBounds(void) |
203 | 0 | { |
204 | | // Reset bounds first |
205 | 0 | mWorldAABB.setNull(); |
206 | | |
207 | | // Update bounds from own attached objects |
208 | 0 | for (auto *o : mObjectsByName) |
209 | 0 | { |
210 | | // Merge world bounds of each object |
211 | 0 | mWorldAABB.merge(o->getWorldBoundingBox(true)); |
212 | 0 | } |
213 | | |
214 | | // Merge with children |
215 | 0 | for (auto child : getChildren()) |
216 | 0 | { |
217 | 0 | SceneNode* sceneChild = static_cast<SceneNode*>(child); |
218 | 0 | mWorldAABB.merge(sceneChild->mWorldAABB); |
219 | 0 | } |
220 | |
|
221 | 0 | } |
222 | | //----------------------------------------------------------------------- |
223 | | void SceneNode::_findVisibleObjects(Camera* cam, RenderQueue* queue, |
224 | | VisibleObjectsBoundsInfo* visibleBounds, bool includeChildren, |
225 | | bool displayNodes, bool onlyShadowCasters) |
226 | 0 | { |
227 | | // Check self visible |
228 | 0 | if (!cam->isVisible(mWorldAABB)) |
229 | 0 | return; |
230 | | |
231 | | // Add all entities |
232 | 0 | for (auto *o : mObjectsByName) |
233 | 0 | { |
234 | 0 | queue->processVisibleObject(o, cam, onlyShadowCasters, visibleBounds); |
235 | 0 | } |
236 | |
|
237 | 0 | if (includeChildren) |
238 | 0 | { |
239 | 0 | for (auto child : getChildren()) |
240 | 0 | { |
241 | 0 | SceneNode* sceneChild = static_cast<SceneNode*>(child); |
242 | 0 | sceneChild->_findVisibleObjects(cam, queue, visibleBounds, includeChildren, |
243 | 0 | displayNodes, onlyShadowCasters); |
244 | 0 | } |
245 | 0 | } |
246 | |
|
247 | 0 | if (mCreator && mCreator->getDebugDrawer()) |
248 | 0 | { |
249 | 0 | mCreator->getDebugDrawer()->drawSceneNode(this); |
250 | 0 | } |
251 | 0 | } |
252 | | |
253 | 0 | SceneNode::ObjectIterator SceneNode::getAttachedObjectIterator(void) { |
254 | 0 | return ObjectIterator(mObjectsByName.begin(), mObjectsByName.end()); |
255 | 0 | } |
256 | 0 | SceneNode::ConstObjectIterator SceneNode::getAttachedObjectIterator(void) const { |
257 | 0 | return ConstObjectIterator(mObjectsByName.begin(), mObjectsByName.end()); |
258 | 0 | } |
259 | | |
260 | | //----------------------------------------------------------------------- |
261 | | void SceneNode::updateFromParentImpl(void) const |
262 | 0 | { |
263 | 0 | Node::updateFromParentImpl(); |
264 | | |
265 | | // Notify objects that it has been moved |
266 | 0 | for (auto o : mObjectsByName) |
267 | 0 | { |
268 | 0 | o->_notifyMoved(); |
269 | 0 | } |
270 | 0 | } |
271 | | //----------------------------------------------------------------------- |
272 | | Node* SceneNode::createChildImpl(void) |
273 | 0 | { |
274 | 0 | assert(mCreator); |
275 | 0 | return mCreator->createSceneNode(); |
276 | 0 | } |
277 | | //----------------------------------------------------------------------- |
278 | | Node* SceneNode::createChildImpl(const String& name) |
279 | 0 | { |
280 | 0 | assert(mCreator); |
281 | 0 | return mCreator->createSceneNode(name); |
282 | 0 | } |
283 | | //----------------------------------------------------------------------- |
284 | | void SceneNode::removeAndDestroyChild(const String& name) |
285 | 0 | { |
286 | 0 | SceneNode* pChild = static_cast<SceneNode*>(removeChild(name)); |
287 | 0 | pChild->removeAndDestroyAllChildren(); |
288 | |
|
289 | 0 | pChild->getCreator()->destroySceneNode(name); |
290 | |
|
291 | 0 | } |
292 | | //----------------------------------------------------------------------- |
293 | | void SceneNode::removeAndDestroyChild(unsigned short index) |
294 | 0 | { |
295 | 0 | SceneNode* pChild = static_cast<SceneNode*>(removeChild(index)); |
296 | 0 | pChild->removeAndDestroyAllChildren(); |
297 | |
|
298 | 0 | pChild->getCreator()->destroySceneNode(pChild); |
299 | 0 | } |
300 | | //----------------------------------------------------------------------- |
301 | | void SceneNode::removeAndDestroyChild(SceneNode* child) |
302 | 0 | { |
303 | 0 | auto it = std::find(getChildren().begin(), getChildren().end(), child); |
304 | 0 | OgreAssert(it != getChildren().end(), "Not a child of this SceneNode"); |
305 | 0 | removeAndDestroyChild(it - getChildren().begin()); |
306 | 0 | } |
307 | | //----------------------------------------------------------------------- |
308 | | void SceneNode::removeAndDestroyAllChildren(void) |
309 | 0 | { |
310 | | // do not store iterators (invalidated by |
311 | | // SceneManager::destroySceneNode because it causes removal from parent) |
312 | 0 | while(!getChildren().empty()) { |
313 | 0 | SceneNode* sn = static_cast<SceneNode*>(getChildren().front()); |
314 | 0 | sn->removeAndDestroyAllChildren(); |
315 | 0 | sn->getCreator()->destroySceneNode(sn); |
316 | 0 | } |
317 | |
|
318 | 0 | mChildren.clear(); |
319 | 0 | needUpdate(); |
320 | 0 | } |
321 | | //----------------------------------------------------------------------- |
322 | 0 | void SceneNode::destroyChildAndObjects(const String& name) { |
323 | 0 | SceneNode* pChild = static_cast<SceneNode*>(getChild(name)); |
324 | 0 | pChild->destroyAllChildrenAndObjects(); |
325 | |
|
326 | 0 | removeChild(name); |
327 | 0 | pChild->getCreator()->destroySceneNode(name); |
328 | |
|
329 | 0 | } |
330 | | |
331 | 0 | void SceneNode::destroyChildAndObjects(unsigned short index) { |
332 | 0 | SceneNode* pChild = static_cast<SceneNode*>(removeChild(index)); |
333 | 0 | pChild->destroyAllChildrenAndObjects(); |
334 | |
|
335 | 0 | pChild->getCreator()->destroySceneNode(pChild); |
336 | 0 | } |
337 | | |
338 | | void SceneNode::destroyChildAndObjects(SceneNode * child) |
339 | 0 | { |
340 | 0 | auto it = std::find(getChildren().begin(), getChildren().end(), child); |
341 | 0 | OgreAssert(it != getChildren().end(), "Not a child of this SceneNode"); |
342 | 0 | destroyChildAndObjects(it - getChildren().begin()); |
343 | 0 | } |
344 | | |
345 | | void SceneNode::destroyAllChildrenAndObjects() |
346 | 0 | { |
347 | | //remove objects directly attached to this node |
348 | 0 | destroyAllObjects(); |
349 | | |
350 | | //go over children |
351 | 0 | while(!getChildren().empty()) { |
352 | 0 | SceneNode* child = static_cast<SceneNode*>(getChildren().front()); |
353 | | //recurse |
354 | 0 | child->destroyAllChildrenAndObjects(); |
355 | | |
356 | | //destroy child |
357 | 0 | child->getCreator()->destroySceneNode(child); |
358 | 0 | } |
359 | 0 | mChildren.clear(); |
360 | 0 | needUpdate(); |
361 | 0 | } |
362 | | //----------------------------------------------------------------------- |
363 | | void SceneNode::loadChildren(const String& filename) |
364 | 0 | { |
365 | 0 | String baseName, strExt; |
366 | 0 | StringUtil::splitBaseFilename(filename, baseName, strExt); |
367 | 0 | auto codec = Codec::getCodec(strExt); |
368 | 0 | if (!codec) |
369 | 0 | OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "No codec found to load " + filename); |
370 | | |
371 | 0 | auto stream = Root::openFileStream( |
372 | 0 | filename, ResourceGroupManager::getSingleton().getWorldResourceGroupName()); |
373 | 0 | codec->decode(stream, this); |
374 | 0 | } |
375 | | void SceneNode::saveChildren(const String& filename) |
376 | 0 | { |
377 | 0 | String baseName, strExt; |
378 | 0 | StringUtil::splitBaseFilename(filename, baseName, strExt); |
379 | 0 | auto codec = Codec::getCodec(strExt); |
380 | 0 | codec->encodeToFile(this, filename); |
381 | 0 | } |
382 | | //----------------------------------------------------------------------- |
383 | | SceneNode* SceneNode::createChildSceneNode(const Vector3& inTranslate, |
384 | | const Quaternion& inRotate) |
385 | 0 | { |
386 | 0 | return static_cast<SceneNode*>(this->createChild(inTranslate, inRotate)); |
387 | 0 | } |
388 | | //----------------------------------------------------------------------- |
389 | | SceneNode* SceneNode::createChildSceneNode(const String& name, const Vector3& inTranslate, |
390 | | const Quaternion& inRotate) |
391 | 0 | { |
392 | 0 | return static_cast<SceneNode*>(this->createChild(name, inTranslate, inRotate)); |
393 | 0 | } |
394 | | //----------------------------------------------------------------------- |
395 | | void SceneNode::findLights(LightList& destList, Real radius, uint32 lightMask) const |
396 | 0 | { |
397 | | // No any optimisation here, hope inherits more smart for that. |
398 | | // |
399 | | // If a scene node is static and lights have moved, light list won't change |
400 | | // can't use a simple global boolean flag since this is only called for |
401 | | // visible nodes, so temporarily visible nodes will not be updated |
402 | | // Since this is only called for visible nodes, skip the check for now |
403 | | // |
404 | 0 | if (mCreator) |
405 | 0 | { |
406 | | // Use SceneManager to calculate |
407 | 0 | mCreator->_populateLightList(this, radius, destList, lightMask); |
408 | 0 | } |
409 | 0 | else |
410 | 0 | { |
411 | 0 | destList.clear(); |
412 | 0 | } |
413 | 0 | } |
414 | | //----------------------------------------------------------------------- |
415 | | void SceneNode::setAutoTracking(bool enabled, SceneNode* const target, |
416 | | const Vector3& localDirectionVector, |
417 | | const Vector3& offset) |
418 | 0 | { |
419 | 0 | if (enabled) |
420 | 0 | { |
421 | 0 | mAutoTrackTarget = target; |
422 | 0 | mAutoTrackOffset = offset; |
423 | 0 | mAutoTrackLocalDirection = localDirectionVector; |
424 | 0 | } |
425 | 0 | else |
426 | 0 | { |
427 | 0 | mAutoTrackTarget = 0; |
428 | 0 | } |
429 | 0 | if (mCreator) |
430 | 0 | mCreator->_notifyAutotrackingSceneNode(this, enabled); |
431 | 0 | } |
432 | | //----------------------------------------------------------------------- |
433 | | void SceneNode::setFixedYawAxis(bool useFixed, const Vector3& fixedAxis) |
434 | 0 | { |
435 | 0 | mYawFixed = useFixed; |
436 | 0 | mYawFixedAxis = fixedAxis; |
437 | 0 | } |
438 | | |
439 | | //----------------------------------------------------------------------- |
440 | | void SceneNode::yaw(const Radian& angle, TransformSpace relativeTo) |
441 | 0 | { |
442 | 0 | if (mYawFixed) |
443 | 0 | { |
444 | 0 | rotate(mYawFixedAxis, angle, relativeTo); |
445 | 0 | } |
446 | 0 | else |
447 | 0 | { |
448 | 0 | rotate(Vector3::UNIT_Y, angle, relativeTo); |
449 | 0 | } |
450 | |
|
451 | 0 | } |
452 | | //----------------------------------------------------------------------- |
453 | | void SceneNode::setDirection(Real x, Real y, Real z, TransformSpace relativeTo, |
454 | | const Vector3& localDirectionVector) |
455 | 0 | { |
456 | 0 | setDirection(Vector3(x,y,z), relativeTo, localDirectionVector); |
457 | 0 | } |
458 | | |
459 | | //----------------------------------------------------------------------- |
460 | | void SceneNode::setDirection(const Vector3& vec, TransformSpace relativeTo, |
461 | | const Vector3& localDirectionVector) |
462 | 0 | { |
463 | | // Do nothing if given a zero vector |
464 | 0 | if (vec == Vector3::ZERO) return; |
465 | | |
466 | | // The direction we want the local direction point to |
467 | 0 | Vector3 targetDir = vec.normalisedCopy(); |
468 | | |
469 | | // Transform target direction to world space |
470 | 0 | switch (relativeTo) |
471 | 0 | { |
472 | 0 | case TS_PARENT: |
473 | 0 | if (getInheritOrientation()) |
474 | 0 | { |
475 | 0 | if (getParent()) |
476 | 0 | { |
477 | 0 | targetDir = getParent()->_getDerivedOrientation() * targetDir; |
478 | 0 | } |
479 | 0 | } |
480 | 0 | break; |
481 | 0 | case TS_LOCAL: |
482 | 0 | targetDir = _getDerivedOrientation() * targetDir; |
483 | 0 | break; |
484 | 0 | case TS_WORLD: |
485 | | // default orientation |
486 | 0 | break; |
487 | 0 | } |
488 | | |
489 | | // Calculate target orientation relative to world space |
490 | 0 | Quaternion targetOrientation; |
491 | 0 | if( mYawFixed ) |
492 | 0 | { |
493 | | // Calculate the quaternion for rotate local Z to target direction |
494 | 0 | Vector3 yawAxis = mYawFixedAxis; |
495 | |
|
496 | 0 | if (getInheritOrientation() && getParent()) |
497 | 0 | { |
498 | 0 | yawAxis = getParent()->_getDerivedOrientation() * yawAxis; |
499 | 0 | } |
500 | |
|
501 | 0 | Quaternion unitZToTarget = Math::lookRotation(targetDir, yawAxis); |
502 | |
|
503 | 0 | if (localDirectionVector == Vector3::NEGATIVE_UNIT_Z) |
504 | 0 | { |
505 | | // Specail case for avoid calculate 180 degree turn |
506 | 0 | targetOrientation = |
507 | 0 | Quaternion(-unitZToTarget.y, -unitZToTarget.z, unitZToTarget.w, unitZToTarget.x); |
508 | 0 | } |
509 | 0 | else |
510 | 0 | { |
511 | | // Calculate the quaternion for rotate local direction to target direction |
512 | 0 | Quaternion localToUnitZ = localDirectionVector.getRotationTo(Vector3::UNIT_Z); |
513 | 0 | targetOrientation = unitZToTarget * localToUnitZ; |
514 | 0 | } |
515 | 0 | } |
516 | 0 | else |
517 | 0 | { |
518 | 0 | const Quaternion& currentOrient = _getDerivedOrientation(); |
519 | | |
520 | | // Get current local direction relative to world space |
521 | 0 | Vector3 currentDir = currentOrient * localDirectionVector; |
522 | |
|
523 | 0 | if ((currentDir+targetDir).squaredLength() < 0.00005f) |
524 | 0 | { |
525 | | // Oops, a 180 degree turn (infinite possible rotation axes) |
526 | | // Default to yaw i.e. use current UP |
527 | 0 | targetOrientation = |
528 | 0 | Quaternion(-currentOrient.y, -currentOrient.z, currentOrient.w, currentOrient.x); |
529 | 0 | } |
530 | 0 | else |
531 | 0 | { |
532 | | // Derive shortest arc to new direction |
533 | 0 | Quaternion rotQuat = currentDir.getRotationTo(targetDir); |
534 | 0 | targetOrientation = rotQuat * currentOrient; |
535 | 0 | } |
536 | 0 | } |
537 | | |
538 | | // Set target orientation, transformed to parent space |
539 | 0 | if (getParent() && getInheritOrientation()) |
540 | 0 | setOrientation(getParent()->_getDerivedOrientation().UnitInverse() * targetOrientation); |
541 | 0 | else |
542 | 0 | setOrientation(targetOrientation); |
543 | 0 | } |
544 | | //----------------------------------------------------------------------- |
545 | | void SceneNode::lookAt( const Vector3& targetPoint, TransformSpace relativeTo, |
546 | | const Vector3& localDirectionVector) |
547 | 0 | { |
548 | | // Calculate ourself origin relative to the given transform space |
549 | 0 | Vector3 origin; |
550 | 0 | switch (relativeTo) |
551 | 0 | { |
552 | 0 | default: // Just in case |
553 | 0 | case TS_WORLD: |
554 | 0 | origin = _getDerivedPosition(); |
555 | 0 | break; |
556 | 0 | case TS_PARENT: |
557 | 0 | origin = getPosition(); |
558 | 0 | break; |
559 | 0 | case TS_LOCAL: |
560 | 0 | origin = Vector3::ZERO; |
561 | 0 | break; |
562 | 0 | } |
563 | | |
564 | 0 | setDirection(targetPoint - origin, relativeTo, localDirectionVector); |
565 | 0 | } |
566 | | //----------------------------------------------------------------------- |
567 | | void SceneNode::_autoTrack(void) |
568 | 0 | { |
569 | | // NB assumes that all scene nodes have been updated |
570 | 0 | if (mAutoTrackTarget) |
571 | 0 | { |
572 | 0 | lookAt(mAutoTrackTarget->_getDerivedPosition() + mAutoTrackOffset, |
573 | 0 | TS_WORLD, mAutoTrackLocalDirection); |
574 | | // update self & children |
575 | 0 | _update(true, true); |
576 | 0 | } |
577 | 0 | } |
578 | | //----------------------------------------------------------------------- |
579 | | SceneNode* SceneNode::getParentSceneNode(void) const |
580 | 0 | { |
581 | 0 | return static_cast<SceneNode*>(getParent()); |
582 | 0 | } |
583 | | //----------------------------------------------------------------------- |
584 | | void SceneNode::setVisible(bool visible, bool cascade) const |
585 | 0 | { |
586 | 0 | for (auto o : mObjectsByName) |
587 | 0 | { |
588 | 0 | o->setVisible(visible); |
589 | 0 | } |
590 | |
|
591 | 0 | if (cascade) |
592 | 0 | { |
593 | 0 | for (auto c : getChildren()) |
594 | 0 | { |
595 | 0 | static_cast<SceneNode*>(c)->setVisible(visible, cascade); |
596 | 0 | } |
597 | 0 | } |
598 | 0 | } |
599 | | //----------------------------------------------------------------------- |
600 | | void SceneNode::setDebugDisplayEnabled(bool enabled, bool cascade) const |
601 | 0 | { |
602 | 0 | for (auto o : mObjectsByName) |
603 | 0 | { |
604 | 0 | o->setDebugDisplayEnabled(enabled); |
605 | 0 | } |
606 | |
|
607 | 0 | if (cascade) |
608 | 0 | { |
609 | 0 | for (auto c : getChildren()) |
610 | 0 | { |
611 | 0 | static_cast<SceneNode*>(c)->setDebugDisplayEnabled(enabled, cascade); |
612 | 0 | } |
613 | 0 | } |
614 | 0 | } |
615 | | //----------------------------------------------------------------------- |
616 | | void SceneNode::flipVisibility(bool cascade) const |
617 | 0 | { |
618 | 0 | for (auto o : mObjectsByName) |
619 | 0 | { |
620 | 0 | o->setVisible(!o->getVisible()); |
621 | 0 | } |
622 | |
|
623 | 0 | if (cascade) |
624 | 0 | { |
625 | 0 | for (auto c : getChildren()) |
626 | 0 | { |
627 | 0 | static_cast<SceneNode*>(c)->flipVisibility(cascade); |
628 | 0 | } |
629 | 0 | } |
630 | 0 | } |
631 | | } |