BsRenderable.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672
  1. //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
  2. //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
  3. #include "Renderer/BsRenderable.h"
  4. #include "RTTI/BsRenderableRTTI.h"
  5. #include "Scene/BsSceneObject.h"
  6. #include "Mesh/BsMesh.h"
  7. #include "Material/BsMaterial.h"
  8. #include "Math/BsBounds.h"
  9. #include "Renderer/BsRenderer.h"
  10. #include "Animation/BsAnimation.h"
  11. #include "Allocators/BsFrameAlloc.h"
  12. #include "Animation/BsMorphShapes.h"
  13. #include "RenderAPI/BsGpuBuffer.h"
  14. #include "Animation/BsAnimationManager.h"
  15. namespace bs
  16. {
  17. template<class T>
  18. bool isMeshValid(const T& mesh) { return false; }
  19. template<>
  20. bool isMeshValid(const HMesh& mesh) { return mesh.isLoaded(); }
  21. template<>
  22. bool isMeshValid(const SPtr<ct::Mesh>& mesh) { return mesh != nullptr; }
  23. template<bool Core>
  24. TRenderable<Core>::TRenderable()
  25. : mLayer(1), mUseOverrideBounds(false), mPosition(BsZero), mTransform(BsIdentity), mTransformNoScale(BsIdentity)
  26. , mIsActive(true), mAnimType(RenderableAnimType::None), mMobility(ObjectMobility::Movable)
  27. {
  28. mMaterials.resize(1);
  29. }
  30. template<bool Core>
  31. TRenderable<Core>::~TRenderable()
  32. {
  33. }
  34. template<bool Core>
  35. void TRenderable<Core>::setMesh(const MeshType& mesh)
  36. {
  37. mMesh = mesh;
  38. int numSubMeshes = 0;
  39. if (isMeshValid(mesh))
  40. numSubMeshes = mesh->getProperties().getNumSubMeshes();
  41. mMaterials.resize(numSubMeshes);
  42. onMeshChanged();
  43. _markDependenciesDirty();
  44. _markResourcesDirty();
  45. _markCoreDirty();
  46. }
  47. template<bool Core>
  48. void TRenderable<Core>::setMaterial(UINT32 idx, const MaterialType& material)
  49. {
  50. if (idx >= (UINT32)mMaterials.size())
  51. return;
  52. mMaterials[idx] = material;
  53. _markDependenciesDirty();
  54. _markResourcesDirty();
  55. _markCoreDirty();
  56. }
  57. template<bool Core>
  58. void TRenderable<Core>::setMaterials(const Vector<MaterialType>& materials)
  59. {
  60. UINT32 numMaterials = (UINT32)mMaterials.size();
  61. UINT32 min = std::min(numMaterials, (UINT32)materials.size());
  62. for (UINT32 i = 0; i < min; i++)
  63. mMaterials[i] = materials[i];
  64. for (UINT32 i = min; i < numMaterials; i++)
  65. mMaterials[i] = nullptr;
  66. _markDependenciesDirty();
  67. _markResourcesDirty();
  68. _markCoreDirty();
  69. }
  70. template<bool Core>
  71. void TRenderable<Core>::setMaterial(const MaterialType& material)
  72. {
  73. setMaterial(0, material);
  74. }
  75. template<bool Core>
  76. void TRenderable<Core>::setLayer(UINT64 layer)
  77. {
  78. bool isPow2 = layer && !((layer - 1) & layer);
  79. if (!isPow2)
  80. {
  81. LOGWRN("Invalid layer provided. Only one layer bit may be set. Ignoring.");
  82. return;
  83. }
  84. mLayer = layer;
  85. _markCoreDirty();
  86. }
  87. template<bool Core>
  88. void TRenderable<Core>::setTransform(const Matrix4& transform, const Matrix4& transformNoScale)
  89. {
  90. mTransform = transform;
  91. mTransformNoScale = transformNoScale;
  92. mPosition = mTransform.getTranslation();
  93. _markCoreDirty(RenderableDirtyFlag::Transform);
  94. }
  95. template<bool Core>
  96. void TRenderable<Core>::setIsActive(bool active)
  97. {
  98. mIsActive = active;
  99. _markCoreDirty();
  100. }
  101. template <bool Core>
  102. void TRenderable<Core>::setMobility(ObjectMobility mobility)
  103. {
  104. mMobility = mobility;
  105. _markCoreDirty(RenderableDirtyFlag::Mobility);
  106. }
  107. template<bool Core>
  108. void TRenderable<Core>::setOverrideBounds(const AABox& bounds)
  109. {
  110. mOverrideBounds = bounds;
  111. if(mUseOverrideBounds)
  112. _markCoreDirty();
  113. }
  114. template<bool Core>
  115. void TRenderable<Core>::setUseOverrideBounds(bool enable)
  116. {
  117. if (mUseOverrideBounds == enable)
  118. return;
  119. mUseOverrideBounds = enable;
  120. _markCoreDirty();
  121. }
  122. template class TRenderable < false >;
  123. template class TRenderable < true >;
  124. Renderable::Renderable()
  125. :mLastUpdateHash(0)
  126. {
  127. }
  128. void Renderable::setAnimation(const SPtr<Animation>& animation)
  129. {
  130. mAnimation = animation;
  131. refreshAnimation();
  132. _markCoreDirty();
  133. }
  134. Bounds Renderable::getBounds() const
  135. {
  136. if(mUseOverrideBounds)
  137. {
  138. Sphere sphere(mOverrideBounds.getCenter(), mOverrideBounds.getRadius());
  139. Bounds bounds(mOverrideBounds, sphere);
  140. bounds.transformAffine(mTransform);
  141. return bounds;
  142. }
  143. HMesh mesh = getMesh();
  144. if (!mesh.isLoaded())
  145. {
  146. AABox box(mPosition, mPosition);
  147. Sphere sphere(mPosition, 0.0f);
  148. return Bounds(box, sphere);
  149. }
  150. else
  151. {
  152. Bounds bounds = mesh->getProperties().getBounds();
  153. bounds.transformAffine(mTransform);
  154. return bounds;
  155. }
  156. }
  157. SPtr<ct::Renderable> Renderable::getCore() const
  158. {
  159. return std::static_pointer_cast<ct::Renderable>(mCoreSpecific);
  160. }
  161. SPtr<ct::CoreObject> Renderable::createCore() const
  162. {
  163. ct::Renderable* handler = new (bs_alloc<ct::Renderable>()) ct::Renderable();
  164. SPtr<ct::Renderable> handlerPtr = bs_shared_ptr<ct::Renderable>(handler);
  165. handlerPtr->_setThisPtr(handlerPtr);
  166. return handlerPtr;
  167. }
  168. void Renderable::onMeshChanged()
  169. {
  170. refreshAnimation();
  171. }
  172. void Renderable::refreshAnimation()
  173. {
  174. if (mAnimation == nullptr)
  175. {
  176. mAnimType = RenderableAnimType::None;
  177. return;
  178. }
  179. if (mMesh.isLoaded(false))
  180. {
  181. SPtr<Skeleton> skeleton = mMesh->getSkeleton();
  182. SPtr<MorphShapes> morphShapes = mMesh->getMorphShapes();
  183. if (skeleton != nullptr && morphShapes != nullptr)
  184. mAnimType = RenderableAnimType::SkinnedMorph;
  185. else if (skeleton != nullptr)
  186. mAnimType = RenderableAnimType::Skinned;
  187. else if (morphShapes != nullptr)
  188. mAnimType = RenderableAnimType::Morph;
  189. else
  190. mAnimType = RenderableAnimType::None;
  191. mAnimation->setSkeleton(mMesh->getSkeleton());
  192. mAnimation->setMorphShapes(mMesh->getMorphShapes());
  193. }
  194. else
  195. {
  196. mAnimType = RenderableAnimType::None;
  197. mAnimation->setSkeleton(nullptr);
  198. mAnimation->setMorphShapes(nullptr);
  199. }
  200. }
  201. void Renderable::_updateTransform(const HSceneObject& so, bool force)
  202. {
  203. UINT32 curHash = so->getTransformHash();
  204. if (curHash != _getLastModifiedHash() || force)
  205. {
  206. // If skinned animation, don't include own transform since that will be handled by root bone animation
  207. bool ignoreOwnTransform;
  208. if (mAnimType == RenderableAnimType::Skinned || mAnimType == RenderableAnimType::SkinnedMorph)
  209. ignoreOwnTransform = mAnimation->_getAnimatesRoot();
  210. else
  211. ignoreOwnTransform = false;
  212. if (ignoreOwnTransform)
  213. {
  214. // Note: Technically we're checking child's hash but using parent's transform. Ideally we check the parent's
  215. // hash to reduce the number of required updates.
  216. HSceneObject parentSO = so->getParent();
  217. if (parentSO != nullptr)
  218. {
  219. Matrix4 transformNoScale = Matrix4::TRS(parentSO->getWorldPosition(), parentSO->getWorldRotation(),
  220. Vector3::ONE);
  221. setTransform(parentSO->getWorldTfrm(), transformNoScale);
  222. }
  223. else
  224. setTransform(Matrix4::IDENTITY, Matrix4::IDENTITY);
  225. }
  226. else
  227. {
  228. Matrix4 transformNoScale = Matrix4::TRS(so->getWorldPosition(), so->getWorldRotation(), Vector3::ONE);
  229. setTransform(so->getWorldTfrm(), transformNoScale);
  230. }
  231. _setLastModifiedHash(curHash);
  232. }
  233. }
  234. void Renderable::_markCoreDirty(RenderableDirtyFlag flag)
  235. {
  236. markCoreDirty((UINT32)flag);
  237. }
  238. void Renderable::_markDependenciesDirty()
  239. {
  240. markDependenciesDirty();
  241. }
  242. void Renderable::_markResourcesDirty()
  243. {
  244. markListenerResourcesDirty();
  245. }
  246. CoreSyncData Renderable::syncToCore(FrameAlloc* allocator)
  247. {
  248. UINT32 numMaterials = (UINT32)mMaterials.size();
  249. UINT64 animationId;
  250. if (mAnimation != nullptr)
  251. animationId = mAnimation->_getId();
  252. else
  253. animationId = (UINT64)-1;
  254. UINT32 size = rttiGetElemSize(mLayer) +
  255. rttiGetElemSize(mOverrideBounds) +
  256. rttiGetElemSize(mUseOverrideBounds) +
  257. rttiGetElemSize(numMaterials) +
  258. rttiGetElemSize(mTransform) +
  259. rttiGetElemSize(mTransformNoScale) +
  260. rttiGetElemSize(mPosition) +
  261. rttiGetElemSize(mIsActive) +
  262. rttiGetElemSize(animationId) +
  263. rttiGetElemSize(mAnimType) +
  264. rttiGetElemSize(mMobility) +
  265. rttiGetElemSize(getCoreDirtyFlags()) +
  266. sizeof(SPtr<ct::Mesh>) +
  267. numMaterials * sizeof(SPtr<ct::Material>);
  268. UINT8* data = allocator->alloc(size);
  269. char* dataPtr = (char*)data;
  270. dataPtr = rttiWriteElem(mLayer, dataPtr);
  271. dataPtr = rttiWriteElem(mOverrideBounds, dataPtr);
  272. dataPtr = rttiWriteElem(mUseOverrideBounds, dataPtr);
  273. dataPtr = rttiWriteElem(numMaterials, dataPtr);
  274. dataPtr = rttiWriteElem(mTransform, dataPtr);
  275. dataPtr = rttiWriteElem(mTransformNoScale, dataPtr);
  276. dataPtr = rttiWriteElem(mPosition, dataPtr);
  277. dataPtr = rttiWriteElem(mIsActive, dataPtr);
  278. dataPtr = rttiWriteElem(animationId, dataPtr);
  279. dataPtr = rttiWriteElem(mAnimType, dataPtr);
  280. dataPtr = rttiWriteElem(mMobility, dataPtr);
  281. dataPtr = rttiWriteElem(getCoreDirtyFlags(), dataPtr);
  282. SPtr<ct::Mesh>* mesh = new (dataPtr) SPtr<ct::Mesh>();
  283. if (mMesh.isLoaded())
  284. *mesh = mMesh->getCore();
  285. dataPtr += sizeof(SPtr<ct::Mesh>);
  286. for (UINT32 i = 0; i < numMaterials; i++)
  287. {
  288. SPtr<ct::Material>* material = new (dataPtr)SPtr<ct::Material>();
  289. if (mMaterials[i].isLoaded())
  290. *material = mMaterials[i]->getCore();
  291. dataPtr += sizeof(SPtr<ct::Material>);
  292. }
  293. return CoreSyncData(data, size);
  294. }
  295. void Renderable::getCoreDependencies(Vector<CoreObject*>& dependencies)
  296. {
  297. if (mMesh.isLoaded())
  298. dependencies.push_back(mMesh.get());
  299. for (auto& material : mMaterials)
  300. {
  301. if (material.isLoaded())
  302. dependencies.push_back(material.get());
  303. }
  304. }
  305. void Renderable::getListenerResources(Vector<HResource>& resources)
  306. {
  307. if (mMesh != nullptr)
  308. resources.push_back(mMesh);
  309. for (auto& material : mMaterials)
  310. {
  311. if (material != nullptr)
  312. resources.push_back(material);
  313. }
  314. }
  315. void Renderable::notifyResourceLoaded(const HResource& resource)
  316. {
  317. if (resource == mMesh)
  318. onMeshChanged();
  319. markDependenciesDirty();
  320. markCoreDirty();
  321. }
  322. void Renderable::notifyResourceChanged(const HResource& resource)
  323. {
  324. if(resource == mMesh)
  325. onMeshChanged();
  326. markDependenciesDirty();
  327. markCoreDirty();
  328. }
  329. SPtr<Renderable> Renderable::create()
  330. {
  331. SPtr<Renderable> handlerPtr = createEmpty();
  332. handlerPtr->initialize();
  333. return handlerPtr;
  334. }
  335. SPtr<Renderable> Renderable::createEmpty()
  336. {
  337. Renderable* handler = new (bs_alloc<Renderable>()) Renderable();
  338. SPtr<Renderable> handlerPtr = bs_core_ptr<Renderable>(handler);
  339. handlerPtr->_setThisPtr(handlerPtr);
  340. return handlerPtr;
  341. }
  342. RTTITypeBase* Renderable::getRTTIStatic()
  343. {
  344. return RenderableRTTI::instance();
  345. }
  346. RTTITypeBase* Renderable::getRTTI() const
  347. {
  348. return Renderable::getRTTIStatic();
  349. }
  350. namespace ct
  351. {
  352. Renderable::Renderable()
  353. :mRendererId(0), mAnimationId((UINT64)-1), mMorphShapeVersion(0)
  354. {
  355. }
  356. Renderable::~Renderable()
  357. {
  358. if (mIsActive)
  359. gRenderer()->notifyRenderableRemoved(this);
  360. }
  361. void Renderable::initialize()
  362. {
  363. gRenderer()->notifyRenderableAdded(this);
  364. CoreObject::initialize();
  365. }
  366. Bounds Renderable::getBounds() const
  367. {
  368. if (mUseOverrideBounds)
  369. {
  370. Sphere sphere(mOverrideBounds.getCenter(), mOverrideBounds.getRadius());
  371. Bounds bounds(mOverrideBounds, sphere);
  372. bounds.transformAffine(mTransform);
  373. return bounds;
  374. }
  375. SPtr<Mesh> mesh = getMesh();
  376. if (mesh == nullptr)
  377. {
  378. AABox box(mPosition, mPosition);
  379. Sphere sphere(mPosition, 0.0f);
  380. return Bounds(box, sphere);
  381. }
  382. else
  383. {
  384. Bounds bounds = mesh->getProperties().getBounds();
  385. bounds.transformAffine(mTransform);
  386. return bounds;
  387. }
  388. }
  389. void Renderable::createAnimationBuffers()
  390. {
  391. if (mAnimType == RenderableAnimType::Skinned || mAnimType == RenderableAnimType::SkinnedMorph)
  392. {
  393. SPtr<Skeleton> skeleton = mMesh->getSkeleton();
  394. UINT32 numBones = skeleton != nullptr ? skeleton->getNumBones() : 0;
  395. if (numBones > 0)
  396. {
  397. GPU_BUFFER_DESC desc;
  398. desc.elementCount = numBones * 3;
  399. desc.elementSize = 0;
  400. desc.type = GBT_STANDARD;
  401. desc.format = BF_32X4F;
  402. desc.usage = GBU_DYNAMIC;
  403. SPtr<GpuBuffer> buffer = GpuBuffer::create(desc);
  404. UINT8* dest = (UINT8*)buffer->lock(0, numBones * 3 * sizeof(Vector4), GBL_WRITE_ONLY_DISCARD);
  405. // Initialize bone transforms to identity, so the object renders properly even if no animation is animating it
  406. for (UINT32 i = 0; i < numBones; i++)
  407. {
  408. memcpy(dest, &Matrix4::IDENTITY, 12 * sizeof(float)); // Assuming row-major format
  409. dest += 12 * sizeof(float);
  410. }
  411. buffer->unlock();
  412. mBoneMatrixBuffer = buffer;
  413. }
  414. else
  415. mBoneMatrixBuffer = nullptr;
  416. }
  417. else
  418. mBoneMatrixBuffer = nullptr;
  419. if (mAnimType == RenderableAnimType::Morph || mAnimType == RenderableAnimType::SkinnedMorph)
  420. {
  421. SPtr<MorphShapes> morphShapes = mMesh->getMorphShapes();
  422. UINT32 vertexSize = sizeof(Vector3) + sizeof(UINT32);
  423. UINT32 numVertices = morphShapes->getNumVertices();
  424. VERTEX_BUFFER_DESC desc;
  425. desc.vertexSize = vertexSize;
  426. desc.numVerts = numVertices;
  427. desc.usage = GBU_DYNAMIC;
  428. SPtr<VertexBuffer> vertexBuffer = VertexBuffer::create(desc);
  429. UINT32 totalSize = vertexSize * numVertices;
  430. UINT8* dest = (UINT8*)vertexBuffer->lock(0, totalSize, GBL_WRITE_ONLY_DISCARD);
  431. memset(dest, 0, totalSize);
  432. vertexBuffer->unlock();
  433. mMorphShapeBuffer = vertexBuffer;
  434. }
  435. else
  436. mMorphShapeBuffer = nullptr;
  437. mMorphShapeVersion = 0;
  438. }
  439. void Renderable::updateAnimationBuffers(const RendererAnimationData& animData)
  440. {
  441. if (mAnimationId == (UINT64)-1)
  442. return;
  443. const RendererAnimationData::AnimInfo* animInfo = nullptr;
  444. auto iterFind = animData.infos.find(mAnimationId);
  445. if (iterFind != animData.infos.end())
  446. animInfo = &iterFind->second;
  447. if (animInfo == nullptr)
  448. return;
  449. if (mAnimType == RenderableAnimType::Skinned || mAnimType == RenderableAnimType::SkinnedMorph)
  450. {
  451. const RendererAnimationData::PoseInfo& poseInfo = animInfo->poseInfo;
  452. // Note: If multiple elements are using the same animation (not possible atm), this buffer should be shared by
  453. // all such elements
  454. UINT8* dest = (UINT8*)mBoneMatrixBuffer->lock(0, poseInfo.numBones * 3 * sizeof(Vector4), GBL_WRITE_ONLY_DISCARD);
  455. for (UINT32 j = 0; j < poseInfo.numBones; j++)
  456. {
  457. const Matrix4& transform = animData.transforms[poseInfo.startIdx + j];
  458. memcpy(dest, &transform, 12 * sizeof(float)); // Assuming row-major format
  459. dest += 12 * sizeof(float);
  460. }
  461. mBoneMatrixBuffer->unlock();
  462. }
  463. if (mAnimType == RenderableAnimType::Morph || mAnimType == RenderableAnimType::SkinnedMorph)
  464. {
  465. if (mMorphShapeVersion != animInfo->morphShapeInfo.version)
  466. {
  467. SPtr<MeshData> meshData = animInfo->morphShapeInfo.meshData;
  468. UINT32 bufferSize = meshData->getSize();
  469. UINT8* data = meshData->getData();
  470. mMorphShapeBuffer->writeData(0, bufferSize, data, BWT_DISCARD);
  471. mMorphShapeVersion = animInfo->morphShapeInfo.version;
  472. }
  473. }
  474. }
  475. void Renderable::syncToCore(const CoreSyncData& data)
  476. {
  477. char* dataPtr = (char*)data.getBuffer();
  478. mMaterials.clear();
  479. UINT32 numMaterials = 0;
  480. UINT32 dirtyFlags = 0;
  481. bool oldIsActive = mIsActive;
  482. dataPtr = rttiReadElem(mLayer, dataPtr);
  483. dataPtr = rttiReadElem(mOverrideBounds, dataPtr);
  484. dataPtr = rttiReadElem(mUseOverrideBounds, dataPtr);
  485. dataPtr = rttiReadElem(numMaterials, dataPtr);
  486. dataPtr = rttiReadElem(mTransform, dataPtr);
  487. dataPtr = rttiReadElem(mTransformNoScale, dataPtr);
  488. dataPtr = rttiReadElem(mPosition, dataPtr);
  489. dataPtr = rttiReadElem(mIsActive, dataPtr);
  490. dataPtr = rttiReadElem(mAnimationId, dataPtr);
  491. dataPtr = rttiReadElem(mAnimType, dataPtr);
  492. dataPtr = rttiReadElem(mMobility, dataPtr);
  493. dataPtr = rttiReadElem(dirtyFlags, dataPtr);
  494. SPtr<Mesh>* mesh = (SPtr<Mesh>*)dataPtr;
  495. mMesh = *mesh;
  496. mesh->~SPtr<Mesh>();
  497. dataPtr += sizeof(SPtr<Mesh>);
  498. for (UINT32 i = 0; i < numMaterials; i++)
  499. {
  500. SPtr<Material>* material = (SPtr<Material>*)dataPtr;
  501. mMaterials.push_back(*material);
  502. material->~SPtr<Material>();
  503. dataPtr += sizeof(SPtr<Material>);
  504. }
  505. if((dirtyFlags & (UINT32)RenderableDirtyFlag::Everything) != 0)
  506. {
  507. createAnimationBuffers();
  508. // Create special vertex declaration if using morph shapes
  509. if (mAnimType == RenderableAnimType::Morph || mAnimType == RenderableAnimType::SkinnedMorph)
  510. {
  511. SPtr<VertexDataDesc> vertexDesc = VertexDataDesc::create();
  512. *vertexDesc = * mMesh->getVertexDesc();
  513. vertexDesc->addVertElem(VET_FLOAT3, VES_POSITION, 1, 1);
  514. vertexDesc->addVertElem(VET_UBYTE4_NORM, VES_NORMAL, 1, 1);
  515. mMorphVertexDeclaration = VertexDeclaration::create(vertexDesc);
  516. }
  517. else
  518. mMorphVertexDeclaration = nullptr;
  519. if (oldIsActive != mIsActive)
  520. {
  521. if (mIsActive)
  522. gRenderer()->notifyRenderableAdded(this);
  523. else
  524. gRenderer()->notifyRenderableRemoved(this);
  525. }
  526. else
  527. {
  528. gRenderer()->notifyRenderableRemoved(this);
  529. gRenderer()->notifyRenderableAdded(this);
  530. }
  531. }
  532. else if((dirtyFlags & (UINT32)RenderableDirtyFlag::Mobility) != 0)
  533. {
  534. gRenderer()->notifyRenderableRemoved(this);
  535. gRenderer()->notifyRenderableAdded(this);
  536. }
  537. else if ((dirtyFlags & (UINT32)RenderableDirtyFlag::Transform) != 0)
  538. {
  539. if (mIsActive)
  540. gRenderer()->notifyRenderableUpdated(this);
  541. }
  542. }
  543. }
  544. }