1#include "AssetSystem.h"
4#include "EntityStore.h"
6#include "Resources/AssetManager.h"
7#include "Resources/ModelManager.h"
8#include "Resources/MaterialManager.h"
10#include "Serialization/SceneReader.h"
11#include "Serialization/EntityCreator.h"
13#include "Systems/Core/TransformSystem.h"
14#include "Systems/Core/CameraSystem.h"
16#include "Resources/ResourceStore.h"
17#include "Resources/ModelManager.h"
18#include "Serialization/ModelLoader.h"
20#include "Systems/Core/ClipShapeSystem.h"
21#include "Systems/Core/RenderSystem.h"
22#include "Systems/Core/MeshSystem.h"
23#include "Systems/Core/SceneSystem.h"
24#include "Systems/Core/DynamicComponentSystem.h"
26#include "Resources/MaterialInstance.h"
28#include "Platform/Instrumentation.h"
30#include "Services/Variables.h"
31#include "Services/QualityService.h"
33#include "Utilities/Parallel.h"
35#include "AssetInstanceData.h"
37#include "Foundation/Collections/Pool.h"
38#include "Foundation/Logging/Logger.h"
39#include "Foundation/Platform/IO.h"
40#include "Foundation/Platform/Timer.h"
41#include "Foundation/Memory/MemTypeAllocator.h"
50 constexpr Cogs::StringView lodFreezeVariableName =
"resources.assets.lodFreeze";
52 constexpr Cogs::StringView maxModelsInFlightVariableName =
"resources.assets.maxModelsInFlight";
53 constexpr Cogs::StringView maxModelLoadsPerFrameVariableName =
"resources.assets.maxModelLoadsPerFrame";
54 constexpr Cogs::StringView maxDistanceVariableName =
"resources.assets.maxDistance";
55 constexpr Cogs::StringView maxLodDepthVariableName =
"resources.assets.maxLodDepth";
56 constexpr Cogs::StringView allowMaterialOverride =
"resources.assets.allowMaterialOverride";
58 constexpr bool kDefaultLodFreeze =
false;
59 constexpr int kDefaultMaxModelsInFlight = 100;
60 constexpr int kDefaultMaxModelLoadsPerFrame = 100;
61 constexpr float kDefaultMaxDistance = -1.0f;
62 constexpr int kDefaultMaxLodDepth = -1;
63 constexpr bool kDefaultAllowMaterialOverride =
true;
70 constexpr uint32_t NoValue =
static_cast<uint32_t
>(-1);
72 template<
class T>
using AssetInstanceVector = std::vector<T, Cogs::Memory::MemTypeAllocator<T, Cogs::MemBlockType::AssetInstance>>;
73 template<
class T>
using AssetSystemVector = std::vector<T, Cogs::Memory::MemTypeAllocator<T, Cogs::MemBlockType::AssetSystemData>>;
75 bool cullModelsValue =
true;
85 Staging = SceneEntityFlags::Last << 1,
113 uint32_t nextSibling = ::NoValue;
114 uint32_t nextLodSibling = ::NoValue;
119 void setFlag(uint32_t flag,
bool value) { flags = (flags & ~flag) | (uint32_t(-(int32_t)value) & flag); }
121 void setStaging(
bool value) { setFlag(EntityInstanceFlags::Staging, value); }
122 bool isStaging()
const {
return flags & EntityInstanceFlags::Staging; }
124 void setReady(
bool value) { setFlag(EntityInstanceFlags::Ready, value); }
125 bool isReady()
const {
return flags & EntityInstanceFlags::Ready; }
127 void setSpawned(
bool value) { setFlag(EntityInstanceFlags::Spawned, value); }
128 bool isSpawned()
const {
return flags & EntityInstanceFlags::Spawned; }
130 bool isAtDesiredLod()
const {
return lod.current == lod.target; }
131 bool isVisible()
const {
return lod.target != ::NoValue; }
133 bool isLodGroup()
const {
return flags & SceneEntityFlags::LodGroup; }
134 bool isAsset()
const {
return flags & SceneEntityFlags::Asset; }
135 bool isModel()
const {
return flags & SceneEntityFlags::Model; }
136 bool hasChildren()
const {
return flags & SceneEntityFlags::Children; }
137 bool hasError()
const {
return flags & SceneEntityFlags::Error; }
142 AssetInstanceVector<float> errors;
143 AssetInstanceVector<uint32_t> ids;
144 AssetInstanceVector<uint32_t> lods;
146 uint32_t previousTarget = ::NoValue;
147 uint32_t nextLodSibling = ::NoValue;
154 uint32_t byteSize = 0;
155 uint32_t references = 0;
157 bool requested =
false;
160 void setReady() { ready =
true; }
164 assert(references > 0 &&
"Cannot dereference with zero count.");
166 if (--references == 0) {
172 request->canceled =
true;
186 uint32_t index = ::NoValue;
187 uint32_t fileIndex = ::NoValue;
188 uint32_t part = ::NoValue;
191 bool requested =
false;
198 AssetInstanceVector<uint32_t> roots;
199 AssetInstanceVector<EntityInstanceData> entities;
201 AssetInstanceVector<LodInstanceData> lods;
204 AssetInstanceVector<ModelFileData> modelFiles;
205 AssetInstanceVector<ModelInstanceData> models;
209 bool initialized =
false;
211 Geometry::BoundingBox & getLocalBounds(
const EntityInstanceData & e) {
return boxes[e.index]; }
219 spawnedEntities(MemBlockType::AssetInstance),
220 destroyedEntities(MemBlockType::AssetInstance)
233 uint16_t assetGeneration = 0;
235 Entity * container =
nullptr;
238 uint32_t objectId = NoObjectId;
246 std::vector<uint32_t> selectedIdRanges;
248 bool visible =
false;
249 bool onDemand =
false;
250 bool relativePaths =
false;
251 bool overrideMaterial =
false;
252 bool cloneMaterial =
false;
253 bool initialized =
false;
254 bool freezeLod =
false;
255 bool forceTolerance =
false;
257 float tolerance = 1.0f;
258 float priority = 0.0f;
259 float minDistance = 1.f;
261 std::string directoryPath;
266 bool useOverrideMaterial()
const {
return overrideMaterial; }
267 bool useCloneMaterial()
const {
return cloneMaterial; }
268 bool useRelativePaths()
const {
return relativePaths; }
270 void spawned(EntityId entityId)
272 EntityId& dst = spawnedEntities.
grow();
275 ++stats.frame.spawnedEntities;
279 void destroyed(EntityId entityId)
281 EntityId& dst = destroyedEntities.
grow();
284 ++stats.frame.destroyedEntities;
290 systemData = instance->systemData;
291 parentInstance = instance;
294 next = instance->next;
295 if (instance->next) instance->next->prev =
this;
296 instance->next =
this;
302 if (next) next->prev = prev;
303 next = prev =
nullptr;
310 return asset->definition.scene[instance.index];
326 instanceData(MemBlockType::AssetInstance),
327 requestPool(MemBlockType::AssetSystemData)
333 AssetSystemVector<AssetModelRequest *> pendingRequests;
334 AssetSystemVector<AssetModelRequest *> inFlightRequests;
335 AssetSystemVector<AssetModelRequest *> stillInFlight;
339 AssetSystemVector<AssetUpdate> newUpdates;
340 AssetSystemVector<AssetInstanceData*> currentOnDemandInstances;
341 AssetSystemVector<AssetUpdate> readyUpdates;
342 AssetSystemVector<AssetInstanceData*> liveAssetInstances;
345 glm::mat4 worldFromView = glm::mat4(1.f);
346 glm::mat4 viewFromWorld = glm::mat4(1.f);
347 glm::mat4 clipFromWorld = glm::mat4(1.f);
348 glm::mat4 worldFromClip = glm::mat4(1.f);
349 float minDistance = 0.f;
350 float maxDistance = 0.f;
351 uint32_t maxLodDepth = 0;
353 MessageId entityCreatedId = NoMessage;
354 MessageId entityDestroyedId = NoMessage;
361 if (req->assetInstance == assetInstance) req->canceled =
true;
364 if (req->assetInstance == assetInstance) req->canceled =
true;
367 if (req->assetInstance == assetInstance) req->canceled =
true;
373 void AssetInstanceData::destroy()
377 systemData->instanceData.destroy(
this);
380 uint32_t findFirstLodChild(
const AssetDefinition & definition, uint32_t firstChild, int32_t targetLevel);
382 void initializeAssetInstance(AssetInstanceData & assetInstance)
384 auto initTime = assetInstance.stats.frame.startInit();
385 assetInstance.assetGeneration =
static_cast<uint16_t
>(assetInstance.asset->getGeneration());
386 assetInstance.directoryPath = IO::parentPath(assetInstance.asset->getSource());
388 assetInstance.stats = {};
389#if ENABLE_COST_TRACKING
390 assetInstance.stats.max.memory = 512 * 1024 * 1024;
391 assetInstance.stats.max.drawCalls = 100'000;
392 assetInstance.stats.max.primitiveCount = 10'000'000;
394 assetInstance.stats.frameLimits.processingTime = 0.010;
396 const AssetDefinition & definition = assetInstance.asset->definition;
397 SceneInstanceData & scene = assetInstance.scene;
399 scene.roots.reserve(definition.scene.numTopLevelEntities);
400 scene.entities.resize(definition.scene.entities.size());
401 scene.lods.reserve(definition.scene.numLodGroups);
402 scene.boxes.resize(definition.scene.entities.size());
404 std::unordered_map<size_t, uint32_t> modelFileIndexes;
406 for (
size_t i = 0; i < definition.scene.entities.size(); ++i) {
407 const SceneEntityDefinition& entityDef = definition.scene.entities[i];
408 EntityInstanceData & entityInstance = scene[i];
409 PropertyRange properties = definition.scene.getProperties(entityDef);
411 entityInstance.index = uint32_t(i);
412 entityInstance.flags = entityDef.flags;
413 entityInstance.nextSibling = entityDef.nextSibling;
414 entityInstance.nextLodSibling = entityDef.nextLodSibling;
416 if (entityDef.parentIndex == ::NoValue) {
417 scene.roots.emplace_back(uint32_t(i));
421 if (entityDef.isLodGroup()) {
422 entityInstance.lod.index = (uint32_t)scene.lods.size();
423 auto & lodData = scene.lods.emplace_back();
425 entityInstance.lod.current = entityInstance.lod.target = ::NoValue;
427 std::span<const uint32_t> lodIds = properties.getProperty(idsString, std::span<const uint32_t>());
428 if (!lodIds.empty()) {
429 size_t lodCount = lodIds.size();
431 LOG_ERROR(logger,
"Lod id range array should have an even number of items (n=%zu)", lodCount);
435 lodData.ids.reserve(lodCount);
436 for (
size_t k = 0; k + 1 < lodCount; k += 2) {
437 uint32_t a = lodIds[k + 0];
438 uint32_t b = lodIds[k + 1];
440 LOG_ERROR(logger,
"Lod id range contains an invalid range ([%u, %u])", a, b);
444 if (!lodData.ids.empty() && b < lodData.ids.back()) {
445 LOG_ERROR(logger,
"Lod id ranges are not sorted.");
449 lodData.ids.emplace_back(a);
450 lodData.ids.emplace_back(b);
455 std::span<const float> bboxValues = properties.getProperty(bboxString, std::span<const float>());
456 if (bboxValues.size() == 6) {
457 scene.boxes[entityDef.index] = Geometry::BoundingBox(std::span<const float, 6>(bboxValues));
460 LOG_ERROR(logger,
"Lod level item bounding box has wrong number of elements (%zu != 6)", bboxValues.size());
461 scene.boxes[entityDef.index] = Cogs::Geometry::BoundingBox();
464 std::span<const float> errorValues = properties.getProperty(errorsString, std::span<const float>());
465 if (errorValues.size() != entityDef.lod.numLods) {
466 LOG_WARNING(logger,
"Lod level item count (=%u) does not match number of error values (=%zu)", entityDef.lod.numLods, errorValues.size());
469 if (errorValues.size()) {
470 lodData.errors.assign(errorValues.begin(), errorValues.end());
472 while (lodData.errors.size() < entityDef.lod.numLods) lodData.errors.push_back(lodData.errors.back() * lodData.errors.back());
477 lodData.errors.resize(entityDef.lod.numLods);
481 if (!isEmpty(scene.boxes[entityDef.index])) scale = glm::distance(scene.boxes[entityDef.index].min, scene.boxes[entityDef.index].max);
483 for (
size_t l = 0; l < lodData.errors.size(); ++l) {
484 lodData.errors[l] = scale * scale * float(l) * float(l);
488 lodData.lods.resize(entityDef.lod.numLods);
489 for (uint32_t j = 0; j < entityDef.lod.numLods; ++j) {
490 lodData.lods[j] = findFirstLodChild(definition, entityDef.firstChild, j);
495 else if (entityDef.isModel()) {
496 uint32_t modelIndex = uint32_t(scene.models.size());
497 ModelInstanceData & modelData = scene.models.emplace_back();
498 modelData.index = entityInstance.index;
499 modelData.cost = { 0, 0, 0 };
500 modelData.part = entityDef.model.part;
501 modelData.depth = entityDef.model.depth;
502 entityInstance.model.index = modelIndex;
504 if (entityInstance.hasError())
continue;
507 Cogs::Geometry::BoundingBox bbox;
508 if (entityDef.numProperties) {
509 std::span<const float> bboxValues = properties.getProperty(bboxString, std::span<const float>());
510 if (bboxValues.size() == 6) {
511 bbox = Geometry::BoundingBox(std::span<const float, 6>(bboxValues));
514 scene.boxes[entityDef.index] = bbox;
518 const PropertyInfo& pathItem = definition.scene.properties.getPropertyByIndex(entityDef.model.index);
519 if (pathItem.type == PropertyType::UnsignedInteger) {
520 path = createAssetResourcePathFromIndex(pathItem.uintValue, AssetResourceType::Model);
522 else if (pathItem.type == PropertyType::String) {
523 path = definition.scene.properties.getString(entityDef.model.index).to_string();
527 if (
auto foundIt = modelFileIndexes.find(code); foundIt != modelFileIndexes.end()) {
528 modelData.fileIndex = foundIt->second;
532 uint32_t fileIndex = (uint32_t)scene.modelFiles.size();
533 ModelFileData & modelFile = scene.modelFiles.emplace_back();
535 if (assetInstance.useRelativePaths() && IO::isRelative(path)) {
536 modelFile.path = IO::combine(assetInstance.directoryPath, std::move(path));
538 modelFile.path = std::move(path);
541 int byteSize = properties.getProperty(byteSizeString, 0);
543 modelFile.byteSize = byteSize;
545 modelFile.byteSize = 1000;
548 modelData.fileIndex = fileIndex;
549 modelFileIndexes[code] = fileIndex;
554 scene.initialized =
true;
557 glm::vec3 euclidean(
const glm::vec4& a)
559 return (1.f / a.w)*glm::vec3(a);
564 glm::vec3 frustumEdgesP[4];
565 glm::vec3 frustumEdgesD[4];
567 glm::vec3 lodRef = glm::vec3(0, 0, 0);
568 glm::vec4 planes[12] = { glm::vec4(0,0,0,1), glm::vec4(0,0,0,1),glm::vec4(0,0,0,1),glm::vec4(0,0,0,1),glm::vec4(0,0,0,1) };
569 uint32_t planeCount = 6;
570 bool invertedClip =
false;
572 float nearDistance = 1.f;
573 float maxDistance = FLT_MAX;
574 uint32_t maxLodDepth = ~0u;
575 uint32_t frustumCullIn = 0;
576 uint32_t frustumCullOut = 0;
577 uint32_t bboxCullOut = 0;
585 if (!trCompRef)
return;
590 const glm::mat4& worldFromModel = context->transformSystem->getLocalToWorld(ref->
getComponent<TransformComponent>());
591 const glm::mat4 modelFromWorld = glm::inverse(worldFromModel);
592 const glm::mat4 modelFromView = modelFromWorld * systemData->worldFromView;
593 const glm::mat4 rawViewProjectionTransposed = glm::transpose(systemData->clipFromWorld * worldFromModel);
595 lodRef.lodRef = euclidean(modelFromView * glm::vec4(0, 0, 0, 1));
596 lodRef.nearDistance = std::max(assetInstance.minDistance, systemData->minDistance);
597 lodRef.planes[0] = rawViewProjectionTransposed[3] + rawViewProjectionTransposed[0];
598 lodRef.planes[1] = rawViewProjectionTransposed[3] - rawViewProjectionTransposed[0];
599 lodRef.planes[2] = rawViewProjectionTransposed[3] + rawViewProjectionTransposed[1];
600 lodRef.planes[3] = rawViewProjectionTransposed[3] - rawViewProjectionTransposed[1];
602 if (0 < systemData->maxDistance) {
603 lodRef.planes[4] = glm::transpose(systemData->viewFromWorld * worldFromModel) * glm::vec4(0, 0, 1, systemData->maxDistance);
604 lodRef.maxDistance = systemData->maxDistance;
607 lodRef.planes[4] = glm::vec4(0, 0, 0, 1);
611 lodRef.planes[5] = glm::vec4(normalize(euclidean(modelFromView * glm::vec4(0, 0, -1, 1)) - lodRef.lodRef), 0.f);
612 lodRef.planes[5].w = -dot(glm::vec3(lodRef.planes[5]), lodRef.lodRef);
614 lodRef.planeCount = 6;
615 if (
const ClipShapeComponent* clipComp = assetInstance.clipShapeComponent.
resolveComponent<ClipShapeComponent>(); clipComp) {
616 const ClipShapeData& clipData = context->clipShapeSystem->getData(clipComp);
617 switch (clipData.shape) {
622 assert(clipData.planeCount == 6);
623 for (
size_t i = 0; i < clipData.planeCount; i++) {
624 lodRef.planes[lodRef.planeCount++] = clipData.planes[i] * worldFromModel;
629 assert(
false &&
"Unhandled case");
634 lodRef.maxLodDepth = systemData->maxLodDepth;
636 const glm::mat4 M = modelFromWorld * systemData->worldFromClip;
637 for (
size_t i = 0; i < 4; i++) {
638 glm::vec3 a = euclidean(M * glm::vec4((i & 1) ? -1.f : 1.f,
639 (i & 2) ? -1.f : 1.f,
641 glm::vec3 b = euclidean(M * glm::vec4((i & 1) ? -1.f : 1.f,
642 (i & 2) ? -1.f : 1.f,
644 float az = dot(lodRef.planes[5], glm::vec4(a, 1.f));
645 float bz = dot(lodRef.planes[5], glm::vec4(b, 1.f));
646 lodRef.frustumEdgesD[i] = (1.f / (bz - az)) * (b - a);
647 lodRef.frustumEdgesP[i] = a - az * lodRef.frustumEdgesD[i];
651 bool isOutside(AssetLodReference& lodReference,
const Geometry::BoundingBox & bbox)
653 float minDist, maxDist;
654 bool outsideOne =
false;
655 size_t fullyInside = 0;
656 for (
size_t j = 0; j < 6; j++) {
659 for (
size_t i = 0; i < 8; i++) {
660 glm::vec4 p((i & 1) ? bbox.min.x : bbox.max.x,
661 (i & 2) ? bbox.min.y : bbox.max.y,
662 (i & 4) ? bbox.min.z : bbox.max.z,
664 float t = dot(lodReference.planes[j], p);
665 minDist = std::min(minDist, t);
666 maxDist = std::max(maxDist, t);
677 size_t fullyInsideClip = 0;
678 bool outsideOneClip =
false;
679 for (
size_t j = 6; j < lodReference.planeCount; j++) {
681 float max = -FLT_MAX;
682 for (
size_t i = 0; i < 8; i++) {
683 glm::vec4 p((i & 1) ? bbox.min.x : bbox.max.x,
684 (i & 2) ? bbox.min.y : bbox.max.y,
685 (i & 4) ? bbox.min.z : bbox.max.z,
687 float t = dot(lodReference.planes[j], p);
688 min = std::min(min, t);
689 max = std::max(max, t);
692 outsideOneClip =
true;
699 if (lodReference.invertedClip ==
false) {
700 outsideOne = outsideOne || outsideOneClip;
701 fullyInside += fullyInsideClip;
704 if (6 + fullyInsideClip == lodReference.planeCount) {
712 lodReference.frustumCullOut++;
715 else if (fullyInside == lodReference.planeCount) {
717 lodReference.frustumCullIn++;
723 minDist = std::max(0.f, std::min(lodReference.maxDistance, minDist));
724 maxDist = std::max(0.f, std::min(lodReference.maxDistance, maxDist));
725 uint32_t planes = 63u;
726 for (
size_t i = 0; i < 4; i++) {
727 glm::vec3 pa = lodReference.frustumEdgesP[i] + minDist * lodReference.frustumEdgesD[i];
728 planes = planes & ((pa.x < bbox.min.x ? 1 : 0) |
729 (pa.y < bbox.min.y ? 2 : 0) |
730 (pa.z < bbox.min.z ? 4 : 0) |
731 (bbox.max.x < pa.x ? 8 : 0) |
732 (bbox.max.y < pa.y ? 16 : 0) |
733 (bbox.max.z < pa.z ? 32 : 0));
734 glm::vec3 pb = lodReference.frustumEdgesP[i] + maxDist * lodReference.frustumEdgesD[i];
735 planes = planes & ((pb.x < bbox.min.x ? 1 : 0) |
736 (pb.y < bbox.min.y ? 2 : 0) |
737 (pb.z < bbox.min.z ? 4 : 0) |
738 (bbox.max.x < pb.x ? 8 : 0) |
739 (bbox.max.y < pb.y ? 16 : 0) |
740 (bbox.max.z < pb.z ? 32 : 0));
743 lodReference.bboxCullOut++;
749 lodReference.frustumCullIn++;
753 void updateLodGroupDesiredLevel(AssetInstanceData & instance, AssetLodReference & lodReference, EntityInstanceData & lodInstance, uint32_t lodDepth,
const float tolerance)
755 auto & localBounds = instance.scene.getLocalBounds(lodInstance);
756 auto & lodData = instance.scene.getLodData(lodInstance);
757 auto & errors = lodData.errors;
760 lodData.previousTarget = lodInstance.lod.target;
762 if (!instance.visible) {
763 lodInstance.lod.target = ::NoValue;
767 if (!instance.selectedIdRanges.empty() && !lodData.ids.empty()) {
772 const std::vector<uint32_t>& selectedIds = instance.selectedIdRanges;
773 const size_t selectedCount = selectedIds.size() / 2;
775 const AssetInstanceVector<uint32_t>& lodIds = lodData.ids;
776 const size_t lodCount = lodIds.size() / 2;
782 bool overlap =
false;
783 for (
size_t j = 0; !overlap && j < selectedCount; j++) {
784 const uint32_t selectedMin = selectedIds[2 * j + 0];
785 const uint32_t selectedMax = selectedIds[2 * j + 1];
787 for (
size_t i = 0; !overlap && i < lodCount; i++) {
788 const uint32_t lodMin = lodIds[2 * i + 0];
789 const uint32_t lodMax = lodIds[2 * i + 1];
792 if (lodMin <= selectedMax && selectedMin <= lodMax) {
798 lodInstance.lod.target = ::NoValue;
803 const bool outside = isOutside(lodReference, localBounds);
805 lodInstance.lod.target = ::NoValue;
807 const uint32_t maxLodDepth = lodReference.maxLodDepth;
808 const size_t maxLocalLodDepth = maxLodDepth - std::min(maxLodDepth, lodDepth);
811 glm::vec3 nearest = glm::max(localBounds.min, glm::min(localBounds.max, lodReference.lodRef));
812 auto d = glm::max(lodReference.nearDistance, glm::distance(nearest, lodReference.lodRef));
814 auto error = d * tolerance;
816 const size_t N = errors.size();
817 const size_t M = N - 1;
818 const size_t O = M - std::min(M, maxLocalLodDepth);
821 lodInstance.lod.target = ::NoValue;
823 else if (lodInstance.lod.current == ::NoValue) {
825 lodInstance.lod.target =
static_cast<uint32_t
>(M);
830 for (; i < M && errors[i + 1] < error; i++) {}
831 lodInstance.lod.target =
static_cast<uint32_t
>(i);
837 while (i0 + 1 < i1) {
838 size_t m = (i0 + i1) / 2;
841 bool less = error < errors[m];
846 bool less = error < errors[i1];
849 lodInstance.lod.target =
static_cast<uint32_t
>(i0);
854 uint32_t findFirstLodChild(
const AssetDefinition & definition, uint32_t firstChild, int32_t targetLevel)
856 uint32_t childIndex = definition.scene.getChild(firstChild, 0);
858 for (int32_t i = 0; i < targetLevel; ++i) {
860 auto ccDef = &definition.scene[childIndex];
862 while (ccDef->nextLodSibling != ::NoValue) {
863 ccDef = &definition.scene[ccDef->nextLodSibling];
866 childIndex = ccDef->nextSibling;
872 void calculateModelCost(Context * , ModelInstanceData & modelData, Model * model)
874 modelData.cost = { 0, 0, 0 };
877 auto parts = modelData.part == ::NoValue ? std::span(model->parts) :
std::span(&model->parts[modelData.part], &model->parts[modelData.part] + 1);
879 for (
auto & part : parts) {
881 if (part.vertexCount == ::NoValue && part.meshIndex != ::NoValue) {
882 auto & mesh = model->meshes[part.meshIndex];
883 modelData.cost.primitiveCount += mesh->getCount() / 3;
885 modelData.cost.primitiveCount += part.vertexCount / 3;
888 modelData.cost.drawCalls += part.meshIndex != ::NoValue ? 1 : 0;
892#if ENABLE_COST_TRACKING
893 void calculateCost(Context* context, AssetInstanceData & instance, EntityInstanceData & entityInstance)
895 if (entityInstance.hasError())
return;
897 if (entityInstance.isLodGroup() && entityInstance.lod.current != ::NoValue) {
898 auto & lodData = instance.scene.getLodData(entityInstance);
899 uint32_t childIndex = lodData.lods[entityInstance.lod.current];
901 while (childIndex != ::NoValue) {
902 calculateCost(context, instance, instance.scene[childIndex]);
904 childIndex = instance.scene[childIndex].nextLodSibling;
906 }
else if (entityInstance.isAsset() && entityInstance.isSpawned()) {
907 auto & nestedInstance = *entityInstance.assetInstance;
908 nestedInstance.stats.current = { 0, 0, 0 };
909 for (
size_t i = 0; i < nestedInstance.scene.roots.size(); ++i) {
910 calculateCost(context, nestedInstance, nestedInstance.scene[nestedInstance.scene.roots[i]]);
912 instance.stats.current += nestedInstance.stats.current;
913 }
else if (entityInstance.isModel() && entityInstance.isSpawned()) {
914 auto & modelData = instance.scene.getModelData(entityInstance);
915 instance.stats.current += modelData.cost;
922#if ENABLE_COST_TRACKING
923 void estimateCost(Context* context, AssetInstanceData & instance, EntityInstanceData & entityInstance)
925 if (entityInstance.hasError())
return;
927 if (entityInstance.isLodGroup() && entityInstance.isVisible()) {
928 auto & lodData = instance.scene.getLodData(entityInstance);
929 uint32_t childIndex = lodData.lods[entityInstance.lod.target];
931 while (childIndex != ::NoValue) {
932 estimateCost(context, instance, instance.scene[childIndex]);
934 childIndex = instance.scene[childIndex].nextLodSibling;
936 }
else if (entityInstance.isAsset() && entityInstance.isSpawned()) {
937 auto & nestedInstance = *entityInstance.assetInstance;
938 nestedInstance.stats.projected = { 0, 0, 0 };
939 for (
size_t i = 0; i < nestedInstance.scene.roots.size(); ++i) {
940 estimateCost(context, nestedInstance, nestedInstance.scene[nestedInstance.scene.roots[i]]);
942 instance.stats.projected += nestedInstance.stats.projected;
943 }
else if (entityInstance.isModel()) {
946 instance.stats.projected.drawCalls += 1;
948 const uint64_t bogusEstimate = 10 * 1024;
949 instance.stats.projected.memory += bogusEstimate;
954 void updateAssetDesiredLevels(Context* context, AssetInstanceData& assetInstance, uint32_t lodDepth, uint32_t maxIterations);
956 void updateEntityDesiredLevels(Context* context, AssetInstanceData & instance, AssetLodReference & lodReference, EntityInstanceData & entityInstance, uint32_t lodDepth,
const float tolerance)
958 if (entityInstance.isLodGroup()) {
959 updateLodGroupDesiredLevel(instance, lodReference, entityInstance, lodDepth, tolerance);
961 if (entityInstance.isVisible() && entityInstance.isAtDesiredLod()) {
962 auto & lodData = instance.scene.getLodData(entityInstance);
963 uint32_t childIndex = lodData.lods[entityInstance.lod.target];
966 while (childIndex != ::NoValue) {
968 updateEntityDesiredLevels(context, instance, lodReference, instance.scene[childIndex], lodDepth + 1 + entityInstance.lod.target, tolerance);
970 childIndex = instance.scene[childIndex].nextLodSibling;
973 }
else if (entityInstance.isSpawned()) {
974 auto & definition = instance.asset->definition;
975 auto & entityDefinition = definition.scene[entityInstance.index];
977 if (entityInstance.isAsset()) {
978 entityInstance.assetInstance->tolerance = instance.tolerance;
979 entityInstance.assetInstance->forceTolerance = instance.forceTolerance;
980#if ENABLE_COST_TRACKING
981 entityInstance.assetInstance->stats.toleranceScale = instance.stats.toleranceScale;
983 entityInstance.assetInstance->visible = instance.visible;
985 entityInstance.assetInstance->stats.frustumCullIn = 0;
986 entityInstance.assetInstance->stats.frustumCullOut = 0;
987 entityInstance.assetInstance->stats.bboxCullOut = 0;
988 updateAssetDesiredLevels(context, *entityInstance.assetInstance, lodDepth, 1);
989 instance.stats.frustumCullIn += entityInstance.assetInstance->stats.frustumCullIn;
990 instance.stats.frustumCullOut += entityInstance.assetInstance->stats.frustumCullOut;
991 instance.stats.bboxCullOut += entityInstance.assetInstance->stats.bboxCullOut;
992 }
else if (entityInstance.hasChildren()) {
993 uint32_t childIndex = entityDefinition.firstChild;
995 while (childIndex != ::NoValue) {
996 updateEntityDesiredLevels(context, instance, lodReference, instance.scene[childIndex], lodDepth, tolerance);
998 childIndex = definition.scene[childIndex].nextSibling;
1004#if ENABLE_COST_TRACKING
1005 bool updateToleranceScale(AssetInstanceStats & stats)
1007 const float currentToleranceScale = stats.toleranceScale;
1008 const float frameToleranceMax = glm::min(currentToleranceScale + stats.toleranceScaleIncrement, stats.toleranceScaleMax);
1009 const float frameToleranceMin = glm::max(currentToleranceScale - stats.toleranceScaleIncrement, stats.toleranceScaleMin);
1011 if (stats.projected.memory > stats.max.memory) {
1012 const float newToleranceScale = (0.9f * stats.max.memory) / glm::max((
decltype(stats.current.memory))1, stats.current.memory) * stats.toleranceScale;
1013 stats.toleranceScale = glm::clamp(newToleranceScale, frameToleranceMin, frameToleranceMax);
1015 }
else if (stats.projected.memory < 0.8f * stats.max.memory) {
1016 if (stats.current.memory != 0) {
1017 const float targetToleranceScale = stats.max.memory / glm::max((
decltype(stats.current.memory))1, stats.current.memory) * currentToleranceScale;
1018 stats.toleranceScale = glm::clamp(targetToleranceScale, frameToleranceMin, frameToleranceMax);
1020 stats.toleranceScale = frameToleranceMax;
1030 void updateAssetDesiredLevels(Context* context, AssetInstanceData & assetInstance, uint32_t lodDepth, uint32_t maxIterations)
1032 CpuInstrumentationScope(SCOPE_ASSETSYSTEM,
"AssetSystem::updateAssetDesiredLevels");
1033 auto lodTime = assetInstance.stats.frame.startLodCalculation();
1035 AssetLodReference lodReference = {};
1036 getLodReference(context, assetInstance, lodReference, assetInstance.container);
1038 auto & scene = assetInstance.scene;
1040 float tolerance = assetInstance.tolerance;
1041#if ENABLE_COST_TRACKING
1042 uint64_t prevIteration = assetInstance.stats.projected.memory;
1044#if ENABLE_COST_TRACKING
1045 for (uint32_t it = 0; it < maxIterations; it++) {
1046 float clampedTolerance = glm::max(1e-5f, (1.f / assetInstance.stats.toleranceScale) * tolerance);
1048 if (maxIterations > 0) {
1049 float clampedTolerance = tolerance;
1051 if (assetInstance.forceTolerance) clampedTolerance = assetInstance.tolerance;
1053 for (
auto & root : scene.roots) {
1054 updateEntityDesiredLevels(context, assetInstance, lodReference, scene[root], lodDepth, clampedTolerance);
1057 assetInstance.stats.frustumCullIn += lodReference.frustumCullIn;
1058 assetInstance.stats.frustumCullOut += lodReference.frustumCullOut;
1059 assetInstance.stats.bboxCullOut += lodReference.bboxCullOut;
1061#if ENABLE_COST_TRACKING
1062 if (assetInstance.parentInstance) {
1067 CpuInstrumentationScope(SCOPE_ASSETSYSTEM,
"AssetSystem::estimateCost");
1069 assetInstance.stats.projected = { 0, 0, 0 };
1070 for (
size_t i = 0; i < scene.roots.size(); ++i) {
1071 estimateCost(context, assetInstance, scene[scene.roots[i]]);
1075 if (assetInstance.stats.projected.memory == prevIteration) {
1078 prevIteration = assetInstance.stats.projected.memory;
1081 if (!updateToleranceScale(assetInstance.stats)) {
1088 void clearEntity(Context * context, AssetInstanceData & assetInstance, EntityInstanceData & entityInstance)
1090 if (entityInstance.entity) {
1091 assetInstance.destroyed(entityInstance.entity->getId());
1094 entityInstance.entity = {};
1095 entityInstance.setSpawned(
false);
1096 entityInstance.setReady(
false);
1098 if (entityInstance.isLodGroup()) {
1099 entityInstance.lod.current = ::NoValue;
1102 if (entityInstance.isStaging()) {
1103 entityInstance.setStaging(
false);
1106 if (entityInstance.hasChildren()) {
1107 auto & definition = assetInstance.asset->definition;
1108 auto & entityDefinition = definition.scene[entityInstance.index];
1110 for (uint32_t i = 0; i < entityDefinition.numChildren; ++i) {
1111 uint32_t childIndex = definition.scene.getChild(entityDefinition.firstChild, i);
1113 clearEntity(context, assetInstance, assetInstance.scene[childIndex]);
1115 }
else if (entityInstance.isAsset() && entityInstance.assetInstance) {
1116 for (
auto & instance : entityInstance.assetInstance->scene.entities) {
1117 clearEntity(context, *entityInstance.assetInstance, instance);
1120 entityInstance.assetInstance->destroy();
1121 entityInstance.assetInstance =
nullptr;
1122 }
else if (entityInstance.isModel()) {
1123 auto & modelData = assetInstance.scene.getModelData(entityInstance);
1130 if (modelData.requested) {
1131 auto & modelFile = assetInstance.scene.modelFiles[modelData.fileIndex];
1133 modelData.requested =
false;
1138 bool checkEntity(Context * context, AssetLodReference& lodReference, AssetInstanceData & assetInstance, EntityInstanceData & entityInstance, EntityInstanceData * parentInstance)
1140 if (entityInstance.hasError())
return true;
1142 if (cullModelsValue && entityInstance.isModel()) {
1143 AssetDefinition& assetDef = assetInstance.asset->definition;
1144 SceneEntityDefinition& entityDef = assetDef.scene[entityInstance.index];
1145 const Cogs::Geometry::BoundingBox& bbox = assetInstance.scene.boxes[entityDef.index];
1146 if (!isEmpty(bbox) && isOutside(lodReference, bbox)) {
1147 ModelInstanceData& modelData = assetInstance.scene.getModelData(entityInstance);
1148 if (entityInstance.isSpawned() || entityInstance.isReady() || modelData.requested) {
1149 clearEntity(context, assetInstance, entityInstance);
1155 if (entityInstance.isReady())
return true;
1157 if (entityInstance.isModel()) {
1158 auto& modelData = assetInstance.scene.getModelData(entityInstance);
1159 auto& modelFile = assetInstance.scene.modelFiles[modelData.fileIndex];
1161 constexpr uint32_t kPriorityPerLevel = 1000;
1162 constexpr uint32_t kMaxInstancePriority = 100000;
1164 if (!modelData.requested) {
1165 if (!modelFile.requested) {
1166 AssetModelRequest* request = assetInstance.systemData->requestPool.create();
1167 request->assetInstance = &assetInstance;
1168 request->modelFileIndex = modelData.fileIndex;
1169 request->path = modelFile.path;
1170 request->priority += (uint32_t)((
float)kMaxInstancePriority * assetInstance.priority);
1171 modelFile.request = request;
1172 assetInstance.systemData->pendingRequests.emplace_back(request);
1173 ++assetInstance.stats.frame.modelsRequested;
1174 ++assetInstance.stats.numRequests;
1176 modelFile.requested =
true;
1179 if (modelFile.request) {
1180 modelFile.request->priority += modelData.depth * kPriorityPerLevel;
1185 modelData.requested =
true;
1188 if (modelFile.ready) {
1189 entityInstance.setReady(
true);
1193 if (!modelFile.model)
return false;
1195 if (!modelFile.model->isLoaded())
return false;
1197 for (
auto & mesh : modelFile.model->meshes) {
1198 if (!mesh->isInitialized() && mesh->getCount() == 0)
continue;
1199 if (!mesh->hasAttachedResource())
return false;
1202 if (!assetInstance.useOverrideMaterial() && !assetInstance.useCloneMaterial()) {
1203 for (
auto & material : modelFile.model->materials) {
1204 if (!material->isActive())
return false;
1208 modelFile.setReady();
1210 entityInstance.setReady(
true);
1213 context->engine->setDirty();
1215 }
else if (entityInstance.isAsset()) {
1216 auto & definition = assetInstance.asset->definition;
1217 auto & entityDefinition = definition.scene[entityInstance.index];
1219 if (!entityInstance.assetInstance) {
1220 entityInstance.assetInstance = assetInstance.systemData->instanceData.create();
1221 entityInstance.assetInstance->link(&assetInstance);
1222 entityInstance.assetInstance->container = parentInstance->entityPtr;
1223 entityInstance.assetInstance->objectId = assetInstance.objectId;
1224 entityInstance.assetInstance->renderLayers = assetInstance.renderLayers;
1225 entityInstance.assetInstance->clipShapeComponent = assetInstance.clipShapeComponent;
1228 if (!entityInstance.assetInstance->initialized) {
1231 const PropertyInfo& pathItem = definition.scene.properties.getPropertyByIndex(entityDefinition.asset.index);
1232 if (pathItem.type == PropertyType::UnsignedInteger) {
1233 path = createAssetResourcePathFromIndex(pathItem.uintValue, AssetResourceType::Asset);
1235 else if (pathItem.type == PropertyType::String) {
1236 path = definition.scene.properties.getString(entityDefinition.asset.index).to_string();
1239 if (assetInstance.useRelativePaths() && IO::isRelative(path)) {
1240 path = IO::combine(assetInstance.directoryPath, std::move(path));
1243 entityInstance.assetInstance->asset = context->assetManager->loadAsset(path, NoResourceId,
AssetLoadFlags::None);
1245 entityInstance.assetInstance->initialized =
true;
1247 if (assetInstance.onDemand) {
1248 entityInstance.assetInstance->onDemand =
true;
1251 if (assetInstance.useOverrideMaterial()) {
1252 entityInstance.assetInstance->overrideMaterial =
true;
1253 entityInstance.assetInstance->material = assetInstance.material;
1255 if (assetInstance.useCloneMaterial()) {
1256 entityInstance.assetInstance->cloneMaterial =
true;
1257 entityInstance.assetInstance->material = assetInstance.material;
1261 if (!entityInstance.assetInstance->asset->isLoaded()) {
1265 if (!entityInstance.assetInstance->scene.initialized) {
1266 auto flags = (
AssetFlags)entityDefinition.asset.flags;
1270 initializeAssetInstance(*entityInstance.assetInstance);
1274 for (
const uint32_t rootIx : entityInstance.assetInstance->scene.roots) {
1275 ready &= checkEntity(context, lodReference, *entityInstance.assetInstance, entityInstance.assetInstance->scene[rootIx], parentInstance);
1277 entityInstance.setReady(ready);
1280 context->engine->setDirty();
1283 }
else if (entityInstance.isLodGroup()) {
1284 auto & definition = assetInstance.asset->definition;
1285 auto & lodData = assetInstance.scene.getLodData(entityInstance);
1287 entityInstance.lod.target = uint32_t(lodData.lods.size()) - 1;
1288 uint32_t childIndex = entityInstance.lod.current != ::NoValue ? entityInstance.lod.current : lodData.lods.back();
1291 while (childIndex != ::NoValue) {
1292 retval &= checkEntity(context, lodReference, assetInstance, assetInstance.scene[childIndex], parentInstance);
1293 childIndex = definition.scene[childIndex].nextLodSibling;
1296 }
else if (entityInstance.hasChildren()) {
1297 auto & definition = assetInstance.asset->definition;
1298 auto & entityDefinition = definition.scene[entityInstance.index];
1300 uint32_t childIndex = entityDefinition.firstChild;
1302 while (childIndex != ::NoValue) {
1303 if (!checkEntity(context, lodReference, assetInstance, assetInstance.scene[childIndex], parentInstance)) {
1307 childIndex = definition.scene[childIndex].nextSibling;
1314 bool spawnEntity(Context * context, AssetLodReference& lodReference, AssetInstanceData & assetInstance, EntityInstanceData & entityInstance, EntityInstanceData * parentInstance, EntityParentMode parentMode);
1316 bool spawnLodGroup(Context * context, AssetLodReference& lodReference, AssetInstanceData & assetInstance, EntityInstanceData & entityInstance, EntityInstanceData * parentInstance, EntityParentMode )
1318 assert(entityInstance.isLodGroup() &&
"Invalid LoD group instance.");
1320 if (entityInstance.isAtDesiredLod())
return false;
1322 auto & scene = assetInstance.scene;
1323 auto & lodData = scene.getLodData(entityInstance);
1325 if (lodData.previousTarget != ::NoValue && lodData.previousTarget != entityInstance.lod.target && lodData.previousTarget != entityInstance.lod.current) {
1326 uint32_t currentLodChild = lodData.lods[lodData.previousTarget];
1328 while (currentLodChild != ::NoValue) {
1329 clearEntity(context, assetInstance, scene[currentLodChild]);
1330 currentLodChild = scene[currentLodChild].nextLodSibling;
1333 lodData.previousTarget = ::NoValue;
1336 if (!entityInstance.isVisible()) {
1337 clearEntity(context, assetInstance, entityInstance);
1341 if (entityInstance.lod.target != ::NoValue) {
1342 uint32_t currentLodChild = lodData.lods[entityInstance.lod.target];
1344 while (currentLodChild != ::NoValue) {
1345 scene[currentLodChild].setStaging(
false);
1346 currentLodChild = scene[currentLodChild].nextLodSibling;
1350 const uint32_t firstChildIndex = lodData.lods[entityInstance.lod.target];
1353 uint32_t childIndex = firstChildIndex;
1354 while (childIndex != ::NoValue) {
1355 auto & childInstance = scene[childIndex];
1357 childInstance.setStaging(
true);
1360 ready &= checkEntity(context, lodReference, assetInstance, childInstance, parentInstance);
1362 childIndex = childInstance.nextLodSibling;
1365 if (!ready)
return false;
1367 if (entityInstance.lod.current != ::NoValue) {
1368 uint32_t currentLodChild = lodData.lods[entityInstance.lod.current];
1370 while (currentLodChild != ::NoValue) {
1371 clearEntity(context, assetInstance, scene[currentLodChild]);
1372 currentLodChild = scene[currentLodChild].nextLodSibling;
1376 auto currentParent = parentInstance;
1378 childIndex = firstChildIndex;
1379 while (childIndex != ::NoValue) {
1380 spawnEntity(context, lodReference, assetInstance, scene[childIndex], currentParent, EntityParentMode::TransformOnly);
1382 childIndex = scene[childIndex].nextLodSibling;
1385 entityInstance.lod.current = entityInstance.lod.target;
1390 bool spawnAsset(Context * context, AssetLodReference& lodReference, EntityInstanceData & entityInstance, EntityInstanceData * parentInstance)
1392 if (entityInstance.isSpawned())
return false;
1393 if (!entityInstance.isReady())
return false;
1395 auto & assetInstance = *entityInstance.assetInstance;
1397 EntityInstanceData root;
1398 root.entityPtr = parentInstance->entityPtr;
1400 for (
auto & subroot : assetInstance.scene.roots) {
1401 spawnEntity(context, lodReference, assetInstance, assetInstance.scene[subroot], parentInstance, EntityParentMode::TransformOnly);
1404 entityInstance.setSpawned(
true);
1405 entityInstance.setStaging(
false);
1410 bool spawnModel(Context * context, AssetInstanceData & assetInstance, EntityInstanceData & entityInstance, EntityInstanceData * parentInstance, EntityParentMode parentMode)
1412 if (entityInstance.isSpawned())
return false;
1413 if (!entityInstance.isReady())
return false;
1415 if (entityInstance.hasError()) {
1416 entityInstance.setSpawned(
true);
1420 auto & modelData = assetInstance.scene.getModelData(entityInstance);
1421 assert(modelData.index == entityInstance.index &&
"Model data attached to invalid entity instance.");
1422 auto & modelFile = assetInstance.scene.modelFiles[modelData.fileIndex];
1424 auto model = modelFile.model.resolve();
1426 if (!model->isLoaded())
return false;
1427 if (model->meshes.empty())
return false;
1429 auto & scene = assetInstance.asset->definition.scene;
1430 auto & entityDefinition = scene[entityInstance.index];
1432 EntityCreationContext entityContext{};
1433 entityContext.context = context;
1434 entityContext.scene = &assetInstance.asset->definition.scene;
1435 entityContext.parent = parentInstance->entityPtr;
1436 entityContext.parentMode = parentMode;
1437 entityContext.skipModel =
true;
1439 entityInstance.entity = createEntity(entityContext, entityDefinition);
1441 assetInstance.spawned(entityInstance.entity->getId());
1443 auto parentTransform = entityInstance.entity->getComponentHandle<TransformComponent>();
1444 auto parentSceneComponent = entityInstance.entity->getComponentHandle<SceneComponent>();
1446 size_t begin = modelData.part != ::NoValue ? modelData.part : 0;
1447 size_t end = modelData.part != ::NoValue ? modelData.part + 1 : model->parts.size();
1448 assert(end > begin &&
"Invalid part range");
1449 size_t count = end - begin;
1450 assert(count &&
"Invalid part range.");
1452 std::vector<EntityPtr> entities(count);
1453 context->store->createEntities(count, entities);
1455 std::vector<ComponentModel::ComponentHandle> transformComponents(count);
1456 std::vector<ComponentModel::ComponentHandle> sceneComponents(count);
1458 for (
size_t i = 0; i < count; ++i) {
1459 auto & part = model->parts[begin + i];
1462 auto sceneComponent = context->sceneSystem->createComponent();
1463 sceneComponents[i] = sceneComponent;
1464 entity->addComponent(sceneComponent);
1466 auto tcHandle = context->transformSystem->createComponent();
1467 transformComponents[i] = tcHandle;
1468 entity->addComponent(tcHandle);
1469 auto transformComponent = tcHandle.resolveComponent<TransformComponent>();
1471 context->transformSystem->setLocalTransform(transformComponent, model->getPartTransform(part));
1473 if (part.parentIndex == ::NoValue) {
1474 transformComponent->parent = parentTransform;
1475 parentSceneComponent.resolveComponent<SceneComponent>()->children.emplace_back(entity);
1477 if (part.parentIndex < begin || end <= part.parentIndex) {
1478 LOG_ERROR(logger,
"Model part parent index %u is outside of requested model part range [%zu, %zu) (%s)", part.parentIndex, begin, end, modelFile.path.c_str());
1481 const ComponentHandle& childParentSceneComponent = sceneComponents[part.parentIndex - begin];
1482 childParentSceneComponent.
resolveComponent<SceneComponent>()->children.emplace_back(entity);
1483 transformComponent->parent = transformComponents[part.parentIndex - begin];
1487 transformComponent->setChanged();
1489 if (part.meshIndex != NoIndex) {
1490 auto mcHandle = context->meshSystem->createComponent();
1491 entity->addComponent(mcHandle);
1493 auto meshComponent = mcHandle.resolveComponent<MeshComponent>();
1494 meshComponent->meshHandle = model->meshes[part.meshIndex];
1495 meshComponent->setChanged();
1497 auto mrcHandle = context->renderSystem->createComponent();
1498 entity->addComponent(mrcHandle);
1500 auto renderComponent = mrcHandle.resolveComponent<MeshRenderComponent>();
1501 if (part.boundsIndex != NoIndex) {
1502 context->renderSystem->setLocalBounds(renderComponent, model->bounds[part.boundsIndex]);
1504 renderComponent->startIndex = part.startIndex;
1505 renderComponent->vertexCount = part.vertexCount;
1506 renderComponent->objectId = assetInstance.objectId;
1507 renderComponent->layer = assetInstance.renderLayers;
1508 renderComponent->clipShapeComponent = assetInstance.clipShapeComponent;
1509 renderComponent->customRayPickHandling = assetInstance.customRayPickHandling;
1511 MaterialInstanceHandle partMaterial = part.materialIndex < model->materials.size()
1512 ? model->materials[part.materialIndex]
1514 if (assetInstance.useOverrideMaterial()) {
1515 renderComponent->material = assetInstance.material;
1517 else if(assetInstance.useCloneMaterial()){
1518 MaterialInstanceHandle master = assetInstance.material;
1519 renderComponent->
material = context->materialInstanceManager->createMaterialInstance(context->materialManager->generateHandle(master->material));
1520 renderComponent->material->clone(master.resolve());
1523 renderComponent->material->setPermutation(partMaterial->getPermutation());
1524 renderComponent->material->instanceFlags = partMaterial->instanceFlags;
1525 renderComponent->material->
options = partMaterial->options;
1526 renderComponent->material->cloneMatchingProperties(partMaterial.resolve());
1527 renderComponent->material->cloneMatchingVariants(partMaterial.resolve());
1531 renderComponent->material = partMaterial;
1534 MaterialHandle master = context->materialManager->getDefaultMaterial();
1535 renderComponent->material = context->materialInstanceManager->createMaterialInstance(master);
1537 renderComponent->setChanged();
1540 assetInstance.spawned(entity->getId());
1543 calculateModelCost(context, modelData, model);
1545 entityInstance.setSpawned(
true);
1546 entityInstance.setStaging(
false);
1551 bool spawnEntity(Context * context, AssetLodReference& lodReference, AssetInstanceData & assetInstance, EntityInstanceData & entityInstance, EntityInstanceData * parentInstance, EntityParentMode parentMode)
1553 auto & definition = assetInstance.asset->definition;
1554 auto & scene = assetInstance.scene;
1555 auto & entityDefinition = definition.scene[entityInstance.index];
1557 if (entityDefinition.isLodGroup()) {
1558 return spawnLodGroup(context, lodReference, assetInstance, entityInstance, parentInstance, parentMode);
1559 }
else if (entityDefinition.isAsset()) {
1560 return spawnAsset(context, lodReference, entityInstance, parentInstance);
1561 }
else if (entityDefinition.isModel()) {
1562 return spawnModel(context, assetInstance, entityInstance, parentInstance, parentMode);
1564 if (entityDefinition.isEmpty()) {
1565 entityInstance.setSpawned(
true);
1569 EntityCreationContext entityContext{};
1570 entityContext.context = context;
1571 entityContext.scene = &definition.scene;
1572 entityContext.parent = parentInstance->entityPtr;
1573 entityContext.parentMode = parentMode;
1575 entityInstance.entity = createEntity(entityContext, entityDefinition);
1576 entityInstance.entityPtr = entityInstance.entity.get();
1577 entityInstance.setSpawned(
true);
1578 entityInstance.setStaging(
false);
1580 assetInstance.spawned(entityInstance.entity->getId());
1582 auto rHandle = entityInstance.entity->getComponentHandle<MeshRenderComponent>();
1584 auto renderComponent = rHandle.resolveComponent<MeshRenderComponent>();
1585 if(renderComponent){
1586 renderComponent->material = assetInstance.material;
1590 if (entityDefinition.numChildren) {
1591 uint32_t childIndex = entityDefinition.firstChild;
1593 while (childIndex != ::NoValue) {
1594 checkEntity(context, lodReference, assetInstance, scene[childIndex], parentInstance);
1596 spawnEntity(context, lodReference, assetInstance, scene[childIndex], &entityInstance, EntityParentMode::Default);
1598 childIndex = definition.scene[childIndex].nextSibling;
1606 void updateEntity(Context * context, AssetLodReference& lodReference, AssetInstanceData & assetInstance, EntityInstanceData & entityInstance, EntityInstanceData * parentInstance)
1608 auto & scene = assetInstance.scene;
1610 if (entityInstance.isLodGroup()) {
1611 if (!entityInstance.isAtDesiredLod()) {
1612 spawnEntity(context, lodReference, assetInstance, entityInstance, parentInstance, EntityParentMode::TransformOnly);
1613 }
else if (entityInstance.isVisible()) {
1614 auto & lodData = scene.getLodData(entityInstance);
1615 uint32_t childIndex = lodData.lods[entityInstance.lod.target];
1617 while (childIndex != ::NoValue) {
1618 updateEntity(context, lodReference, assetInstance, scene[childIndex], parentInstance);
1620 childIndex = scene[childIndex].nextLodSibling;
1623 }
else if (entityInstance.isSpawned()) {
1624 if (entityInstance.isAsset()) {
1625 entityInstance.assetInstance->priority = assetInstance.priority;
1626 for (
auto & root : entityInstance.assetInstance->scene.roots) {
1627 updateEntity(context, lodReference, *entityInstance.assetInstance, entityInstance.assetInstance->scene[root], parentInstance);
1629 }
else if (entityInstance.hasChildren()) {
1630 auto & definition = assetInstance.asset->definition;
1631 auto & entityDefinition = definition.scene[entityInstance.index];
1633 for (uint32_t i = 0; i < entityDefinition.numChildren; ++i) {
1634 auto & childDefinition = definition.scene[definition.scene.getChild(entityDefinition.firstChild, i)];
1636 updateEntity(context, lodReference, assetInstance, scene[childDefinition.index], &entityInstance);
1639 }
else if (entityInstance.isStaging()) {
1640 if (entityInstance.isAsset() || entityInstance.isModel()) {
1641 if (checkEntity(context, lodReference, assetInstance, entityInstance, parentInstance)) {
1642 spawnEntity(context, lodReference, assetInstance, entityInstance, parentInstance, EntityParentMode::TransformOnly);
1645 spawnEntity(context, lodReference, assetInstance, entityInstance, parentInstance, EntityParentMode::TransformOnly);
1648 if (!entityInstance.isSpawned()) {
1649 if (checkEntity(context, lodReference, assetInstance, entityInstance, parentInstance)) {
1650 spawnEntity(context, lodReference, assetInstance, entityInstance, parentInstance, EntityParentMode::TransformOnly);
1656 void dispatchMessages(Context * context, AssetInstanceData & assetInstance)
1658 CpuInstrumentationScope(SCOPE_ASSETSYSTEM,
"AssetSystem::dispatch");
1659 auto dispatchTime =assetInstance.stats.frame.startDispatch();
1661 auto systemData = assetInstance.systemData;
1667 for (EntityId spawned : assetInstance.spawnedEntities) {
1668 context->dynamicComponentSystem->sendMessage(assetInstance.container, systemData->entityCreatedId, (
void *)spawned);
1671 for (EntityId destroyed : assetInstance.destroyedEntities) {
1672 context->dynamicComponentSystem->sendMessage(assetInstance.container, systemData->entityDestroyedId, (
void *)destroyed);
1675 assetInstance.spawnedEntities.clear();
1676 assetInstance.destroyedEntities.clear();
1679 void processInFlightRequests(Context* context, AssetSystemData * systemData)
1681 CpuInstrumentationScope(SCOPE_ASSETSYSTEM,
"AssetSystem::processInFlightRequests");
1683 for (
auto request : systemData->inFlightRequests) {
1684 if (request->canceled) {
1685 context->modelManager->cancelModelLoad(request->model);
1686 systemData->requestPool.destroy(request);
1690 if (request->model && request->model->isLoaded()) {
1691 auto assetInstance = request->assetInstance;
1693 auto & modelFile = assetInstance->scene.modelFiles[request->modelFileIndex];
1694 modelFile.model = request->model;
1695 modelFile.request =
nullptr;
1697 --assetInstance->stats.numRequests;
1699 systemData->requestPool.destroy(request);
1701 systemData->stillInFlight.emplace_back(request);
1705 std::swap(systemData->stillInFlight, systemData->inFlightRequests);
1706 systemData->stillInFlight.clear();
1709 size_t processPendingRequests(Context* context,
1710 AssetSystemData* systemData,
1711 ModelManager* modelManager,
1712 AssetSystemVector<AssetModelRequest*>& pendingRequests,
1713 AssetSystemVector<AssetModelRequest*>& inFlightRequests,
1714 const size_t maxInFlight,
1715 const size_t maxProcessed)
1717 CpuInstrumentationScope(SCOPE_ASSETSYSTEM,
"AssetSystem::processPendingRequests");
1719 size_t processed = 0;
1721 if (!pendingRequests.empty()) {
1722 std::sort(pendingRequests.begin(), pendingRequests.end(), [](
auto a,
auto b) ->
bool
1724 return a->priority < b->priority;
1728 while (!pendingRequests.empty() && processed < maxProcessed && inFlightRequests.size() < maxInFlight) {
1729 auto request = pendingRequests.back();
1730 pendingRequests.pop_back();
1732 if (systemData && request->canceled) {
1733 LOG_TRACE(logger,
"Discarding cancelled pending request");
1734 systemData->requestPool.destroy(request);
1738 auto assetInstance = request->assetInstance;
1741 if (assetInstance && assetInstance->useOverrideMaterial()) {
1744 if (assetInstance && assetInstance->useCloneMaterial()) {
1748 request->model = modelManager ? modelManager->loadModel(request->path, NoResourceId, flags) : ModelHandle{};
1750 inFlightRequests.emplace_back(request);
1755 while (systemData && maxInFlight < inFlightRequests.size()) {
1756 LOG_TRACE(logger,
"Cancelling in-flight request and moving it to pending as limits are violated.");
1758 AssetModelRequest* request = inFlightRequests.back();
1759 inFlightRequests.pop_back();
1761 if (request->model) {
1762 context->modelManager->cancelModelLoad(request->model);
1763 request->model = ModelHandle{};
1766 pendingRequests.emplace_back(request);
1772 void pullCameraMatrices(Context* context, AssetSystemData* systemData)
1774 if (CameraComponent* mainCam = context->cameraSystem->getMainCamera(); mainCam) {
1775 if (TransformComponent* trCompLod = mainCam->getComponent<TransformComponent>(); trCompLod) {
1777 if (trCompLod->hasChanged()) {
1778 context->transformSystem->updateTransformData(*trCompLod);
1780 const CameraData& camData = context->cameraSystem->getData(mainCam);
1782 systemData->worldFromView = context->transformSystem->getLocalToWorld(trCompLod);
1783 systemData->worldFromClip = systemData->worldFromView * glm::inverse(camData.rawProjectionMatrix);
1785 systemData->viewFromWorld = glm::inverse(systemData->worldFromView);
1786 systemData->clipFromWorld = camData.rawProjectionMatrix * systemData->viewFromWorld;
1788 systemData->minDistance = mainCam->enableClippingPlaneAdjustment ? mainCam->nearPlaneLimit : mainCam->nearDistance;
1789 systemData->maxDistance = context->variables->get(maxDistanceVariableName, kDefaultMaxDistance);
1791 if (
int maxLodDepth = context->variables->get(maxLodDepthVariableName, kDefaultMaxLodDepth); 0 <= maxLodDepth) {
1792 systemData->maxLodDepth = maxLodDepth;
1795 systemData->maxLodDepth = ~0u;
1800 systemData->worldFromView = glm::mat4(1.f);
1801 systemData->worldFromClip = glm::mat4(1.f);
1802 systemData->viewFromWorld = glm::mat4(1.f);
1803 systemData->clipFromWorld = glm::mat4(1.f);
1806 void updateRenderPropertiesRecurse(
const AssetInstanceData& instanceData,
Entity* entity) {
1808 if (RenderComponent* renderComp = entity->
getComponent<RenderComponent>(); renderComp) {
1809 renderComp->layer = instanceData.renderLayers;
1810 renderComp->objectId = instanceData.objectId;
1811 renderComp->clipShapeComponent = instanceData.clipShapeComponent;
1812 renderComp->setChanged();
1815 if (SceneComponent* sceneComp = entity->
getComponent<SceneComponent>(); sceneComp) {
1816 for (
EntityPtr& e : sceneComp->children) {
1817 updateRenderPropertiesRecurse(instanceData, e.get());
1823 void updateInstancedModelChildEntityRenderProperties(Context* context, AssetInstanceData& assetInstance,
Entity& entity)
1825 if (RenderComponent* renderComp = entity.
getComponent<RenderComponent>(); renderComp) {
1826 renderComp->objectId = assetInstance.objectId;
1827 renderComp->layer = assetInstance.renderLayers;
1828 renderComp->clipShapeComponent = assetInstance.clipShapeComponent;
1829 renderComp->setChanged();
1832 if (SceneComponent* sceneComp = entity.
getComponent<SceneComponent>(); sceneComp) {
1833 for (
EntityPtr& child : sceneComp->children) {
1835 updateInstancedModelChildEntityRenderProperties(context, assetInstance, *child.get());
1840 void updateAssetInstanceRenderProperties(Context* context, AssetInstanceData& assetInstance);
1842 void updateEntityInstanceRenderProperties(Context* context, AssetInstanceData& assetInstance, EntityInstanceData& entityInstance)
1844 if (entityInstance.isLodGroup()) {
1846 if (entityInstance.isVisible() && entityInstance.isAtDesiredLod()) {
1847 LodInstanceData& lodData = assetInstance.scene.getLodData(entityInstance);
1848 uint32_t childIndex = lodData.lods[entityInstance.lod.target];
1849 while (childIndex != ::NoValue) {
1850 updateEntityInstanceRenderProperties(context, assetInstance, assetInstance.scene[childIndex]);
1851 childIndex = assetInstance.scene[childIndex].nextLodSibling;
1857 else if (entityInstance.isSpawned()) {
1859 const AssetDefinition& definition = assetInstance.asset->definition;
1860 const SceneEntityDefinition& entityDefinition = definition.scene[entityInstance.index];
1862 if (entityInstance.isAsset()) {
1863 entityInstance.assetInstance->clipShapeComponent = assetInstance.clipShapeComponent;
1864 updateAssetInstanceRenderProperties(context, *entityInstance.assetInstance);
1867 else if (entityInstance.isModel()) {
1868 if (entityInstance.entity) {
1869 updateInstancedModelChildEntityRenderProperties(context, assetInstance, *entityInstance.entity.get());
1873 else if (entityInstance.hasChildren()) {
1875 uint32_t childIndex = entityDefinition.firstChild;
1876 while (childIndex != ::NoValue) {
1877 updateEntityInstanceRenderProperties(context, assetInstance, assetInstance.scene[childIndex]);
1878 childIndex = definition.scene[childIndex].nextSibling;
1886 void updateAssetInstanceRenderProperties(Context* context, AssetInstanceData& assetInstance)
1888 for (uint32_t root : assetInstance.scene.roots) {
1889 updateEntityInstanceRenderProperties(context, assetInstance, assetInstance.scene[root]);
1900 for (
auto& assetComponent : assetSystem->pool) {
1901 const auto bbox = assetSystem->getWorldBounds(&assetComponent,
false);
1910 if (!assetComponent)
return false;
1911 const auto bbox = context->assetSystem->getWorldBounds(assetComponent, ignoreVisibility);
1912 if (!isEmpty(bbox)) {
1923Cogs::Core::AssetSystem::~AssetSystem() =
default;
1927 this->context = context;
1929 systemData = std::make_unique<AssetSystemData>();
1933 systemData->entityCreatedId = context->dynamicComponentSystem->registerMessage(
"entityCreated");
1934 systemData->entityDestroyedId = context->dynamicComponentSystem->registerMessage(
"entityDestroyed");
1936 context->
variables->getOrAdd(lodFreezeVariableName, kDefaultLodFreeze);
1937 context->
variables->getOrAdd(cullModelsName, cullModelsValue);
1938 if (
Variable* var = context->
variables->get(maxModelsInFlightVariableName); var->isEmpty()) {
1939 context->
variables->set(maxModelsInFlightVariableName, kDefaultMaxModelsInFlight);
1941 if (
Variable* var = context->
variables->get(maxModelLoadsPerFrameVariableName); var->isEmpty()) {
1942 context->
variables->set(maxModelLoadsPerFrameVariableName, kDefaultMaxModelLoadsPerFrame);
1944 if (
Variable* var = context->
variables->get(maxDistanceVariableName); var->isEmpty()) {
1945 context->
variables->set(maxDistanceVariableName, kDefaultMaxDistance);
1947 if (
Variable* var = context->
variables->get(maxLodDepthVariableName); var->isEmpty()) {
1948 context->
variables->set(maxLodDepthVariableName, kDefaultMaxLodDepth);
1950 context->
variables->getOrAdd(allowMaterialOverride, kDefaultAllowMaterialOverride);
1956 const float qualityScale = context->
qualityService->assetSystemToleranceScale;
1958 bool freeze = context->
variables->get(lodFreezeVariableName, kDefaultLodFreeze);
1960 if (
const Variable* var = context->
variables->get(cullModelsName); !var->isEmpty()) {
1961 cullModelsValue = var->getBool();
1964 pullCameraMatrices(context, systemData.get());
1966 bool allowOverrideMaterial = context->
variables->get(allowMaterialOverride,
true);
1972 AssetSystemVector<AssetUpdate>& newUpdates = systemData->newUpdates; assert(newUpdates.empty());
1973 AssetSystemVector<AssetInstanceData*>& currentOnDemandInstances = systemData->currentOnDemandInstances; assert(currentOnDemandInstances.empty());
1974 for (
auto & assetComponent : pool) {
1978 AssetData& assetData = getData<AssetData>(&assetComponent);
1981 if (assetData.hasChanged(assetComponent.asset)) {
1982 assetData.setAsset(assetComponent.asset);
1986 if (!assetData.instanceData) {
1987 auto instanceData = systemData->instanceData.create();
1988 assetData.instanceData = std::shared_ptr<AssetInstanceData>(instanceData, [&](
AssetInstanceData * d)
1990 auto current = d->next;
1992 auto next = current->next;
1993 systemData->removeRequests(current);
1994 systemData->instanceData.destroy(current);
1997 systemData->removeRequests(d);
1998 systemData->instanceData.destroy(d);
2006 assetInstance->systemData = systemData.get();
2007 assetInstance->asset = assetData.asset;
2008 assetInstance->container = assetComponent.getContainer();
2009 assetInstance->scene.initialized =
false;
2014 assetInstance->customRayPickHandling = assetData.customRayPickHandling;
2015 assetInstance->priority = assetComponent.priority;
2018 assetInstance->objectId = renderComponent->objectId;
2019 assetInstance->renderLayers = renderComponent->layer;
2023 if (assetInstance->useOverrideMaterial() || assetInstance->useCloneMaterial()) {
2024 if (assetComponent.material) {
2025 assetInstance->material = assetComponent.material;
2028 if (!systemData->defaultMaterialHandle) {
2029 systemData->defaultMaterialHandle = context->materialInstanceManager->createMaterialInstance(context->materialManager->getDefaultMaterial());
2031 assetInstance->material = systemData->defaultMaterialHandle;
2036 newUpdates.push_back({
2037 assetComponent.getContainer(),
2038 getHandle(&assetComponent),
2039 assetComponent.asset,
2045 if (assetData.asset && assetData.instanceData) {
2051 if (
EntityPtr clipShape = clipRefComp->clipShape.lock(); clipShape) {
2055 bool forward = assetInstance.clipShapeComponent != clipShapeComponent;
2056 assetInstance.clipShapeComponent = clipShapeComponent;
2060 forward = forward || (assetInstance.objectId != renderComponent->objectId);
2061 assetInstance.objectId = renderComponent->objectId;
2063 forward = forward || (assetInstance.renderLayers != renderComponent->layer);
2064 assetInstance.renderLayers = renderComponent->layer;
2069 updateAssetInstanceRenderProperties(context, assetInstance);
2074 if (assetData.asset && assetData.instanceData && assetData.instanceData->onDemand) {
2075 assetData.instanceData->freezeLod = freeze || assetComponent.freezeLod;
2076 assetData.instanceData->forceTolerance = assetComponent.forceTolerance;
2077 assetData.instanceData->tolerance = (assetComponent.forceTolerance ? 1.f : qualityScale) * assetComponent.tolerance;
2078 assetData.instanceData->minDistance = assetComponent.minDistance;
2080 assetData.instanceData->selectedIdRanges.clear();
2081 if (!assetComponent.idRanges.empty()) {
2083 size_t count = assetComponent.idRanges.size() / 2;
2084 std::vector<uint32_t>& selectedIdRanges = assetData.instanceData->selectedIdRanges;
2085 for (
size_t k = 0; k < count; k++) {
2086 const uint32_t rangeMin = assetComponent.idRanges[2 * k + 0];
2087 const uint32_t rangeMax = assetComponent.idRanges[2 * k + 1];
2088 if (rangeMax < rangeMin || (!selectedIdRanges.empty() && rangeMin < selectedIdRanges.back()) ) {
2090 selectedIdRanges.clear();
2093 selectedIdRanges.push_back(rangeMin);
2094 selectedIdRanges.push_back(rangeMax);
2098 currentOnDemandInstances.emplace_back(assetData.instanceData.get());
2107 AssetSystemVector<AssetUpdate>& readyUpdates = systemData->readyUpdates; assert(readyUpdates.empty());
2108 for (
auto & assetUpdate : newUpdates) {
2109 if (!assetUpdate.asset) {
2111 }
else if (assetUpdate.asset && assetUpdate.asset->isLoaded()) {
2113 for (
auto & resourceDefinition : assetUpdate.asset->definition.resources) {
2114 context->assetManager->instantiateResource(assetUpdate.asset, resourceDefinition);
2117 if (!assetUpdate.instanceData->onDemand) {
2119 readyUpdates.emplace_back(assetUpdate);
2126 for (
auto & assetUpdate : readyUpdates) {
2127 auto asset = assetUpdate.asset.resolve();
2128 const size_t entityCount = asset->definition.scene.entities.size();
2129 std::vector<ComponentModel::Entity*> entities(entityCount);
2130 for (
auto & def : asset->definition.scene.entities) {
2131 Entity* parent = assetUpdate.container;
2132 if (def.parentIndex !=
static_cast<uint32_t
>(-1)) {
2133 assert(def.parentIndex < entityCount && entities[def.parentIndex]);
2134 parent = entities[def.parentIndex];
2136 assert(def.index < entityCount && entities[def.index] ==
nullptr);
2137 entities[def.index] = createEntity(context, asset->definition.
scene, def, parent);
2140 readyUpdates.clear();
2145 processInFlightRequests(context, systemData.get());
2150 AssetSystemVector<AssetInstanceData*>& liveAssetInstances = systemData->liveAssetInstances; assert(liveAssetInstances.empty());
2151 liveAssetInstances.reserve(currentOnDemandInstances.size());
2152 for (
auto assetInstance : currentOnDemandInstances) {
2153 assetInstance->stats.frame = {};
2155 if (!assetInstance->scene.initialized) {
2156 initializeAssetInstance(*assetInstance);
2159 if (assetInstance->scene.entities.empty())
continue;
2161 auto sceneComponent = assetInstance->container->getComponent<
SceneComponent>();
2163 const bool visible = sceneComponent ? sceneComponent->
visible :
true;
2164 const bool visibleChanged = visible != assetInstance->visible;
2166 if (!visible && !visibleChanged)
continue;
2168 assetInstance->visible = visible;
2170 liveAssetInstances.emplace_back(assetInstance);
2172 currentOnDemandInstances.clear();
2175 Parallel::forEach(context, liveAssetInstances.size(), [&](
size_t i)
2177 auto assetInstance = liveAssetInstances[i];
2179 if (!assetInstance->freezeLod) {
2180 assetInstance->stats.frustumCullIn = 0;
2181 assetInstance->stats.frustumCullOut = 0;
2182 assetInstance->stats.bboxCullOut = 0;
2183 updateAssetDesiredLevels(context, *assetInstance, 0, 1);
2185 },
"AssetSystem::updateLiveInstances");
2187 for (
auto assetInstance : liveAssetInstances) {
2189 auto processingTime = assetInstance->stats.frame.startProcessing();
2192 auto updateTime = assetInstance->stats.frame.startUpdate();
2195 CpuInstrumentationScope(SCOPE_ASSETSYSTEM,
"AssetSystem::updateEntities");
2198 root.entityPtr = assetInstance->container;
2201 getLodReference(context, *assetInstance, lodReference, assetInstance->container);
2203 for (
size_t i = 0; i < assetInstance->scene.roots.size(); ++i) {
2204 updateEntity(context, lodReference, *assetInstance, assetInstance->scene[assetInstance->scene.roots[i]], &root);
2208 CpuInstrumentationScope(SCOPE_ASSETSYSTEM,
"AssetSystem::calculateCost");
2210 auto costTime = assetInstance->stats.frame.startCostCalculation();
2212#if ENABLE_COST_TRACKING
2213 assetInstance->stats.current = { 0, 0, 0 };
2214 for (
size_t i = 0; i < assetInstance->scene.roots.size(); ++i) {
2215 calculateCost(context, *assetInstance, assetInstance->scene[assetInstance->scene.roots[i]]);
2220 dispatchMessages(context, *assetInstance);
2223 double limit = assetInstance->stats.frameLimits.processingTime;
2224 if (assetInstance->stats.frame.processingTime > limit) {
2225 LOG_TRACE(logger,
"Asset processing time exceeded limit %.3fs.", limit);
2226 LOG_TRACE(logger,
" Lod time: %.3fs", assetInstance->stats.frame.lodTime);
2227 LOG_TRACE(logger,
" Update time: %.3fs", assetInstance->stats.frame.updateTime);
2228 LOG_TRACE(logger,
" Cost time: %.3fs", assetInstance->stats.frame.calculateCostTime);
2229 LOG_TRACE(logger,
" ------------------------");
2230 LOG_TRACE(logger,
" Entities spawned: %d", assetInstance->stats.frame.spawnedEntities);
2231 LOG_TRACE(logger,
" Entities destroyed: %d", assetInstance->stats.frame.destroyedEntities);
2232 LOG_TRACE(logger,
" Models requested: %d", assetInstance->stats.frame.modelsRequested);
2233 LOG_TRACE(logger,
"");
2236 liveAssetInstances.clear();
2238 processPendingRequests(context, systemData.get(),
2239 context->modelManager,
2240 systemData->pendingRequests,
2241 systemData->inFlightRequests,
2242 (
size_t)context->variables->get(maxModelsInFlightVariableName, kDefaultMaxModelsInFlight),
2243 (
size_t)context->variables->get(maxModelLoadsPerFrameVariableName, kDefaultMaxModelLoadsPerFrame));
2248 base::postUpdate(context);
2256 if (pool.size() == 1u) {
2257 assert(bounds ==
nullptr);
2258 bounds = std::make_unique<AssetBounds>(
this);
2259 context->bounds->addBoundsExtension(bounds.get());
2267 base::destroyComponent(component);
2269 if (pool.size() == 0u && bounds) {
2270 context->bounds->removeBoundsExtension(bounds.get());
2277 if (!assetComponent)
return nullptr;
2279 auto & data = getData<AssetData>(assetComponent);
2281 return data.instanceData ? &data.instanceData->stats :
nullptr;
2284const Cogs::Geometry::BoundingBox* Cogs::Core::AssetSystem::getLocalBounds(AssetComponent* assetComponent)
const
2286 if (!assetComponent)
2289 auto& data = getData<AssetData>(assetComponent);
2290 if (!data.instanceData)
return nullptr;
2291 if (data.instanceData->scene.entities.empty())
return nullptr;
2294 return &data.instanceData->scene.getLocalBounds(data.instanceData->scene[0]);
2297std::span<const Cogs::Core::AssetModelRequest* const> Cogs::Core::AssetSystem::getPendingRequests()
const
2299 return systemData->pendingRequests;
2302std::span<const Cogs::Core::AssetModelRequest* const> Cogs::Core::AssetSystem::getInFlightRequests()
const
2304 return systemData->inFlightRequests;
2307Cogs::Geometry::BoundingBox Cogs::Core::AssetSystem::getWorldBounds(AssetComponent* assetComponent,
bool ignoreVisibility)
const
2309 if (!ignoreVisibility) {
2310 auto sc = assetComponent->getComponent<SceneComponent>();
2311 if (sc && !sc->visible)
2312 return Cogs::Geometry::BoundingBox();
2315 auto bbox = getLocalBounds(assetComponent);
2316 if (bbox ==
nullptr || isEmpty(*bbox))
2317 return Cogs::Geometry::BoundingBox();
2319 auto transformComponent = assetComponent->getComponent<TransformComponent>();
2320 if (transformComponent) {
2321 const auto& transform = context->transformSystem->getLocalToWorld(transformComponent);
2322 return Bounds::getTransformedBounds(*bbox, transform);
ComponentType * getComponent() const
class Entity * getContainer() const
Get the container currently owning this component instance.
Container for components, providing composition of dynamic entities.
T * getComponent() const
Get a pointer to the first component implementing the given type in the entity.
void getBounds(Context *context, Cogs::Geometry::BoundingBox &bounds) override
Expand bounds including bounds of all entities in this system in world coordinates.
ComponentHandle createComponent() override
Create a new component instance.
void initialize(Context *context) override
Initialize the system.
void destroyComponent(ComponentHandle component) override
Destroy the component held by the given handle.
Sets up a clipping shape that can be used by multiple entities.
Component that attaches a ClipShape to an entity.
void postUpdate()
Perform post update logic in the system.
void update()
Updates the system state to that of the current frame.
Component system template with multiple parallel structures per component stored in separate pools si...
A Context instance contains all the services, systems and runtime components needed to use Cogs.
std::unique_ptr< class QualityService > qualityService
Quality service instance.
class EntityStore * store
Entity store.
std::unique_ptr< class Variables > variables
Variables service instance.
std::unique_ptr< class Scene > scene
Scene structure.
void removeChildren(ComponentModel::Entity *entity)
Removes all children from the given entity.
Base component for all rendering content.
Contains information on how the entity behaves in the scene.
bool visible
If the entity this component is a member of should be visible.
Log implementation class.
Base allocator implementation.
Provides a weakly referenced view over the contents of a string.
constexpr size_t hash() const noexcept
Get the hash code of the string.
Contains the Engine, Renderer, resource managers and other systems needed to run Cogs....
@ InvertedCube
Clip the inside of a cube.
@ None
No clipping at all.
@ Cube
Clip the outside of a cube,.
std::shared_ptr< ComponentModel::Entity > EntityPtr
Smart pointer for Entity access.
bool HandleIsValid(const ResourceHandle_t< T > &handle)
Check if the given resource is valid, that is not equal to NoHandle or InvalidHandle.
ModelLoadFlags
Model loading flags. May be combined with resource loading flags.
@ NoDefaultName
No default name.
@ SkipMaterials
Tell the loader it may skip creating material instances during loading.
@ Ready
All 6 baselayers are fully loaded, instance will begin rendering.
AssetFlags
Controls asset system's model instance behaviour.
@ InstantiateOnDemand
Dynamically calculate lod levels and only instantiate what is visible.
@ RelativePaths
Paths in asset file is relative to file's path.
@ OverrideMaterial
Override any model's material with material member of asset component.
@ CloneMaterial
Clone the assetComponent material over any loaded models's material and copy over properties and vari...
RenderLayers
Contains common render layers.
Contains geometry calculations and generation.
constexpr Log getLogger(const char(&name)[LEN]) noexcept
ComponentIndex SizeType
Type used to track the size of pools.
Pool used to store elements of ElementType.
Handle to a Component instance.
static ComponentHandle Empty()
Returns an empty, invalid handle. Will evaluate to false if tested against using operator bool().
ComponentType * resolveComponent() const
Instantiates an asset model into the scene.
Material * material
Material resource this MaterialInstance is created from.
MaterialOptions options
Material rendering options.
uint32_t depth
Depth in the lod hierarchy, root is 1.
static const ResourceHandle_t NoHandle
Handle representing a default (or none if default not present) resource.
Runtime control variable.