/src/ogre/OgreMain/src/OgreMovableObject.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 "OgreTagPoint.h" |
31 | | #include "OgreEntity.h" |
32 | | #include "OgreLodListener.h" |
33 | | |
34 | | namespace Ogre { |
35 | | //----------------------------------------------------------------------- |
36 | | //----------------------------------------------------------------------- |
37 | | uint32 MovableObject::msDefaultQueryFlags = 0xFFFFFFFF; |
38 | | uint32 MovableObject::msDefaultVisibilityFlags = 0xFFFFFFFF; |
39 | | //----------------------------------------------------------------------- |
40 | 0 | MovableObject::MovableObject() : MovableObject(BLANKSTRING) {} |
41 | | //----------------------------------------------------------------------- |
42 | | MovableObject::MovableObject(const String& name) |
43 | 0 | : mName(name) |
44 | 0 | , mCreator(0) |
45 | 0 | , mManager(0) |
46 | 0 | , mParentNode(0) |
47 | 0 | , mListener(0) |
48 | 0 | , mParentIsTagPoint(false) |
49 | 0 | , mVisible(true) |
50 | 0 | , mDebugDisplay(false) |
51 | 0 | , mBeyondFarDistance(false) |
52 | 0 | , mCastShadows(true) |
53 | 0 | , mRenderQueueIDSet(false) |
54 | 0 | , mRenderQueuePrioritySet(false) |
55 | 0 | , mRenderingDisabled(false) |
56 | 0 | , mRenderQueueID(RENDER_QUEUE_MAIN) |
57 | 0 | , mRenderQueuePriority(100) |
58 | 0 | , mUpperDistance(0) |
59 | 0 | , mSquaredUpperDistance(0) |
60 | 0 | , mMinPixelSize(0) |
61 | 0 | , mQueryFlags(msDefaultQueryFlags) |
62 | 0 | , mVisibilityFlags(msDefaultVisibilityFlags) |
63 | 0 | , mLightListUpdated(0) |
64 | 0 | , mLightMask(0xFFFFFFFF) |
65 | 0 | { |
66 | 0 | if (Root::getSingletonPtr()) |
67 | 0 | mMinPixelSize = Root::getSingleton().getDefaultMinPixelSize(); |
68 | 0 | } |
69 | | //----------------------------------------------------------------------- |
70 | | MovableObject::~MovableObject() |
71 | 0 | { |
72 | | // Call listener (note, only called if there's something to do) |
73 | 0 | if (mListener) |
74 | 0 | { |
75 | 0 | mListener->objectDestroyed(this); |
76 | 0 | } |
77 | |
|
78 | 0 | detachFromParent(); // this should never throw, if it does terminating is the thing to do |
79 | 0 | } |
80 | | //----------------------------------------------------------------------- |
81 | | void MovableObject::_notifyAttached(Node* parent, bool isTagPoint) |
82 | 0 | { |
83 | 0 | assert(!mParentNode || !parent); |
84 | |
|
85 | 0 | bool different = (parent != mParentNode); |
86 | |
|
87 | 0 | mParentNode = parent; |
88 | 0 | mParentIsTagPoint = isTagPoint; |
89 | | |
90 | | // Mark light list being dirty, simply decrease |
91 | | // counter by one for minimise overhead |
92 | 0 | --mLightListUpdated; |
93 | | |
94 | | // Call listener (note, only called if there's something to do) |
95 | 0 | if (mListener && different) |
96 | 0 | { |
97 | 0 | if (mParentNode) |
98 | 0 | mListener->objectAttached(this); |
99 | 0 | else |
100 | 0 | mListener->objectDetached(this); |
101 | 0 | } |
102 | 0 | } |
103 | | //----------------------------------------------------------------------- |
104 | | SceneNode* MovableObject::getParentSceneNode(void) const |
105 | 0 | { |
106 | 0 | if (mParentIsTagPoint) |
107 | 0 | { |
108 | 0 | TagPoint* tp = static_cast<TagPoint*>(mParentNode); |
109 | 0 | return tp->getParentEntity()->getParentSceneNode(); |
110 | 0 | } |
111 | 0 | else |
112 | 0 | { |
113 | 0 | return static_cast<SceneNode*>(mParentNode); |
114 | 0 | } |
115 | 0 | } |
116 | | //--------------------------------------------------------------------- |
117 | | void MovableObject::detachFromParent(void) |
118 | 0 | { |
119 | 0 | if (isAttached()) |
120 | 0 | { |
121 | 0 | if (mParentIsTagPoint) |
122 | 0 | { |
123 | 0 | TagPoint* tp = static_cast<TagPoint*>(mParentNode); |
124 | 0 | tp->getParentEntity()->detachObjectFromBone(this); |
125 | 0 | } |
126 | 0 | else |
127 | 0 | { |
128 | 0 | SceneNode* sn = static_cast<SceneNode*>(mParentNode); |
129 | 0 | sn->detachObject(this); |
130 | 0 | } |
131 | 0 | } |
132 | 0 | } |
133 | | //----------------------------------------------------------------------- |
134 | | bool MovableObject::isInScene(void) const |
135 | 0 | { |
136 | 0 | if (mParentNode != 0) |
137 | 0 | { |
138 | 0 | if (mParentIsTagPoint) |
139 | 0 | { |
140 | 0 | TagPoint* tp = static_cast<TagPoint*>(mParentNode); |
141 | 0 | return tp->getParentEntity()->isInScene(); |
142 | 0 | } |
143 | 0 | else |
144 | 0 | { |
145 | 0 | SceneNode* sn = static_cast<SceneNode*>(mParentNode); |
146 | 0 | return sn->isInSceneGraph(); |
147 | 0 | } |
148 | 0 | } |
149 | 0 | else |
150 | 0 | { |
151 | 0 | return false; |
152 | 0 | } |
153 | 0 | } |
154 | | //----------------------------------------------------------------------- |
155 | | void MovableObject::_notifyMoved(void) |
156 | 0 | { |
157 | | // Mark light list being dirty, simply decrease |
158 | | // counter by one for minimise overhead |
159 | 0 | --mLightListUpdated; |
160 | | |
161 | | // Notify listener if exists |
162 | 0 | if (mListener) |
163 | 0 | { |
164 | 0 | mListener->objectMoved(this); |
165 | 0 | } |
166 | 0 | } |
167 | | //----------------------------------------------------------------------- |
168 | | bool MovableObject::isVisible(void) const |
169 | 0 | { |
170 | 0 | if (!mVisible || mBeyondFarDistance || mRenderingDisabled) |
171 | 0 | return false; |
172 | | |
173 | 0 | SceneManager* sm = Root::getSingleton()._getCurrentSceneManager(); |
174 | 0 | if (sm && !(getVisibilityFlags() & sm->_getCombinedVisibilityMask())) |
175 | 0 | return false; |
176 | | |
177 | 0 | return true; |
178 | 0 | } |
179 | | //----------------------------------------------------------------------- |
180 | | void MovableObject::_notifyCurrentCamera(Camera* cam) |
181 | 0 | { |
182 | 0 | if (mParentNode) |
183 | 0 | { |
184 | 0 | mBeyondFarDistance = false; |
185 | |
|
186 | 0 | if (cam->getUseRenderingDistance() && mUpperDistance > 0) |
187 | 0 | { |
188 | 0 | Real rad = getBoundingRadiusScaled(); |
189 | 0 | Real squaredDist = mParentNode->getSquaredViewDepth(cam->getLodCamera()); |
190 | | |
191 | | // Max distance to still render |
192 | 0 | Real maxDist = mUpperDistance + rad; |
193 | 0 | if (squaredDist > Math::Sqr(maxDist)) |
194 | 0 | { |
195 | 0 | mBeyondFarDistance = true; |
196 | 0 | } |
197 | 0 | } |
198 | |
|
199 | 0 | if (!mBeyondFarDistance && cam->getUseMinPixelSize() && mMinPixelSize > 0) |
200 | 0 | { |
201 | |
|
202 | 0 | Real pixelRatio = cam->getPixelDisplayRatio(); |
203 | | |
204 | | //if ratio is relative to distance than the distance at which the object should be displayed |
205 | | //is the size of the radius divided by the ratio |
206 | | //get the size of the entity in the world |
207 | 0 | Ogre::Vector3 objBound = getBoundingBox().getSize() * |
208 | 0 | getParentNode()->_getDerivedScale(); |
209 | | |
210 | | //We object are projected from 3 dimensions to 2. The shortest displayed dimension of |
211 | | //as object will always be at most the second largest dimension of the 3 dimensional |
212 | | //bounding box. |
213 | | //The square calculation come both to get rid of minus sign and for improve speed |
214 | | //in the final calculation |
215 | 0 | objBound.x = Math::Sqr(objBound.x); |
216 | 0 | objBound.y = Math::Sqr(objBound.y); |
217 | 0 | objBound.z = Math::Sqr(objBound.z); |
218 | 0 | float sqrObjMedianSize = std::max(std::max( |
219 | 0 | std::min(objBound.x,objBound.y), |
220 | 0 | std::min(objBound.x,objBound.z)), |
221 | 0 | std::min(objBound.y,objBound.z)); |
222 | | |
223 | | //If we have a perspective camera calculations are done relative to distance |
224 | 0 | Real sqrDistance = 1; |
225 | 0 | if (cam->getProjectionType() == PT_PERSPECTIVE) |
226 | 0 | { |
227 | 0 | sqrDistance = mParentNode->getSquaredViewDepth(cam->getLodCamera()); |
228 | 0 | } |
229 | | |
230 | | //Final Calculation to tell whether the object is to small |
231 | 0 | mBeyondFarDistance = sqrObjMedianSize < |
232 | 0 | sqrDistance * Math::Sqr(pixelRatio * mMinPixelSize); |
233 | 0 | } |
234 | | |
235 | | // Construct event object |
236 | 0 | MovableObjectLodChangedEvent evt; |
237 | 0 | evt.movableObject = this; |
238 | 0 | evt.camera = cam; |
239 | | |
240 | | // Notify LOD event listeners |
241 | 0 | cam->getSceneManager()->_notifyMovableObjectLodChanged(evt); |
242 | |
|
243 | 0 | } |
244 | |
|
245 | 0 | mRenderingDisabled = mListener && !mListener->objectRendering(this, cam); |
246 | 0 | } |
247 | | //----------------------------------------------------------------------- |
248 | | void MovableObject::setRenderQueueGroup(uint8 queueID) |
249 | 0 | { |
250 | 0 | assert(queueID <= RENDER_QUEUE_MAX && "Render queue out of range!"); |
251 | 0 | mRenderQueueID = queueID; |
252 | 0 | mRenderQueueIDSet = true; |
253 | 0 | } |
254 | | |
255 | | //----------------------------------------------------------------------- |
256 | | void MovableObject::setRenderQueueGroupAndPriority(uint8 queueID, ushort priority) |
257 | 0 | { |
258 | 0 | setRenderQueueGroup(queueID); |
259 | 0 | mRenderQueuePriority = priority; |
260 | 0 | mRenderQueuePrioritySet = true; |
261 | |
|
262 | 0 | } |
263 | | |
264 | | //----------------------------------------------------------------------- |
265 | | const Affine3& MovableObject::_getParentNodeFullTransform(void) const |
266 | 0 | { |
267 | | |
268 | 0 | if(mParentNode) |
269 | 0 | { |
270 | | // object attached to a sceneNode |
271 | 0 | return mParentNode->_getFullTransform(); |
272 | 0 | } |
273 | | // fallback |
274 | 0 | return Affine3::IDENTITY; |
275 | 0 | } |
276 | | |
277 | | Real MovableObject::getBoundingRadiusScaled() const |
278 | 0 | { |
279 | 0 | const Vector3& scl = mParentNode->_getDerivedScale(); |
280 | 0 | Real factor = std::max(std::max(std::abs(scl.x), std::abs(scl.y)), std::abs(scl.z)); |
281 | 0 | return getBoundingRadius() * factor; |
282 | 0 | } |
283 | | |
284 | | //----------------------------------------------------------------------- |
285 | | const AxisAlignedBox& MovableObject::getWorldBoundingBox(bool derive) const |
286 | 0 | { |
287 | 0 | if (derive) |
288 | 0 | { |
289 | 0 | mWorldAABB = this->getBoundingBox(); |
290 | 0 | mWorldAABB.transform(_getParentNodeFullTransform()); |
291 | 0 | } |
292 | |
|
293 | 0 | return mWorldAABB; |
294 | |
|
295 | 0 | } |
296 | | //----------------------------------------------------------------------- |
297 | | const Sphere& MovableObject::getWorldBoundingSphere(bool derive) const |
298 | 0 | { |
299 | 0 | if (derive) |
300 | 0 | { |
301 | 0 | mWorldBoundingSphere.setRadius(getBoundingRadiusScaled()); |
302 | 0 | mWorldBoundingSphere.setCenter(mParentNode->_getDerivedPosition()); |
303 | 0 | } |
304 | 0 | return mWorldBoundingSphere; |
305 | 0 | } |
306 | | //----------------------------------------------------------------------- |
307 | | const LightList& MovableObject::queryLights(void) const |
308 | 0 | { |
309 | | // Try listener first |
310 | 0 | if (mListener) |
311 | 0 | { |
312 | 0 | const LightList* lightList = |
313 | 0 | mListener->objectQueryLights(this); |
314 | 0 | if (lightList) |
315 | 0 | { |
316 | 0 | return *lightList; |
317 | 0 | } |
318 | 0 | } |
319 | | |
320 | | // Query from parent entity if exists |
321 | 0 | if (mParentIsTagPoint) |
322 | 0 | { |
323 | 0 | TagPoint* tp = static_cast<TagPoint*>(mParentNode); |
324 | 0 | return tp->getParentEntity()->queryLights(); |
325 | 0 | } |
326 | | |
327 | 0 | if (mParentNode) |
328 | 0 | { |
329 | 0 | SceneNode* sn = static_cast<SceneNode*>(mParentNode); |
330 | | |
331 | | // Make sure we only update this only if need. |
332 | 0 | ulong frame = sn->getCreator()->_getLightsDirtyCounter(); |
333 | 0 | if (mLightListUpdated != frame) |
334 | 0 | { |
335 | 0 | mLightListUpdated = frame; |
336 | |
|
337 | 0 | sn->findLights(mLightList, getBoundingRadiusScaled(), this->getLightMask()); |
338 | 0 | } |
339 | 0 | } |
340 | 0 | else |
341 | 0 | { |
342 | 0 | mLightList.clear(); |
343 | 0 | } |
344 | |
|
345 | 0 | return mLightList; |
346 | 0 | } |
347 | | //----------------------------------------------------------------------- |
348 | | Real MovableObject::getPointExtrusionDistance(const Light* l) const |
349 | 0 | { |
350 | 0 | if (mParentNode) |
351 | 0 | { |
352 | | // exclude distance from the light to the shadow caster from the extrusion |
353 | 0 | Real extrusionDistance = l->getAttenuationRange() - getWorldBoundingBox().distance(l->getDerivedPosition()); |
354 | 0 | if(extrusionDistance < 0) |
355 | 0 | extrusionDistance = 0; |
356 | | |
357 | | // Take into account that extrusion would be done in object-space, |
358 | | // and non-uniformly scaled objects would cast non-uniformly scaled shadows. |
359 | 0 | Matrix3 m3 = _getParentNodeFullTransform().linear(); |
360 | 0 | Real c0 = m3.GetColumn(0).squaredLength(), c1 = m3.GetColumn(1).squaredLength(), c2 = m3.GetColumn(2).squaredLength(); |
361 | 0 | Real minScale = Math::Sqrt(std::min(std::min(c0, c1), c2)); |
362 | 0 | Real maxScale = Math::Sqrt(std::max(std::max(c0, c1), c2)); |
363 | 0 | if(minScale > 0.0) |
364 | 0 | extrusionDistance *= (maxScale / minScale); |
365 | | |
366 | 0 | return extrusionDistance; |
367 | 0 | } |
368 | 0 | else |
369 | 0 | { |
370 | 0 | return 0; |
371 | 0 | } |
372 | 0 | } |
373 | | //----------------------------------------------------------------------- |
374 | | uint32 MovableObject::getTypeFlags(void) const |
375 | 0 | { |
376 | 0 | if (mCreator) |
377 | 0 | { |
378 | 0 | return mCreator->getTypeFlags(); |
379 | 0 | } |
380 | 0 | else |
381 | 0 | { |
382 | 0 | return 0xFFFFFFFF; |
383 | 0 | } |
384 | 0 | } |
385 | | //--------------------------------------------------------------------- |
386 | | void MovableObject::setLightMask(uint32 lightMask) |
387 | 0 | { |
388 | 0 | this->mLightMask = lightMask; |
389 | | //make sure to request a new light list from the scene manager if mask changed |
390 | 0 | mLightListUpdated = 0; |
391 | 0 | } |
392 | | //--------------------------------------------------------------------- |
393 | | class MORecvShadVisitor : public Renderable::Visitor |
394 | | { |
395 | | public: |
396 | | bool anyReceiveShadows; |
397 | 0 | MORecvShadVisitor() : anyReceiveShadows(false) |
398 | 0 | { |
399 | |
|
400 | 0 | } |
401 | | void visit(Renderable* rend, ushort lodIndex, bool isDebug, Any* pAny = 0) override |
402 | 0 | { |
403 | 0 | const auto& mat = rend->getMaterial(); |
404 | 0 | bool techReceivesShadows = !mat || mat->getReceiveShadows(); |
405 | 0 | anyReceiveShadows = anyReceiveShadows || techReceivesShadows; |
406 | 0 | } |
407 | | }; |
408 | | //--------------------------------------------------------------------- |
409 | | bool MovableObject::getReceivesShadows() |
410 | 0 | { |
411 | 0 | MORecvShadVisitor visitor; |
412 | 0 | visitRenderables(&visitor); |
413 | 0 | return visitor.anyReceiveShadows; |
414 | |
|
415 | 0 | } |
416 | | //----------------------------------------------------------------------- |
417 | | //----------------------------------------------------------------------- |
418 | | MovableObject* MovableObjectFactory::createInstance( |
419 | | const String& name, SceneManager* manager, |
420 | | const NameValuePairList* params) |
421 | 0 | { |
422 | 0 | MovableObject* m = createInstanceImpl(name, params); |
423 | 0 | m->_notifyCreator(this); |
424 | 0 | m->_notifyManager(manager); |
425 | 0 | return m; |
426 | 0 | } |
427 | | |
428 | | |
429 | | } |
430 | | |