1//************************************ bs::framework - Copyright 2018 Marko Pintera **************************************//
2//*********** Licensed under the MIT license. See LICENSE.md for full terms. This notice is not to be removed. ***********//
3#include "Renderer/BsRenderable.h"
4#include "Private/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 "Animation/BsMorphShapes.h"
12#include "RenderAPI/BsGpuBuffer.h"
13#include "Animation/BsAnimationManager.h"
14#include "Scene/BsSceneManager.h"
15#include "CoreThread/BsCoreObjectSync.h"
16
17namespace bs
18{
19 template<class T>
20 bool isMeshValid(const T& mesh) { return false; }
21
22 template<>
23 bool isMeshValid(const HMesh& mesh) { return mesh.isLoaded(); }
24
25 template<>
26 bool isMeshValid(const SPtr<ct::Mesh>& mesh) { return mesh != nullptr; }
27
28 template<bool Core>
29 TRenderable<Core>::TRenderable()
30 {
31 mMaterials.resize(1);
32 }
33
34 template <bool Core>
35 void TRenderable<Core>::setTransform(const Transform& transform)
36 {
37 if (mMobility != ObjectMobility::Movable)
38 return;
39
40 mTransform = transform;
41 mTfrmMatrix = transform.getMatrix();
42 mTfrmMatrixNoScale = Matrix4::TRS(transform.getPosition(), transform.getRotation(), Vector3::ONE);
43
44 _markCoreDirty(ActorDirtyFlag::Transform);
45 }
46
47 template<bool Core>
48 void TRenderable<Core>::setMesh(const MeshType& mesh)
49 {
50 mMesh = mesh;
51
52 int numSubMeshes = 0;
53 if (isMeshValid(mesh))
54 numSubMeshes = mesh->getProperties().getNumSubMeshes();
55
56 mMaterials.resize(numSubMeshes);
57
58 onMeshChanged();
59
60 _markDependenciesDirty();
61 _markResourcesDirty();
62 _markCoreDirty();
63 }
64
65 template<bool Core>
66 void TRenderable<Core>::setMaterial(UINT32 idx, const MaterialType& material)
67 {
68 if (idx >= (UINT32)mMaterials.size())
69 return;
70
71 mMaterials[idx] = material;
72
73 _markDependenciesDirty();
74 _markResourcesDirty();
75 _markCoreDirty();
76 }
77
78 template<bool Core>
79 void TRenderable<Core>::setMaterials(const Vector<MaterialType>& materials)
80 {
81 UINT32 numMaterials = (UINT32)mMaterials.size();
82 UINT32 min = std::min(numMaterials, (UINT32)materials.size());
83
84 for (UINT32 i = 0; i < min; i++)
85 mMaterials[i] = materials[i];
86
87 for (UINT32 i = min; i < numMaterials; i++)
88 mMaterials[i] = nullptr;
89
90 _markDependenciesDirty();
91 _markResourcesDirty();
92 _markCoreDirty();
93 }
94
95 template<bool Core>
96 void TRenderable<Core>::setMaterial(const MaterialType& material)
97 {
98 setMaterial(0, material);
99 }
100
101 template <bool Core>
102 typename TRenderable<Core>::MaterialType TRenderable<Core>::getMaterial(UINT32 idx) const
103 {
104 if(idx >= (UINT32)mMaterials.size())
105 return nullptr;
106
107 return mMaterials[idx];
108 }
109
110 template<bool Core>
111 void TRenderable<Core>::setLayer(UINT64 layer)
112 {
113 const bool isPow2 = layer && !((layer - 1) & layer);
114
115 if (!isPow2)
116 {
117 LOGWRN("Invalid layer provided. Only one layer bit may be set. Ignoring.");
118 return;
119 }
120
121 mLayer = layer;
122 _markCoreDirty();
123 }
124
125 template<bool Core>
126 void TRenderable<Core>::setOverrideBounds(const AABox& bounds)
127 {
128 mOverrideBounds = bounds;
129
130 if(mUseOverrideBounds)
131 _markCoreDirty();
132 }
133
134 template<bool Core>
135 void TRenderable<Core>::setUseOverrideBounds(bool enable)
136 {
137 if (mUseOverrideBounds == enable)
138 return;
139
140 mUseOverrideBounds = enable;
141 _markCoreDirty();
142 }
143
144 template<bool Core>
145 void TRenderable<Core>::setCullDistanceFactor(float factor)
146 {
147 mCullDistanceFactor = factor;
148
149 _markCoreDirty();
150 }
151
152 template class TRenderable < false >;
153 template class TRenderable < true >;
154
155 void Renderable::initialize()
156 {
157 CoreObject::initialize();
158
159 // Since we don't pass any information along to the core thread object on its construction, make sure the data
160 // sync executes
161 _markCoreDirty();
162
163 // If any resources were deserialized before initialization, make sure the listener is notified
164 _markResourcesDirty();
165 }
166
167
168 void Renderable::setAnimation(const SPtr<Animation>& animation)
169 {
170 mAnimation = animation;
171 refreshAnimation();
172
173 _markCoreDirty();
174 }
175
176 Bounds Renderable::getBounds() const
177 {
178 if(mUseOverrideBounds)
179 {
180 Sphere sphere(mOverrideBounds.getCenter(), mOverrideBounds.getRadius());
181
182 Bounds bounds(mOverrideBounds, sphere);
183 bounds.transformAffine(mTfrmMatrix);
184
185 return bounds;
186 }
187
188 HMesh mesh = getMesh();
189
190 if (!mesh.isLoaded())
191 {
192 const Transform& tfrm = getTransform();
193
194 AABox box(tfrm.getPosition(), tfrm.getPosition());
195 Sphere sphere(tfrm.getPosition(), 0.0f);
196
197 return Bounds(box, sphere);
198 }
199 else
200 {
201 Bounds bounds = mesh->getProperties().getBounds();
202 bounds.transformAffine(mTfrmMatrix);
203
204 return bounds;
205 }
206 }
207
208 SPtr<ct::Renderable> Renderable::getCore() const
209 {
210 return std::static_pointer_cast<ct::Renderable>(mCoreSpecific);
211 }
212
213 SPtr<ct::CoreObject> Renderable::createCore() const
214 {
215 ct::Renderable* handler = new (bs_alloc<ct::Renderable>()) ct::Renderable();
216 SPtr<ct::Renderable> handlerPtr = bs_shared_ptr<ct::Renderable>(handler);
217 handlerPtr->_setThisPtr(handlerPtr);
218
219 return handlerPtr;
220 }
221
222 void Renderable::onMeshChanged()
223 {
224 refreshAnimation();
225 }
226
227 void Renderable::refreshAnimation()
228 {
229 if (mAnimation == nullptr)
230 {
231 mAnimType = RenderableAnimType::None;
232 return;
233 }
234
235 if (mMesh.isLoaded(false))
236 {
237 SPtr<Skeleton> skeleton = mMesh->getSkeleton();
238 SPtr<MorphShapes> morphShapes = mMesh->getMorphShapes();
239
240 if (skeleton != nullptr && morphShapes != nullptr)
241 mAnimType = RenderableAnimType::SkinnedMorph;
242 else if (skeleton != nullptr)
243 mAnimType = RenderableAnimType::Skinned;
244 else if (morphShapes != nullptr)
245 mAnimType = RenderableAnimType::Morph;
246 else
247 mAnimType = RenderableAnimType::None;
248
249 mAnimation->setSkeleton(mMesh->getSkeleton());
250 mAnimation->setMorphShapes(mMesh->getMorphShapes());
251 }
252 else
253 {
254 mAnimType = RenderableAnimType::None;
255
256 mAnimation->setSkeleton(nullptr);
257 mAnimation->setMorphShapes(nullptr);
258 }
259 }
260
261 void Renderable::_updateState(const SceneObject& so, bool force)
262 {
263 UINT32 curHash = so.getTransformHash();
264 if (curHash != mHash || force)
265 {
266 // If skinned animation, don't include own transform since that will be handled by root bone animation
267 bool ignoreOwnTransform;
268 if (mAnimType == RenderableAnimType::Skinned || mAnimType == RenderableAnimType::SkinnedMorph)
269 ignoreOwnTransform = mAnimation->_getAnimatesRoot();
270 else
271 ignoreOwnTransform = false;
272
273 if (ignoreOwnTransform)
274 {
275 // Note: Technically we're checking child's hash but using parent's transform. Ideally we check the parent's
276 // hash to reduce the number of required updates.
277 HSceneObject parentSO = so.getParent();
278 if (parentSO != nullptr)
279 setTransform(parentSO->getTransform());
280 else
281 setTransform(Transform());
282 }
283 else
284 setTransform(so.getTransform());
285
286 mHash = curHash;
287 }
288
289 // Hash now matches so transform won't be applied twice, so we can just call base class version
290 SceneActor::_updateState(so, force);
291 }
292
293 void Renderable::_markCoreDirty(ActorDirtyFlag flag)
294 {
295 markCoreDirty((UINT32)flag);
296 }
297
298 void Renderable::_markDependenciesDirty()
299 {
300 markDependenciesDirty();
301 }
302
303 void Renderable::_markResourcesDirty()
304 {
305 markListenerResourcesDirty();
306 }
307
308 CoreSyncData Renderable::syncToCore(FrameAlloc* allocator)
309 {
310 const UINT32 dirtyFlags = getCoreDirtyFlags();
311 UINT32 size = rttiGetElemSize(dirtyFlags);
312 SceneActor::rttiEnumFields(RttiCoreSyncSize(size), (ActorDirtyFlags)dirtyFlags);
313
314 // The most common case if only the transform changed, so we sync only transform related options
315 UINT32 numMaterials = 0;
316 UINT64 animationId = 0;
317 if(dirtyFlags != (UINT32)ActorDirtyFlag::Transform)
318 {
319 numMaterials = (UINT32)mMaterials.size();
320
321 if (mAnimation != nullptr)
322 animationId = mAnimation->_getId();
323 else
324 animationId = (UINT64)-1;
325
326 size +=
327 rttiGetElemSize(mLayer) +
328 rttiGetElemSize(mOverrideBounds) +
329 rttiGetElemSize(mUseOverrideBounds) +
330 rttiGetElemSize(numMaterials) +
331 rttiGetElemSize(animationId) +
332 rttiGetElemSize(mAnimType) +
333 rttiGetElemSize(mCullDistanceFactor) +
334 sizeof(SPtr<ct::Mesh>) +
335 numMaterials * sizeof(SPtr<ct::Material>);
336 }
337
338
339 UINT8* data = allocator->alloc(size);
340 char* dataPtr = (char*)data;
341
342 dataPtr = rttiWriteElem(dirtyFlags, dataPtr);
343 SceneActor::rttiEnumFields(RttiCoreSyncWriter(&dataPtr), (ActorDirtyFlags)dirtyFlags);
344
345 if(dirtyFlags != (UINT32)ActorDirtyFlag::Transform)
346 {
347 dataPtr = rttiWriteElem(mLayer, dataPtr);
348 dataPtr = rttiWriteElem(mOverrideBounds, dataPtr);
349 dataPtr = rttiWriteElem(mUseOverrideBounds, dataPtr);
350 dataPtr = rttiWriteElem(numMaterials, dataPtr);
351 dataPtr = rttiWriteElem(animationId, dataPtr);
352 dataPtr = rttiWriteElem(mAnimType, dataPtr);
353 dataPtr = rttiWriteElem(mCullDistanceFactor, dataPtr);
354
355 SPtr<ct::Mesh>* mesh = new (dataPtr) SPtr<ct::Mesh>();
356 if (mMesh.isLoaded())
357 *mesh = mMesh->getCore();
358
359 dataPtr += sizeof(SPtr<ct::Mesh>);
360
361 for (UINT32 i = 0; i < numMaterials; i++)
362 {
363 SPtr<ct::Material>* material = new (dataPtr)SPtr<ct::Material>();
364 if (mMaterials[i].isLoaded())
365 *material = mMaterials[i]->getCore();
366
367 dataPtr += sizeof(SPtr<ct::Material>);
368 }
369 }
370
371 return CoreSyncData(data, size);
372 }
373
374 void Renderable::getCoreDependencies(Vector<CoreObject*>& dependencies)
375 {
376 if (mMesh.isLoaded())
377 dependencies.push_back(mMesh.get());
378
379 for (auto& material : mMaterials)
380 {
381 if (material.isLoaded())
382 dependencies.push_back(material.get());
383 }
384 }
385
386 void Renderable::onDependencyDirty(CoreObject* dependency, UINT32 dirtyFlags)
387 {
388 if(mMesh.isLoaded(false) && mMesh.get() == dependency)
389 {
390 CoreObject::onDependencyDirty(dependency, dirtyFlags);
391 return;
392 }
393
394 if(((UINT32)MaterialDirtyFlags::Shader & dirtyFlags) != 0)
395 CoreObject::onDependencyDirty(dependency, dirtyFlags);
396 }
397
398 void Renderable::getListenerResources(Vector<HResource>& resources)
399 {
400 if (mMesh != nullptr)
401 resources.push_back(mMesh);
402
403 for (auto& material : mMaterials)
404 {
405 if (material != nullptr)
406 resources.push_back(material);
407 }
408 }
409
410 void Renderable::notifyResourceLoaded(const HResource& resource)
411 {
412 if (resource == mMesh)
413 onMeshChanged();
414
415 markDependenciesDirty();
416 markCoreDirty();
417 }
418
419 void Renderable::notifyResourceChanged(const HResource& resource)
420 {
421 if(resource == mMesh)
422 onMeshChanged();
423
424 markDependenciesDirty();
425 markCoreDirty();
426 }
427
428 SPtr<Renderable> Renderable::create()
429 {
430 SPtr<Renderable> handlerPtr = createEmpty();
431 handlerPtr->initialize();
432
433 return handlerPtr;
434 }
435
436 SPtr<Renderable> Renderable::createEmpty()
437 {
438 Renderable* handler = new (bs_alloc<Renderable>()) Renderable();
439 SPtr<Renderable> handlerPtr = bs_core_ptr<Renderable>(handler);
440 handlerPtr->_setThisPtr(handlerPtr);
441
442 return handlerPtr;
443 }
444
445 RTTITypeBase* Renderable::getRTTIStatic()
446 {
447 return RenderableRTTI::instance();
448 }
449
450 RTTITypeBase* Renderable::getRTTI() const
451 {
452 return Renderable::getRTTIStatic();
453 }
454
455 namespace ct
456 {
457 Renderable::Renderable()
458 :mRendererId(0), mAnimationId((UINT64)-1), mMorphShapeVersion(0)
459 {
460 }
461
462 Renderable::~Renderable()
463 {
464 if (mActive)
465 gRenderer()->notifyRenderableRemoved(this);
466 }
467
468 void Renderable::initialize()
469 {
470 gRenderer()->notifyRenderableAdded(this);
471
472 CoreObject::initialize();
473 }
474
475 Bounds Renderable::getBounds() const
476 {
477 if (mUseOverrideBounds)
478 {
479 Sphere sphere(mOverrideBounds.getCenter(), mOverrideBounds.getRadius());
480
481 Bounds bounds(mOverrideBounds, sphere);
482 bounds.transformAffine(mTfrmMatrix);
483
484 return bounds;
485 }
486
487 SPtr<Mesh> mesh = getMesh();
488
489 if (mesh == nullptr)
490 {
491 const Transform& tfrm = getTransform();
492
493 AABox box(tfrm.getPosition(), tfrm.getPosition());
494 Sphere sphere(tfrm.getPosition(), 0.0f);
495
496 return Bounds(box, sphere);
497 }
498 else
499 {
500 Bounds bounds = mesh->getProperties().getBounds();
501 bounds.transformAffine(mTfrmMatrix);
502
503 return bounds;
504 }
505 }
506
507 void Renderable::createAnimationBuffers()
508 {
509 if (mAnimType == RenderableAnimType::Skinned || mAnimType == RenderableAnimType::SkinnedMorph)
510 {
511 SPtr<Skeleton> skeleton = mMesh->getSkeleton();
512 UINT32 numBones = skeleton != nullptr ? skeleton->getNumBones() : 0;
513
514 if (numBones > 0)
515 {
516 GPU_BUFFER_DESC desc;
517 desc.elementCount = numBones * 3;
518 desc.elementSize = 0;
519 desc.type = GBT_STANDARD;
520 desc.format = BF_32X4F;
521 desc.usage = GBU_DYNAMIC;
522
523 SPtr<GpuBuffer> buffer = GpuBuffer::create(desc);
524 UINT8* dest = (UINT8*)buffer->lock(0, numBones * 3 * sizeof(Vector4), GBL_WRITE_ONLY_DISCARD);
525
526 // Initialize bone transforms to identity, so the object renders properly even if no animation is animating it
527 for (UINT32 i = 0; i < numBones; i++)
528 {
529 memcpy(dest, &Matrix4::IDENTITY, 12 * sizeof(float)); // Assuming row-major format
530
531 dest += 12 * sizeof(float);
532 }
533
534 buffer->unlock();
535
536 mBoneMatrixBuffer = buffer;
537 }
538 else
539 mBoneMatrixBuffer = nullptr;
540 }
541 else
542 mBoneMatrixBuffer = nullptr;
543
544 if (mAnimType == RenderableAnimType::Morph || mAnimType == RenderableAnimType::SkinnedMorph)
545 {
546 SPtr<MorphShapes> morphShapes = mMesh->getMorphShapes();
547
548 UINT32 vertexSize = sizeof(Vector3) + sizeof(UINT32);
549 UINT32 numVertices = morphShapes->getNumVertices();
550
551 VERTEX_BUFFER_DESC desc;
552 desc.vertexSize = vertexSize;
553 desc.numVerts = numVertices;
554 desc.usage = GBU_DYNAMIC;
555
556 SPtr<VertexBuffer> vertexBuffer = VertexBuffer::create(desc);
557
558 UINT32 totalSize = vertexSize * numVertices;
559 UINT8* dest = (UINT8*)vertexBuffer->lock(0, totalSize, GBL_WRITE_ONLY_DISCARD);
560 memset(dest, 0, totalSize);
561 vertexBuffer->unlock();
562
563 mMorphShapeBuffer = vertexBuffer;
564 }
565 else
566 mMorphShapeBuffer = nullptr;
567
568 mMorphShapeVersion = 0;
569 }
570
571 void Renderable::updateAnimationBuffers(const EvaluatedAnimationData& animData)
572 {
573 if (mAnimationId == (UINT64)-1)
574 return;
575
576 const EvaluatedAnimationData::AnimInfo* animInfo = nullptr;
577
578 auto iterFind = animData.infos.find(mAnimationId);
579 if (iterFind != animData.infos.end())
580 animInfo = &iterFind->second;
581
582 if (animInfo == nullptr)
583 return;
584
585 if (mAnimType == RenderableAnimType::Skinned || mAnimType == RenderableAnimType::SkinnedMorph)
586 {
587 const EvaluatedAnimationData::PoseInfo& poseInfo = animInfo->poseInfo;
588
589 // Note: If multiple elements are using the same animation (not possible atm), this buffer should be shared by
590 // all such elements
591 UINT8* dest = (UINT8*)mBoneMatrixBuffer->lock(0, poseInfo.numBones * 3 * sizeof(Vector4), GBL_WRITE_ONLY_DISCARD);
592 for (UINT32 j = 0; j < poseInfo.numBones; j++)
593 {
594 const Matrix4& transform = animData.transforms[poseInfo.startIdx + j];
595 memcpy(dest, &transform, 12 * sizeof(float)); // Assuming row-major format
596
597 dest += 12 * sizeof(float);
598 }
599
600 mBoneMatrixBuffer->unlock();
601 }
602
603 if (mAnimType == RenderableAnimType::Morph || mAnimType == RenderableAnimType::SkinnedMorph)
604 {
605 if (mMorphShapeVersion != animInfo->morphShapeInfo.version)
606 {
607 SPtr<MeshData> meshData = animInfo->morphShapeInfo.meshData;
608
609 UINT32 bufferSize = meshData->getSize();
610 UINT8* data = meshData->getData();
611
612 mMorphShapeBuffer->writeData(0, bufferSize, data, BWT_DISCARD);
613 mMorphShapeVersion = animInfo->morphShapeInfo.version;
614 }
615 }
616 }
617
618 void Renderable::syncToCore(const CoreSyncData& data)
619 {
620 char* dataPtr = (char*)data.getBuffer();
621
622 mMaterials.clear();
623
624 UINT32 numMaterials = 0;
625 UINT32 dirtyFlags = 0;
626 bool oldIsActive = mActive;
627
628 dataPtr = rttiReadElem(dirtyFlags, dataPtr);
629 SceneActor::rttiEnumFields(RttiCoreSyncReader(&dataPtr), (ActorDirtyFlags)dirtyFlags);
630
631 mTfrmMatrix = mTransform.getMatrix();
632 mTfrmMatrixNoScale = Matrix4::TRS(mTransform.getPosition(), mTransform.getRotation(), Vector3::ONE);
633
634 if(dirtyFlags != (UINT32)ActorDirtyFlag::Transform)
635 {
636 dataPtr = rttiReadElem(mLayer, dataPtr);
637 dataPtr = rttiReadElem(mOverrideBounds, dataPtr);
638 dataPtr = rttiReadElem(mUseOverrideBounds, dataPtr);
639 dataPtr = rttiReadElem(numMaterials, dataPtr);
640 dataPtr = rttiReadElem(mAnimationId, dataPtr);
641 dataPtr = rttiReadElem(mAnimType, dataPtr);
642 dataPtr = rttiReadElem(mCullDistanceFactor, dataPtr);
643
644 SPtr<Mesh>* mesh = (SPtr<Mesh>*)dataPtr;
645 mMesh = *mesh;
646 mesh->~SPtr<Mesh>();
647 dataPtr += sizeof(SPtr<Mesh>);
648
649 for (UINT32 i = 0; i < numMaterials; i++)
650 {
651 SPtr<Material>* material = (SPtr<Material>*)dataPtr;
652 mMaterials.push_back(*material);
653 material->~SPtr<Material>();
654 dataPtr += sizeof(SPtr<Material>);
655 }
656 }
657
658 UINT32 updateEverythingFlag = (UINT32)ActorDirtyFlag::Everything
659 | (UINT32)ActorDirtyFlag::Active
660 | (UINT32)ActorDirtyFlag::Dependency;
661
662 if((dirtyFlags & updateEverythingFlag) != 0)
663 {
664 createAnimationBuffers();
665
666 // Create special vertex declaration if using morph shapes
667 if (mAnimType == RenderableAnimType::Morph || mAnimType == RenderableAnimType::SkinnedMorph)
668 {
669 SPtr<VertexDataDesc> vertexDesc = VertexDataDesc::create();
670 *vertexDesc = * mMesh->getVertexDesc();
671
672 vertexDesc->addVertElem(VET_FLOAT3, VES_POSITION, 1, 1);
673 vertexDesc->addVertElem(VET_UBYTE4_NORM, VES_NORMAL, 1, 1);
674
675 mMorphVertexDeclaration = VertexDeclaration::create(vertexDesc);
676 }
677 else
678 mMorphVertexDeclaration = nullptr;
679
680 if (oldIsActive != mActive)
681 {
682 if (mActive)
683 gRenderer()->notifyRenderableAdded(this);
684 else
685 gRenderer()->notifyRenderableRemoved(this);
686 }
687 else
688 {
689 gRenderer()->notifyRenderableRemoved(this);
690 gRenderer()->notifyRenderableAdded(this);
691 }
692 }
693 else if((dirtyFlags & (UINT32)ActorDirtyFlag::Mobility) != 0)
694 {
695 gRenderer()->notifyRenderableRemoved(this);
696 gRenderer()->notifyRenderableAdded(this);
697 }
698 else if ((dirtyFlags & (UINT32)ActorDirtyFlag::Transform) != 0)
699 {
700 if (mActive)
701 gRenderer()->notifyRenderableUpdated(this);
702 }
703 }
704 }
705}
706