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