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;
236 uint32_t objectId = NoObjectId;
244 std::vector<uint32_t> selectedIdRanges;
246 bool visible =
false;
247 bool onDemand =
false;
248 bool relativePaths =
false;
249 bool overrideMaterial =
false;
250 bool cloneMaterial =
false;
251 bool initialized =
false;
252 bool freezeLod =
false;
253 bool forceTolerance =
false;
254 float tolerance = 1.0f;
255 float priority = 0.0f;
256 float minDistance = 1.f;
258 std::string directoryPath;
263 bool useOverrideMaterial()
const {
return overrideMaterial; }
264 bool useCloneMaterial()
const {
return cloneMaterial; }
265 bool useRelativePaths()
const {
return relativePaths; }
267 void spawned(EntityId entityId)
269 EntityId& dst = spawnedEntities.
grow();
272 ++stats.frame.spawnedEntities;
276 void destroyed(EntityId entityId)
278 EntityId& dst = destroyedEntities.
grow();
281 ++stats.frame.destroyedEntities;
287 systemData = instance->systemData;
288 parentInstance = instance;
291 next = instance->next;
292 if (instance->next) instance->next->prev =
this;
293 instance->next =
this;
299 if (next) next->prev = prev;
300 next = prev =
nullptr;
307 return asset->definition.scene[instance.index];
323 instanceData(MemBlockType::AssetInstance),
324 requestPool(MemBlockType::AssetSystemData)
330 AssetSystemVector<AssetModelRequest *> pendingRequests;
331 AssetSystemVector<AssetModelRequest *> inFlightRequests;
332 AssetSystemVector<AssetModelRequest *> stillInFlight;
336 AssetSystemVector<AssetUpdate> newUpdates;
337 AssetSystemVector<AssetInstanceData*> currentOnDemandInstances;
338 AssetSystemVector<AssetUpdate> readyUpdates;
339 AssetSystemVector<AssetInstanceData*> liveAssetInstances;
342 glm::mat4 worldFromView = glm::mat4(1.f);
343 glm::mat4 viewFromWorld = glm::mat4(1.f);
344 glm::mat4 clipFromWorld = glm::mat4(1.f);
345 glm::mat4 worldFromClip = glm::mat4(1.f);
346 float minDistance = 0.f;
347 float maxDistance = 0.f;
348 uint32_t maxLodDepth = 0;
350 MessageId entityCreatedId = NoMessage;
351 MessageId entityDestroyedId = NoMessage;
356 void AssetInstanceData::destroy()
360 systemData->instanceData.destroy(
this);
363 uint32_t findFirstLodChild(
const AssetDefinition & definition, uint32_t firstChild, int32_t targetLevel);
365 void initializeAssetInstance(AssetInstanceData & assetInstance)
367 auto initTime = assetInstance.stats.frame.startInit();
368 assetInstance.assetGeneration =
static_cast<uint16_t
>(assetInstance.asset->getGeneration());
369 assetInstance.directoryPath = IO::parentPath(assetInstance.asset->getSource());
371 assetInstance.stats = {};
372#if ENABLE_COST_TRACKING
373 assetInstance.stats.max.memory = 512 * 1024 * 1024;
374 assetInstance.stats.max.drawCalls = 100'000;
375 assetInstance.stats.max.primitiveCount = 10'000'000;
377 assetInstance.stats.frameLimits.processingTime = 0.010;
379 const AssetDefinition & definition = assetInstance.asset->definition;
380 SceneInstanceData & scene = assetInstance.scene;
382 scene.roots.reserve(definition.scene.numTopLevelEntities);
383 scene.entities.resize(definition.scene.entities.size());
384 scene.lods.reserve(definition.scene.numLodGroups);
385 scene.boxes.resize(definition.scene.entities.size());
387 std::unordered_map<size_t, uint32_t> modelFileIndexes;
389 for (
size_t i = 0; i < definition.scene.entities.size(); ++i) {
390 const SceneEntityDefinition& entityDef = definition.scene.entities[i];
391 EntityInstanceData & entityInstance = scene[i];
392 PropertyRange properties = definition.scene.getProperties(entityDef);
394 entityInstance.index = uint32_t(i);
395 entityInstance.flags = entityDef.flags;
396 entityInstance.nextSibling = entityDef.nextSibling;
397 entityInstance.nextLodSibling = entityDef.nextLodSibling;
399 if (entityDef.parentIndex == ::NoValue) {
400 scene.roots.emplace_back(uint32_t(i));
404 if (entityDef.isLodGroup()) {
405 entityInstance.lod.index = (uint32_t)scene.lods.size();
406 auto & lodData = scene.lods.emplace_back();
408 entityInstance.lod.current = entityInstance.lod.target = ::NoValue;
410 std::span<const uint32_t> lodIds = properties.getProperty(idsString, std::span<const uint32_t>());
411 if (!lodIds.empty()) {
412 size_t lodCount = lodIds.size();
414 LOG_ERROR(logger,
"Lod id range array should have an even number of items (n=%zu)", lodCount);
418 lodData.ids.reserve(lodCount);
419 for (
size_t k = 0; k + 1 < lodCount; k += 2) {
420 uint32_t a = lodIds[k + 0];
421 uint32_t b = lodIds[k + 1];
423 LOG_ERROR(logger,
"Lod id range contains an invalid range ([%u, %u])", a, b);
427 if (!lodData.ids.empty() && b < lodData.ids.back()) {
428 LOG_ERROR(logger,
"Lod id ranges are not sorted.");
432 lodData.ids.emplace_back(a);
433 lodData.ids.emplace_back(b);
438 std::span<const float> bboxValues = properties.getProperty(bboxString, std::span<const float>());
439 if (bboxValues.size() == 6) {
440 scene.boxes[entityDef.index] = Geometry::BoundingBox(std::span<const float, 6>(bboxValues));
443 LOG_ERROR(logger,
"Lod level item bounding box has wrong number of elements (%zu != 6)", bboxValues.size());
444 scene.boxes[entityDef.index] = Cogs::Geometry::BoundingBox();
447 std::span<const float> errorValues = properties.getProperty(errorsString, std::span<const float>());
448 if (errorValues.size() != entityDef.lod.numLods) {
449 LOG_WARNING(logger,
"Lod level item count (=%u) does not match number of error values (=%zu)", entityDef.lod.numLods, errorValues.size());
452 if (errorValues.size()) {
453 lodData.errors.assign(errorValues.begin(), errorValues.end());
455 while (lodData.errors.size() < entityDef.lod.numLods) lodData.errors.push_back(lodData.errors.back() * lodData.errors.back());
460 lodData.errors.resize(entityDef.lod.numLods);
464 if (!isEmpty(scene.boxes[entityDef.index])) scale = glm::distance(scene.boxes[entityDef.index].min, scene.boxes[entityDef.index].max);
466 for (
size_t l = 0; l < lodData.errors.size(); ++l) {
467 lodData.errors[l] = scale * scale * float(l) * float(l);
471 lodData.lods.resize(entityDef.lod.numLods);
472 for (uint32_t j = 0; j < entityDef.lod.numLods; ++j) {
473 lodData.lods[j] = findFirstLodChild(definition, entityDef.firstChild, j);
478 else if (entityDef.isModel()) {
479 uint32_t modelIndex = uint32_t(scene.models.size());
480 ModelInstanceData & modelData = scene.models.emplace_back();
481 modelData.index = entityInstance.index;
482 modelData.cost = { 0, 0, 0 };
483 modelData.part = entityDef.model.part;
484 modelData.depth = entityDef.model.depth;
485 entityInstance.model.index = modelIndex;
487 if (entityInstance.hasError())
continue;
490 Cogs::Geometry::BoundingBox bbox;
491 if (entityDef.numProperties) {
492 std::span<const float> bboxValues = properties.getProperty(bboxString, std::span<const float>());
493 if (bboxValues.size() == 6) {
494 bbox = Geometry::BoundingBox(std::span<const float, 6>(bboxValues));
497 scene.boxes[entityDef.index] = bbox;
501 const PropertyInfo& pathItem = definition.scene.properties.getPropertyByIndex(entityDef.model.index);
502 if (pathItem.type == PropertyType::UnsignedInteger) {
503 path = createAssetResourcePathFromIndex(pathItem.uintValue, AssetResourceType::Model);
505 else if (pathItem.type == PropertyType::String) {
506 path = definition.scene.properties.getString(entityDef.model.index).to_string();
510 if (
auto foundIt = modelFileIndexes.find(code); foundIt != modelFileIndexes.end()) {
511 modelData.fileIndex = foundIt->second;
515 uint32_t fileIndex = (uint32_t)scene.modelFiles.size();
516 ModelFileData & modelFile = scene.modelFiles.emplace_back();
518 if (assetInstance.useRelativePaths() && IO::isRelative(path)) {
519 modelFile.path = IO::combine(assetInstance.directoryPath, std::move(path));
521 modelFile.path = std::move(path);
524 int byteSize = properties.getProperty(byteSizeString, 0);
526 modelFile.byteSize = byteSize;
528 modelFile.byteSize = 1000;
531 modelData.fileIndex = fileIndex;
532 modelFileIndexes[code] = fileIndex;
537 scene.initialized =
true;
540 glm::vec3 euclidean(
const glm::vec4& a)
542 return (1.f / a.w)*glm::vec3(a);
547 glm::vec3 frustumEdgesP[4];
548 glm::vec3 frustumEdgesD[4];
550 glm::vec3 lodRef = glm::vec3(0, 0, 0);
551 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) };
552 uint32_t planeCount = 6;
553 bool invertedClip =
false;
555 float nearDistance = 1.f;
556 float maxDistance = FLT_MAX;
557 uint32_t maxLodDepth = ~0u;
558 uint32_t frustumCullIn = 0;
559 uint32_t frustumCullOut = 0;
560 uint32_t bboxCullOut = 0;
568 if (!trCompRef)
return;
573 const glm::mat4& worldFromModel = context->transformSystem->getLocalToWorld(ref->
getComponent<TransformComponent>());
574 const glm::mat4 modelFromWorld = glm::inverse(worldFromModel);
575 const glm::mat4 modelFromView = modelFromWorld * systemData->worldFromView;
576 const glm::mat4 rawViewProjectionTransposed = glm::transpose(systemData->clipFromWorld * worldFromModel);
578 lodRef.lodRef = euclidean(modelFromView * glm::vec4(0, 0, 0, 1));
579 lodRef.nearDistance = std::max(assetInstance.minDistance, systemData->minDistance);
580 lodRef.planes[0] = rawViewProjectionTransposed[3] + rawViewProjectionTransposed[0];
581 lodRef.planes[1] = rawViewProjectionTransposed[3] - rawViewProjectionTransposed[0];
582 lodRef.planes[2] = rawViewProjectionTransposed[3] + rawViewProjectionTransposed[1];
583 lodRef.planes[3] = rawViewProjectionTransposed[3] - rawViewProjectionTransposed[1];
585 if (0 < systemData->maxDistance) {
586 lodRef.planes[4] = glm::transpose(systemData->viewFromWorld * worldFromModel) * glm::vec4(0, 0, 1, systemData->maxDistance);
587 lodRef.maxDistance = systemData->maxDistance;
590 lodRef.planes[4] = glm::vec4(0, 0, 0, 1);
594 lodRef.planes[5] = glm::vec4(normalize(euclidean(modelFromView * glm::vec4(0, 0, -1, 1)) - lodRef.lodRef), 0.f);
595 lodRef.planes[5].w = -dot(glm::vec3(lodRef.planes[5]), lodRef.lodRef);
597 lodRef.planeCount = 6;
598 if (
const ClipShapeComponent* clipComp = assetInstance.clipShapeComponent.
resolveComponent<ClipShapeComponent>(); clipComp) {
599 const ClipShapeData& clipData = context->clipShapeSystem->getData(clipComp);
600 switch (clipData.shape) {
605 assert(clipData.planeCount == 6);
606 for (
size_t i = 0; i < clipData.planeCount; i++) {
607 lodRef.planes[lodRef.planeCount++] = clipData.planes[i] * worldFromModel;
612 assert(
false &&
"Unhandled case");
617 lodRef.maxLodDepth = systemData->maxLodDepth;
619 const glm::mat4 M = modelFromWorld * systemData->worldFromClip;
620 for (
size_t i = 0; i < 4; i++) {
621 glm::vec3 a = euclidean(M * glm::vec4((i & 1) ? -1.f : 1.f,
622 (i & 2) ? -1.f : 1.f,
624 glm::vec3 b = euclidean(M * glm::vec4((i & 1) ? -1.f : 1.f,
625 (i & 2) ? -1.f : 1.f,
627 float az = dot(lodRef.planes[5], glm::vec4(a, 1.f));
628 float bz = dot(lodRef.planes[5], glm::vec4(b, 1.f));
629 lodRef.frustumEdgesD[i] = (1.f / (bz - az)) * (b - a);
630 lodRef.frustumEdgesP[i] = a - az * lodRef.frustumEdgesD[i];
634 bool isOutside(AssetLodReference& lodReference,
const Geometry::BoundingBox & bbox)
636 float minDist, maxDist;
637 bool outsideOne =
false;
638 size_t fullyInside = 0;
639 for (
size_t j = 0; j < 6; j++) {
642 for (
size_t i = 0; i < 8; i++) {
643 glm::vec4 p((i & 1) ? bbox.min.x : bbox.max.x,
644 (i & 2) ? bbox.min.y : bbox.max.y,
645 (i & 4) ? bbox.min.z : bbox.max.z,
647 float t = dot(lodReference.planes[j], p);
648 minDist = std::min(minDist, t);
649 maxDist = std::max(maxDist, t);
660 size_t fullyInsideClip = 0;
661 bool outsideOneClip =
false;
662 for (
size_t j = 6; j < lodReference.planeCount; j++) {
664 float max = -FLT_MAX;
665 for (
size_t i = 0; i < 8; i++) {
666 glm::vec4 p((i & 1) ? bbox.min.x : bbox.max.x,
667 (i & 2) ? bbox.min.y : bbox.max.y,
668 (i & 4) ? bbox.min.z : bbox.max.z,
670 float t = dot(lodReference.planes[j], p);
671 min = std::min(min, t);
672 max = std::max(max, t);
675 outsideOneClip =
true;
682 if (lodReference.invertedClip ==
false) {
683 outsideOne = outsideOne || outsideOneClip;
684 fullyInside += fullyInsideClip;
687 if (6 + fullyInsideClip == lodReference.planeCount) {
695 lodReference.frustumCullOut++;
698 else if (fullyInside == lodReference.planeCount) {
700 lodReference.frustumCullIn++;
706 minDist = std::max(0.f, std::min(lodReference.maxDistance, minDist));
707 maxDist = std::max(0.f, std::min(lodReference.maxDistance, maxDist));
708 uint32_t planes = 63u;
709 for (
size_t i = 0; i < 4; i++) {
710 glm::vec3 pa = lodReference.frustumEdgesP[i] + minDist * lodReference.frustumEdgesD[i];
711 planes = planes & ((pa.x < bbox.min.x ? 1 : 0) |
712 (pa.y < bbox.min.y ? 2 : 0) |
713 (pa.z < bbox.min.z ? 4 : 0) |
714 (bbox.max.x < pa.x ? 8 : 0) |
715 (bbox.max.y < pa.y ? 16 : 0) |
716 (bbox.max.z < pa.z ? 32 : 0));
717 glm::vec3 pb = lodReference.frustumEdgesP[i] + maxDist * lodReference.frustumEdgesD[i];
718 planes = planes & ((pb.x < bbox.min.x ? 1 : 0) |
719 (pb.y < bbox.min.y ? 2 : 0) |
720 (pb.z < bbox.min.z ? 4 : 0) |
721 (bbox.max.x < pb.x ? 8 : 0) |
722 (bbox.max.y < pb.y ? 16 : 0) |
723 (bbox.max.z < pb.z ? 32 : 0));
726 lodReference.bboxCullOut++;
732 lodReference.frustumCullIn++;
736 void updateLodGroupDesiredLevel(AssetInstanceData & instance, AssetLodReference & lodReference, EntityInstanceData & lodInstance, uint32_t lodDepth,
const float tolerance)
738 auto & localBounds = instance.scene.getLocalBounds(lodInstance);
739 auto & lodData = instance.scene.getLodData(lodInstance);
740 auto & errors = lodData.errors;
743 lodData.previousTarget = lodInstance.lod.target;
745 if (!instance.visible) {
746 lodInstance.lod.target = ::NoValue;
750 if (!instance.selectedIdRanges.empty() && !lodData.ids.empty()) {
755 const std::vector<uint32_t>& selectedIds = instance.selectedIdRanges;
756 const size_t selectedCount = selectedIds.size() / 2;
758 const AssetInstanceVector<uint32_t>& lodIds = lodData.ids;
759 const size_t lodCount = lodIds.size() / 2;
765 bool overlap =
false;
766 for (
size_t j = 0; !overlap && j < selectedCount; j++) {
767 const uint32_t selectedMin = selectedIds[2 * j + 0];
768 const uint32_t selectedMax = selectedIds[2 * j + 1];
770 for (
size_t i = 0; !overlap && i < lodCount; i++) {
771 const uint32_t lodMin = lodIds[2 * i + 0];
772 const uint32_t lodMax = lodIds[2 * i + 1];
775 if (lodMin <= selectedMax && selectedMin <= lodMax) {
781 lodInstance.lod.target = ::NoValue;
786 const bool outside = isOutside(lodReference, localBounds);
788 lodInstance.lod.target = ::NoValue;
790 const uint32_t maxLodDepth = lodReference.maxLodDepth;
791 const size_t maxLocalLodDepth = maxLodDepth - std::min(maxLodDepth, lodDepth);
794 glm::vec3 nearest = glm::max(localBounds.min, glm::min(localBounds.max, lodReference.lodRef));
795 auto d = glm::max(lodReference.nearDistance, glm::distance(nearest, lodReference.lodRef));
797 auto error = d * tolerance;
799 const size_t N = errors.size();
800 const size_t M = N - 1;
801 const size_t O = M - std::min(M, maxLocalLodDepth);
804 lodInstance.lod.target = ::NoValue;
806 else if (lodInstance.lod.current == ::NoValue) {
808 lodInstance.lod.target =
static_cast<uint32_t
>(M);
813 for (; i < M && errors[i + 1] < error; i++) {}
814 lodInstance.lod.target =
static_cast<uint32_t
>(i);
820 while (i0 + 1 < i1) {
821 size_t m = (i0 + i1) / 2;
824 bool less = error < errors[m];
829 bool less = error < errors[i1];
832 lodInstance.lod.target =
static_cast<uint32_t
>(i0);
837 uint32_t findFirstLodChild(
const AssetDefinition & definition, uint32_t firstChild, int32_t targetLevel)
839 uint32_t childIndex = definition.scene.getChild(firstChild, 0);
841 for (int32_t i = 0; i < targetLevel; ++i) {
843 auto ccDef = &definition.scene[childIndex];
845 while (ccDef->nextLodSibling != ::NoValue) {
846 ccDef = &definition.scene[ccDef->nextLodSibling];
849 childIndex = ccDef->nextSibling;
855 void calculateModelCost(Context * , ModelInstanceData & modelData, Model * model)
857 modelData.cost = { 0, 0, 0 };
860 auto parts = modelData.part == ::NoValue ? std::span(model->parts) :
std::span(&model->parts[modelData.part], &model->parts[modelData.part] + 1);
862 for (
auto & part : parts) {
864 if (part.vertexCount == ::NoValue && part.meshIndex != ::NoValue) {
865 auto & mesh = model->meshes[part.meshIndex];
866 modelData.cost.primitiveCount += mesh->getCount() / 3;
868 modelData.cost.primitiveCount += part.vertexCount / 3;
871 modelData.cost.drawCalls += part.meshIndex != ::NoValue ? 1 : 0;
875#if ENABLE_COST_TRACKING
876 void calculateCost(Context* context, AssetInstanceData & instance, EntityInstanceData & entityInstance)
878 if (entityInstance.hasError())
return;
880 if (entityInstance.isLodGroup() && entityInstance.lod.current != ::NoValue) {
881 auto & lodData = instance.scene.getLodData(entityInstance);
882 uint32_t childIndex = lodData.lods[entityInstance.lod.current];
884 while (childIndex != ::NoValue) {
885 calculateCost(context, instance, instance.scene[childIndex]);
887 childIndex = instance.scene[childIndex].nextLodSibling;
889 }
else if (entityInstance.isAsset() && entityInstance.isSpawned()) {
890 auto & nestedInstance = *entityInstance.assetInstance;
891 nestedInstance.stats.current = { 0, 0, 0 };
892 for (
size_t i = 0; i < nestedInstance.scene.roots.size(); ++i) {
893 calculateCost(context, nestedInstance, nestedInstance.scene[nestedInstance.scene.roots[i]]);
895 instance.stats.current += nestedInstance.stats.current;
896 }
else if (entityInstance.isModel() && entityInstance.isSpawned()) {
897 auto & modelData = instance.scene.getModelData(entityInstance);
898 instance.stats.current += modelData.cost;
905#if ENABLE_COST_TRACKING
906 void estimateCost(Context* context, AssetInstanceData & instance, EntityInstanceData & entityInstance)
908 if (entityInstance.hasError())
return;
910 if (entityInstance.isLodGroup() && entityInstance.isVisible()) {
911 auto & lodData = instance.scene.getLodData(entityInstance);
912 uint32_t childIndex = lodData.lods[entityInstance.lod.target];
914 while (childIndex != ::NoValue) {
915 estimateCost(context, instance, instance.scene[childIndex]);
917 childIndex = instance.scene[childIndex].nextLodSibling;
919 }
else if (entityInstance.isAsset() && entityInstance.isSpawned()) {
920 auto & nestedInstance = *entityInstance.assetInstance;
921 nestedInstance.stats.projected = { 0, 0, 0 };
922 for (
size_t i = 0; i < nestedInstance.scene.roots.size(); ++i) {
923 estimateCost(context, nestedInstance, nestedInstance.scene[nestedInstance.scene.roots[i]]);
925 instance.stats.projected += nestedInstance.stats.projected;
926 }
else if (entityInstance.isModel()) {
929 instance.stats.projected.drawCalls += 1;
931 const uint64_t bogusEstimate = 10 * 1024;
932 instance.stats.projected.memory += bogusEstimate;
937 void updateAssetDesiredLevels(Context* context, AssetInstanceData& assetInstance, uint32_t lodDepth, uint32_t maxIterations);
939 void updateEntityDesiredLevels(Context* context, AssetInstanceData & instance, AssetLodReference & lodReference, EntityInstanceData & entityInstance, uint32_t lodDepth,
const float tolerance)
941 if (entityInstance.isLodGroup()) {
942 updateLodGroupDesiredLevel(instance, lodReference, entityInstance, lodDepth, tolerance);
944 if (entityInstance.isVisible() && entityInstance.isAtDesiredLod()) {
945 auto & lodData = instance.scene.getLodData(entityInstance);
946 uint32_t childIndex = lodData.lods[entityInstance.lod.target];
949 while (childIndex != ::NoValue) {
951 updateEntityDesiredLevels(context, instance, lodReference, instance.scene[childIndex], lodDepth + 1 + entityInstance.lod.target, tolerance);
953 childIndex = instance.scene[childIndex].nextLodSibling;
956 }
else if (entityInstance.isSpawned()) {
957 auto & definition = instance.asset->definition;
958 auto & entityDefinition = definition.scene[entityInstance.index];
960 if (entityInstance.isAsset()) {
961 entityInstance.assetInstance->tolerance = instance.tolerance;
962 entityInstance.assetInstance->forceTolerance = instance.forceTolerance;
963#if ENABLE_COST_TRACKING
964 entityInstance.assetInstance->stats.toleranceScale = instance.stats.toleranceScale;
966 entityInstance.assetInstance->visible = instance.visible;
968 entityInstance.assetInstance->stats.frustumCullIn = 0;
969 entityInstance.assetInstance->stats.frustumCullOut = 0;
970 entityInstance.assetInstance->stats.bboxCullOut = 0;
971 updateAssetDesiredLevels(context, *entityInstance.assetInstance, lodDepth, 1);
972 instance.stats.frustumCullIn += entityInstance.assetInstance->stats.frustumCullIn;
973 instance.stats.frustumCullOut += entityInstance.assetInstance->stats.frustumCullOut;
974 instance.stats.bboxCullOut += entityInstance.assetInstance->stats.bboxCullOut;
975 }
else if (entityInstance.hasChildren()) {
976 uint32_t childIndex = entityDefinition.firstChild;
978 while (childIndex != ::NoValue) {
979 updateEntityDesiredLevels(context, instance, lodReference, instance.scene[childIndex], lodDepth, tolerance);
981 childIndex = definition.scene[childIndex].nextSibling;
987#if ENABLE_COST_TRACKING
988 bool updateToleranceScale(AssetInstanceStats & stats)
990 const float currentToleranceScale = stats.toleranceScale;
991 const float frameToleranceMax = glm::min(currentToleranceScale + stats.toleranceScaleIncrement, stats.toleranceScaleMax);
992 const float frameToleranceMin = glm::max(currentToleranceScale - stats.toleranceScaleIncrement, stats.toleranceScaleMin);
994 if (stats.projected.memory > stats.max.memory) {
995 const float newToleranceScale = (0.9f * stats.max.memory) / glm::max((
decltype(stats.current.memory))1, stats.current.memory) * stats.toleranceScale;
996 stats.toleranceScale = glm::clamp(newToleranceScale, frameToleranceMin, frameToleranceMax);
998 }
else if (stats.projected.memory < 0.8f * stats.max.memory) {
999 if (stats.current.memory != 0) {
1000 const float targetToleranceScale = stats.max.memory / glm::max((
decltype(stats.current.memory))1, stats.current.memory) * currentToleranceScale;
1001 stats.toleranceScale = glm::clamp(targetToleranceScale, frameToleranceMin, frameToleranceMax);
1003 stats.toleranceScale = frameToleranceMax;
1013 void updateAssetDesiredLevels(Context* context, AssetInstanceData & assetInstance, uint32_t lodDepth, uint32_t maxIterations)
1015 CpuInstrumentationScope(SCOPE_ASSETSYSTEM,
"AssetSystem::updateAssetDesiredLevels");
1016 auto lodTime = assetInstance.stats.frame.startLodCalculation();
1018 AssetLodReference lodReference = {};
1019 getLodReference(context, assetInstance, lodReference, assetInstance.container);
1021 auto & scene = assetInstance.scene;
1023 float tolerance = assetInstance.tolerance;
1024#if ENABLE_COST_TRACKING
1025 uint64_t prevIteration = assetInstance.stats.projected.memory;
1027#if ENABLE_COST_TRACKING
1028 for (uint32_t it = 0; it < maxIterations; it++) {
1029 float clampedTolerance = glm::max(1e-5f, (1.f / assetInstance.stats.toleranceScale) * tolerance);
1031 if (maxIterations > 0) {
1032 float clampedTolerance = tolerance;
1034 if (assetInstance.forceTolerance) clampedTolerance = assetInstance.tolerance;
1036 for (
auto & root : scene.roots) {
1037 updateEntityDesiredLevels(context, assetInstance, lodReference, scene[root], lodDepth, clampedTolerance);
1040 assetInstance.stats.frustumCullIn += lodReference.frustumCullIn;
1041 assetInstance.stats.frustumCullOut += lodReference.frustumCullOut;
1042 assetInstance.stats.bboxCullOut += lodReference.bboxCullOut;
1044#if ENABLE_COST_TRACKING
1045 if (assetInstance.parentInstance) {
1050 CpuInstrumentationScope(SCOPE_ASSETSYSTEM,
"AssetSystem::estimateCost");
1052 assetInstance.stats.projected = { 0, 0, 0 };
1053 for (
size_t i = 0; i < scene.roots.size(); ++i) {
1054 estimateCost(context, assetInstance, scene[scene.roots[i]]);
1058 if (assetInstance.stats.projected.memory == prevIteration) {
1061 prevIteration = assetInstance.stats.projected.memory;
1064 if (!updateToleranceScale(assetInstance.stats)) {
1071 void clearEntity(Context * context, AssetInstanceData & assetInstance, EntityInstanceData & entityInstance)
1073 if (entityInstance.entity) {
1074 assetInstance.destroyed(entityInstance.entity->getId());
1077 entityInstance.entity = {};
1078 entityInstance.setSpawned(
false);
1079 entityInstance.setReady(
false);
1081 if (entityInstance.isLodGroup()) {
1082 entityInstance.lod.current = ::NoValue;
1085 if (entityInstance.isStaging()) {
1086 entityInstance.setStaging(
false);
1089 if (entityInstance.hasChildren()) {
1090 auto & definition = assetInstance.asset->definition;
1091 auto & entityDefinition = definition.scene[entityInstance.index];
1093 for (uint32_t i = 0; i < entityDefinition.numChildren; ++i) {
1094 uint32_t childIndex = definition.scene.getChild(entityDefinition.firstChild, i);
1096 clearEntity(context, assetInstance, assetInstance.scene[childIndex]);
1098 }
else if (entityInstance.isAsset() && entityInstance.assetInstance) {
1099 for (
auto & instance : entityInstance.assetInstance->scene.entities) {
1100 clearEntity(context, *entityInstance.assetInstance, instance);
1103 entityInstance.assetInstance->destroy();
1104 entityInstance.assetInstance =
nullptr;
1105 }
else if (entityInstance.isModel()) {
1106 auto & modelData = assetInstance.scene.getModelData(entityInstance);
1113 if (modelData.requested) {
1114 auto & modelFile = assetInstance.scene.modelFiles[modelData.fileIndex];
1116 modelData.requested =
false;
1121 bool checkEntity(Context * context, AssetLodReference& lodReference, AssetInstanceData & assetInstance, EntityInstanceData & entityInstance, EntityInstanceData * parentInstance)
1123 if (entityInstance.hasError())
return true;
1125 if (cullModelsValue && entityInstance.isModel()) {
1126 AssetDefinition& assetDef = assetInstance.asset->definition;
1127 SceneEntityDefinition& entityDef = assetDef.scene[entityInstance.index];
1128 const Cogs::Geometry::BoundingBox& bbox = assetInstance.scene.boxes[entityDef.index];
1129 if (!isEmpty(bbox) && isOutside(lodReference, bbox)) {
1130 ModelInstanceData& modelData = assetInstance.scene.getModelData(entityInstance);
1131 if (entityInstance.isSpawned() || entityInstance.isReady() || modelData.requested) {
1132 clearEntity(context, assetInstance, entityInstance);
1138 if (entityInstance.isReady())
return true;
1140 if (entityInstance.isModel()) {
1141 auto& modelData = assetInstance.scene.getModelData(entityInstance);
1142 auto& modelFile = assetInstance.scene.modelFiles[modelData.fileIndex];
1144 constexpr uint32_t kPriorityPerLevel = 1000;
1145 constexpr uint32_t kMaxInstancePriority = 100000;
1147 if (!modelData.requested) {
1148 if (!modelFile.requested) {
1149 AssetModelRequest* request = assetInstance.systemData->requestPool.create();
1150 request->assetInstance = &assetInstance;
1151 request->modelFileIndex = modelData.fileIndex;
1152 request->path = modelFile.path;
1153 request->priority += (uint32_t)((
float)kMaxInstancePriority * assetInstance.priority);
1154 modelFile.request = request;
1155 assetInstance.systemData->pendingRequests.emplace_back(request);
1156 ++assetInstance.stats.frame.modelsRequested;
1157 ++assetInstance.stats.numRequests;
1159 modelFile.requested =
true;
1162 if (modelFile.request) {
1163 modelFile.request->priority += modelData.depth * kPriorityPerLevel;
1168 modelData.requested =
true;
1171 if (modelFile.ready) {
1172 entityInstance.setReady(
true);
1176 if (!modelFile.model)
return false;
1178 if (!modelFile.model->isLoaded())
return false;
1180 for (
auto & mesh : modelFile.model->meshes) {
1181 if (!mesh->isInitialized() && mesh->getCount() == 0)
continue;
1182 if (!mesh->hasAttachedResource())
return false;
1185 if (!assetInstance.useOverrideMaterial() && !assetInstance.useCloneMaterial()) {
1186 for (
auto & material : modelFile.model->materials) {
1187 if (!material->isActive())
return false;
1191 modelFile.setReady();
1193 entityInstance.setReady(
true);
1196 context->engine->setDirty();
1198 }
else if (entityInstance.isAsset()) {
1199 auto & definition = assetInstance.asset->definition;
1200 auto & entityDefinition = definition.scene[entityInstance.index];
1202 if (!entityInstance.assetInstance) {
1203 entityInstance.assetInstance = assetInstance.systemData->instanceData.create();
1204 entityInstance.assetInstance->link(&assetInstance);
1205 entityInstance.assetInstance->container = parentInstance->entityPtr;
1206 entityInstance.assetInstance->objectId = assetInstance.objectId;
1207 entityInstance.assetInstance->renderLayers = assetInstance.renderLayers;
1208 entityInstance.assetInstance->clipShapeComponent = assetInstance.clipShapeComponent;
1211 if (!entityInstance.assetInstance->initialized) {
1214 const PropertyInfo& pathItem = definition.scene.properties.getPropertyByIndex(entityDefinition.asset.index);
1215 if (pathItem.type == PropertyType::UnsignedInteger) {
1216 path = createAssetResourcePathFromIndex(pathItem.uintValue, AssetResourceType::Asset);
1218 else if (pathItem.type == PropertyType::String) {
1219 path = definition.scene.properties.getString(entityDefinition.asset.index).to_string();
1222 if (assetInstance.useRelativePaths() && IO::isRelative(path)) {
1223 path = IO::combine(assetInstance.directoryPath, std::move(path));
1226 entityInstance.assetInstance->asset = context->assetManager->loadAsset(path, NoResourceId,
AssetLoadFlags::None);
1228 entityInstance.assetInstance->initialized =
true;
1230 if (assetInstance.onDemand) {
1231 entityInstance.assetInstance->onDemand =
true;
1234 if (assetInstance.useOverrideMaterial()) {
1235 entityInstance.assetInstance->overrideMaterial =
true;
1236 entityInstance.assetInstance->material = assetInstance.material;
1238 if (assetInstance.useCloneMaterial()) {
1239 entityInstance.assetInstance->cloneMaterial =
true;
1240 entityInstance.assetInstance->material = assetInstance.material;
1244 if (!entityInstance.assetInstance->asset->isLoaded()) {
1248 if (!entityInstance.assetInstance->scene.initialized) {
1249 auto flags = (
AssetFlags)entityDefinition.asset.flags;
1253 initializeAssetInstance(*entityInstance.assetInstance);
1257 for (
const uint32_t rootIx : entityInstance.assetInstance->scene.roots) {
1258 ready &= checkEntity(context, lodReference, *entityInstance.assetInstance, entityInstance.assetInstance->scene[rootIx], parentInstance);
1260 entityInstance.setReady(ready);
1263 context->engine->setDirty();
1266 }
else if (entityInstance.isLodGroup()) {
1267 auto & definition = assetInstance.asset->definition;
1268 auto & lodData = assetInstance.scene.getLodData(entityInstance);
1270 entityInstance.lod.target = uint32_t(lodData.lods.size()) - 1;
1271 uint32_t childIndex = entityInstance.lod.current != ::NoValue ? entityInstance.lod.current : lodData.lods.back();
1274 while (childIndex != ::NoValue) {
1275 retval &= checkEntity(context, lodReference, assetInstance, assetInstance.scene[childIndex], parentInstance);
1276 childIndex = definition.scene[childIndex].nextLodSibling;
1279 }
else if (entityInstance.hasChildren()) {
1280 auto & definition = assetInstance.asset->definition;
1281 auto & entityDefinition = definition.scene[entityInstance.index];
1283 uint32_t childIndex = entityDefinition.firstChild;
1285 while (childIndex != ::NoValue) {
1286 if (!checkEntity(context, lodReference, assetInstance, assetInstance.scene[childIndex], parentInstance)) {
1290 childIndex = definition.scene[childIndex].nextSibling;
1297 bool spawnEntity(Context * context, AssetLodReference& lodReference, AssetInstanceData & assetInstance, EntityInstanceData & entityInstance, EntityInstanceData * parentInstance, EntityParentMode parentMode);
1299 bool spawnLodGroup(Context * context, AssetLodReference& lodReference, AssetInstanceData & assetInstance, EntityInstanceData & entityInstance, EntityInstanceData * parentInstance, EntityParentMode )
1301 assert(entityInstance.isLodGroup() &&
"Invalid LoD group instance.");
1303 if (entityInstance.isAtDesiredLod())
return false;
1305 auto & scene = assetInstance.scene;
1306 auto & lodData = scene.getLodData(entityInstance);
1308 if (lodData.previousTarget != ::NoValue && lodData.previousTarget != entityInstance.lod.target && lodData.previousTarget != entityInstance.lod.current) {
1309 uint32_t currentLodChild = lodData.lods[lodData.previousTarget];
1311 while (currentLodChild != ::NoValue) {
1312 clearEntity(context, assetInstance, scene[currentLodChild]);
1313 currentLodChild = scene[currentLodChild].nextLodSibling;
1316 lodData.previousTarget = ::NoValue;
1319 if (!entityInstance.isVisible()) {
1320 clearEntity(context, assetInstance, entityInstance);
1324 if (entityInstance.lod.target != ::NoValue) {
1325 uint32_t currentLodChild = lodData.lods[entityInstance.lod.target];
1327 while (currentLodChild != ::NoValue) {
1328 scene[currentLodChild].setStaging(
false);
1329 currentLodChild = scene[currentLodChild].nextLodSibling;
1333 const uint32_t firstChildIndex = lodData.lods[entityInstance.lod.target];
1336 uint32_t childIndex = firstChildIndex;
1337 while (childIndex != ::NoValue) {
1338 auto & childInstance = scene[childIndex];
1340 childInstance.setStaging(
true);
1343 ready &= checkEntity(context, lodReference, assetInstance, childInstance, parentInstance);
1345 childIndex = childInstance.nextLodSibling;
1348 if (!ready)
return false;
1350 if (entityInstance.lod.current != ::NoValue) {
1351 uint32_t currentLodChild = lodData.lods[entityInstance.lod.current];
1353 while (currentLodChild != ::NoValue) {
1354 clearEntity(context, assetInstance, scene[currentLodChild]);
1355 currentLodChild = scene[currentLodChild].nextLodSibling;
1359 auto currentParent = parentInstance;
1361 childIndex = firstChildIndex;
1362 while (childIndex != ::NoValue) {
1363 spawnEntity(context, lodReference, assetInstance, scene[childIndex], currentParent, EntityParentMode::TransformOnly);
1365 childIndex = scene[childIndex].nextLodSibling;
1368 entityInstance.lod.current = entityInstance.lod.target;
1373 bool spawnAsset(Context * context, AssetLodReference& lodReference, EntityInstanceData & entityInstance, EntityInstanceData * parentInstance)
1375 if (entityInstance.isSpawned())
return false;
1376 if (!entityInstance.isReady())
return false;
1378 auto & assetInstance = *entityInstance.assetInstance;
1380 EntityInstanceData root;
1381 root.entityPtr = parentInstance->entityPtr;
1383 for (
auto & subroot : assetInstance.scene.roots) {
1384 spawnEntity(context, lodReference, assetInstance, assetInstance.scene[subroot], parentInstance, EntityParentMode::TransformOnly);
1387 entityInstance.setSpawned(
true);
1388 entityInstance.setStaging(
false);
1393 bool spawnModel(Context * context, AssetInstanceData & assetInstance, EntityInstanceData & entityInstance, EntityInstanceData * parentInstance, EntityParentMode parentMode)
1395 if (entityInstance.isSpawned())
return false;
1396 if (!entityInstance.isReady())
return false;
1398 if (entityInstance.hasError()) {
1399 entityInstance.setSpawned(
true);
1403 auto & modelData = assetInstance.scene.getModelData(entityInstance);
1404 assert(modelData.index == entityInstance.index &&
"Model data attached to invalid entity instance.");
1405 auto & modelFile = assetInstance.scene.modelFiles[modelData.fileIndex];
1407 auto model = modelFile.model.resolve();
1409 if (!model->isLoaded())
return false;
1410 if (model->meshes.empty())
return false;
1412 auto & scene = assetInstance.asset->definition.scene;
1413 auto & entityDefinition = scene[entityInstance.index];
1415 EntityCreationContext entityContext{};
1416 entityContext.context = context;
1417 entityContext.scene = &assetInstance.asset->definition.scene;
1418 entityContext.parent = parentInstance->entityPtr;
1419 entityContext.parentMode = parentMode;
1420 entityContext.skipModel =
true;
1422 entityInstance.entity = createEntity(entityContext, entityDefinition);
1424 assetInstance.spawned(entityInstance.entity->getId());
1426 auto parentTransform = entityInstance.entity->getComponentHandle<TransformComponent>();
1427 auto parentSceneComponent = entityInstance.entity->getComponentHandle<SceneComponent>();
1429 size_t begin = modelData.part != ::NoValue ? modelData.part : 0;
1430 size_t end = modelData.part != ::NoValue ? modelData.part + 1 : model->parts.size();
1431 assert(end > begin &&
"Invalid part range");
1432 size_t count = end - begin;
1433 assert(count &&
"Invalid part range.");
1435 std::vector<EntityPtr> entities(count);
1436 context->store->createEntities(count, entities);
1438 std::vector<ComponentModel::ComponentHandle> transformComponents(count);
1439 std::vector<ComponentModel::ComponentHandle> sceneComponents(count);
1441 for (
size_t i = 0; i < count; ++i) {
1442 auto & part = model->parts[begin + i];
1445 auto sceneComponent = context->sceneSystem->createComponent();
1446 sceneComponents[i] = sceneComponent;
1447 entity->addComponent(sceneComponent);
1449 auto tcHandle = context->transformSystem->createComponent();
1450 transformComponents[i] = tcHandle;
1451 entity->addComponent(tcHandle);
1452 auto transformComponent = tcHandle.resolveComponent<TransformComponent>();
1454 context->transformSystem->setLocalTransform(transformComponent, model->getPartTransform(part));
1456 if (part.parentIndex == ::NoValue) {
1457 transformComponent->parent = parentTransform;
1458 parentSceneComponent.resolveComponent<SceneComponent>()->children.emplace_back(entity);
1460 if (part.parentIndex < begin || end <= part.parentIndex) {
1461 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());
1464 const ComponentHandle& childParentSceneComponent = sceneComponents[part.parentIndex - begin];
1465 childParentSceneComponent.
resolveComponent<SceneComponent>()->children.emplace_back(entity);
1466 transformComponent->parent = transformComponents[part.parentIndex - begin];
1470 transformComponent->setChanged();
1472 if (part.meshIndex != NoIndex) {
1473 auto mcHandle = context->meshSystem->createComponent();
1474 entity->addComponent(mcHandle);
1476 auto meshComponent = mcHandle.resolveComponent<MeshComponent>();
1477 meshComponent->meshHandle = model->meshes[part.meshIndex];
1478 meshComponent->setChanged();
1480 auto mrcHandle = context->renderSystem->createComponent();
1481 entity->addComponent(mrcHandle);
1483 auto renderComponent = mrcHandle.resolveComponent<MeshRenderComponent>();
1484 if (part.boundsIndex != NoIndex) {
1485 context->renderSystem->setLocalBounds(renderComponent, model->bounds[part.boundsIndex]);
1487 renderComponent->startIndex = part.startIndex;
1488 renderComponent->vertexCount = part.vertexCount;
1489 renderComponent->objectId = assetInstance.objectId;
1490 renderComponent->layer = assetInstance.renderLayers;
1491 renderComponent->clipShapeComponent = assetInstance.clipShapeComponent;
1493 MaterialInstanceHandle partMaterial = part.materialIndex < model->materials.size()
1494 ? model->materials[part.materialIndex]
1496 if (assetInstance.useOverrideMaterial()) {
1497 renderComponent->material = assetInstance.material;
1499 else if(assetInstance.useCloneMaterial()){
1500 MaterialInstanceHandle master = assetInstance.material;
1501 renderComponent->
material = context->materialInstanceManager->createMaterialInstance(context->materialManager->generateHandle(master->material));
1502 renderComponent->material->clone(master.resolve());
1505 renderComponent->material->setPermutation(partMaterial->getPermutation());
1506 renderComponent->material->instanceFlags = partMaterial->instanceFlags;
1507 renderComponent->material->
options = partMaterial->options;
1508 renderComponent->material->cloneMatchingProperties(partMaterial.resolve());
1509 renderComponent->material->cloneMatchingVariants(partMaterial.resolve());
1513 renderComponent->material = partMaterial;
1516 MaterialHandle master = context->materialManager->getDefaultMaterial();
1517 renderComponent->material = context->materialInstanceManager->createMaterialInstance(master);
1519 renderComponent->setChanged();
1522 assetInstance.spawned(entity->getId());
1525 calculateModelCost(context, modelData, model);
1527 entityInstance.setSpawned(
true);
1528 entityInstance.setStaging(
false);
1533 bool spawnEntity(Context * context, AssetLodReference& lodReference, AssetInstanceData & assetInstance, EntityInstanceData & entityInstance, EntityInstanceData * parentInstance, EntityParentMode parentMode)
1535 auto & definition = assetInstance.asset->definition;
1536 auto & scene = assetInstance.scene;
1537 auto & entityDefinition = definition.scene[entityInstance.index];
1539 if (entityDefinition.isLodGroup()) {
1540 return spawnLodGroup(context, lodReference, assetInstance, entityInstance, parentInstance, parentMode);
1541 }
else if (entityDefinition.isAsset()) {
1542 return spawnAsset(context, lodReference, entityInstance, parentInstance);
1543 }
else if (entityDefinition.isModel()) {
1544 return spawnModel(context, assetInstance, entityInstance, parentInstance, parentMode);
1546 if (entityDefinition.isEmpty()) {
1547 entityInstance.setSpawned(
true);
1551 EntityCreationContext entityContext{};
1552 entityContext.context = context;
1553 entityContext.scene = &definition.scene;
1554 entityContext.parent = parentInstance->entityPtr;
1555 entityContext.parentMode = parentMode;
1557 entityInstance.entity = createEntity(entityContext, entityDefinition);
1558 entityInstance.entityPtr = entityInstance.entity.get();
1559 entityInstance.setSpawned(
true);
1560 entityInstance.setStaging(
false);
1562 assetInstance.spawned(entityInstance.entity->getId());
1564 auto rHandle = entityInstance.entity->getComponentHandle<MeshRenderComponent>();
1566 auto renderComponent = rHandle.resolveComponent<MeshRenderComponent>();
1567 if(renderComponent){
1568 renderComponent->material = assetInstance.material;
1572 if (entityDefinition.numChildren) {
1573 uint32_t childIndex = entityDefinition.firstChild;
1575 while (childIndex != ::NoValue) {
1576 checkEntity(context, lodReference, assetInstance, scene[childIndex], parentInstance);
1578 spawnEntity(context, lodReference, assetInstance, scene[childIndex], &entityInstance, EntityParentMode::Default);
1580 childIndex = definition.scene[childIndex].nextSibling;
1588 void updateEntity(Context * context, AssetLodReference& lodReference, AssetInstanceData & assetInstance, EntityInstanceData & entityInstance, EntityInstanceData * parentInstance)
1590 auto & scene = assetInstance.scene;
1592 if (entityInstance.isLodGroup()) {
1593 if (!entityInstance.isAtDesiredLod()) {
1594 spawnEntity(context, lodReference, assetInstance, entityInstance, parentInstance, EntityParentMode::TransformOnly);
1595 }
else if (entityInstance.isVisible()) {
1596 auto & lodData = scene.getLodData(entityInstance);
1597 uint32_t childIndex = lodData.lods[entityInstance.lod.target];
1599 while (childIndex != ::NoValue) {
1600 updateEntity(context, lodReference, assetInstance, scene[childIndex], parentInstance);
1602 childIndex = scene[childIndex].nextLodSibling;
1605 }
else if (entityInstance.isSpawned()) {
1606 if (entityInstance.isAsset()) {
1607 entityInstance.assetInstance->priority = assetInstance.priority;
1608 for (
auto & root : entityInstance.assetInstance->scene.roots) {
1609 updateEntity(context, lodReference, *entityInstance.assetInstance, entityInstance.assetInstance->scene[root], parentInstance);
1611 }
else if (entityInstance.hasChildren()) {
1612 auto & definition = assetInstance.asset->definition;
1613 auto & entityDefinition = definition.scene[entityInstance.index];
1615 for (uint32_t i = 0; i < entityDefinition.numChildren; ++i) {
1616 auto & childDefinition = definition.scene[definition.scene.getChild(entityDefinition.firstChild, i)];
1618 updateEntity(context, lodReference, assetInstance, scene[childDefinition.index], &entityInstance);
1621 }
else if (entityInstance.isStaging()) {
1622 if (entityInstance.isAsset() || entityInstance.isModel()) {
1623 if (checkEntity(context, lodReference, assetInstance, entityInstance, parentInstance)) {
1624 spawnEntity(context, lodReference, assetInstance, entityInstance, parentInstance, EntityParentMode::TransformOnly);
1627 spawnEntity(context, lodReference, assetInstance, entityInstance, parentInstance, EntityParentMode::TransformOnly);
1630 if (!entityInstance.isSpawned()) {
1631 if (checkEntity(context, lodReference, assetInstance, entityInstance, parentInstance)) {
1632 spawnEntity(context, lodReference, assetInstance, entityInstance, parentInstance, EntityParentMode::TransformOnly);
1638 void dispatchMessages(Context * context, AssetInstanceData & assetInstance)
1640 CpuInstrumentationScope(SCOPE_ASSETSYSTEM,
"AssetSystem::dispatch");
1641 auto dispatchTime =assetInstance.stats.frame.startDispatch();
1643 auto systemData = assetInstance.systemData;
1649 for (EntityId spawned : assetInstance.spawnedEntities) {
1650 context->dynamicComponentSystem->sendMessage(assetInstance.container, systemData->entityCreatedId, (
void *)spawned);
1653 for (EntityId destroyed : assetInstance.destroyedEntities) {
1654 context->dynamicComponentSystem->sendMessage(assetInstance.container, systemData->entityDestroyedId, (
void *)destroyed);
1657 assetInstance.spawnedEntities.clear();
1658 assetInstance.destroyedEntities.clear();
1661 void processInFlightRequests(Context* context, AssetSystemData * systemData)
1663 CpuInstrumentationScope(SCOPE_ASSETSYSTEM,
"AssetSystem::processInFlightRequests");
1665 for (
auto request : systemData->inFlightRequests) {
1666 if (request->canceled) {
1667 context->modelManager->cancelModelLoad(request->model);
1668 systemData->requestPool.destroy(request);
1672 if (request->model && request->model->isLoaded()) {
1673 auto assetInstance = request->assetInstance;
1675 auto & modelFile = assetInstance->scene.modelFiles[request->modelFileIndex];
1676 modelFile.model = request->model;
1677 modelFile.request =
nullptr;
1679 --assetInstance->stats.numRequests;
1681 systemData->requestPool.destroy(request);
1683 systemData->stillInFlight.emplace_back(request);
1687 std::swap(systemData->stillInFlight, systemData->inFlightRequests);
1688 systemData->stillInFlight.clear();
1691 size_t processPendingRequests(Context* context,
1692 AssetSystemData* systemData,
1693 ModelManager* modelManager,
1694 AssetSystemVector<AssetModelRequest*>& pendingRequests,
1695 AssetSystemVector<AssetModelRequest*>& inFlightRequests,
1696 const size_t maxInFlight,
1697 const size_t maxProcessed)
1699 CpuInstrumentationScope(SCOPE_ASSETSYSTEM,
"AssetSystem::processPendingRequests");
1701 size_t processed = 0;
1703 if (!pendingRequests.empty()) {
1704 std::sort(pendingRequests.begin(), pendingRequests.end(), [](
auto a,
auto b) ->
bool
1706 return a->priority < b->priority;
1710 while (!pendingRequests.empty() && processed < maxProcessed && inFlightRequests.size() < maxInFlight) {
1711 auto request = pendingRequests.back();
1712 pendingRequests.pop_back();
1714 if (systemData && request->canceled) {
1715 LOG_TRACE(logger,
"Discarding cancelled pending request");
1716 systemData->requestPool.destroy(request);
1720 auto assetInstance = request->assetInstance;
1723 if (assetInstance && assetInstance->useOverrideMaterial()) {
1726 if (assetInstance && assetInstance->useCloneMaterial()) {
1730 request->model = modelManager ? modelManager->loadModel(request->path, NoResourceId, flags) : ModelHandle{};
1732 inFlightRequests.emplace_back(request);
1737 while (systemData && maxInFlight < inFlightRequests.size()) {
1738 LOG_TRACE(logger,
"Cancelling in-flight request and moving it to pending as limits are violated.");
1740 AssetModelRequest* request = inFlightRequests.back();
1741 inFlightRequests.pop_back();
1743 if (request->model) {
1744 context->modelManager->cancelModelLoad(request->model);
1745 request->model = ModelHandle{};
1748 pendingRequests.emplace_back(request);
1754 void pullCameraMatrices(Context* context, AssetSystemData* systemData)
1756 if (CameraComponent* mainCam = context->cameraSystem->getMainCamera(); mainCam) {
1757 if (TransformComponent* trCompLod = mainCam->getComponent<TransformComponent>(); trCompLod) {
1759 if (trCompLod->hasChanged()) {
1760 context->transformSystem->updateTransformData(*trCompLod);
1762 const CameraData& camData = context->cameraSystem->getData(mainCam);
1764 systemData->worldFromView = context->transformSystem->getLocalToWorld(trCompLod);
1765 systemData->worldFromClip = systemData->worldFromView * glm::inverse(camData.rawProjectionMatrix);
1767 systemData->viewFromWorld = glm::inverse(systemData->worldFromView);
1768 systemData->clipFromWorld = camData.rawProjectionMatrix * systemData->viewFromWorld;
1770 systemData->minDistance = mainCam->enableClippingPlaneAdjustment ? mainCam->nearPlaneLimit : mainCam->nearDistance;
1771 systemData->maxDistance = context->variables->get(maxDistanceVariableName, kDefaultMaxDistance);
1773 if (
int maxLodDepth = context->variables->get(maxLodDepthVariableName, kDefaultMaxLodDepth); 0 <= maxLodDepth) {
1774 systemData->maxLodDepth = maxLodDepth;
1777 systemData->maxLodDepth = ~0u;
1782 systemData->worldFromView = glm::mat4(1.f);
1783 systemData->worldFromClip = glm::mat4(1.f);
1784 systemData->viewFromWorld = glm::mat4(1.f);
1785 systemData->clipFromWorld = glm::mat4(1.f);
1788 void updateRenderPropertiesRecurse(
const AssetInstanceData& instanceData,
Entity* entity) {
1790 if (RenderComponent* renderComp = entity->
getComponent<RenderComponent>(); renderComp) {
1791 renderComp->layer = instanceData.renderLayers;
1792 renderComp->objectId = instanceData.objectId;
1793 renderComp->clipShapeComponent = instanceData.clipShapeComponent;
1794 renderComp->setChanged();
1797 if (SceneComponent* sceneComp = entity->
getComponent<SceneComponent>(); sceneComp) {
1798 for (
EntityPtr& e : sceneComp->children) {
1799 updateRenderPropertiesRecurse(instanceData, e.get());
1805 void updateInstancedModelChildEntityRenderProperties(Context* context, AssetInstanceData& assetInstance,
Entity& entity)
1807 if (RenderComponent* renderComp = entity.
getComponent<RenderComponent>(); renderComp) {
1808 renderComp->objectId = assetInstance.objectId;
1809 renderComp->layer = assetInstance.renderLayers;
1810 renderComp->clipShapeComponent = assetInstance.clipShapeComponent;
1811 renderComp->setChanged();
1814 if (SceneComponent* sceneComp = entity.
getComponent<SceneComponent>(); sceneComp) {
1815 for (
EntityPtr& child : sceneComp->children) {
1817 updateInstancedModelChildEntityRenderProperties(context, assetInstance, *child.get());
1822 void updateAssetInstanceRenderProperties(Context* context, AssetInstanceData& assetInstance);
1824 void updateEntityInstanceRenderProperties(Context* context, AssetInstanceData& assetInstance, EntityInstanceData& entityInstance)
1826 if (entityInstance.isLodGroup()) {
1828 if (entityInstance.isVisible() && entityInstance.isAtDesiredLod()) {
1829 LodInstanceData& lodData = assetInstance.scene.getLodData(entityInstance);
1830 uint32_t childIndex = lodData.lods[entityInstance.lod.target];
1831 while (childIndex != ::NoValue) {
1832 updateEntityInstanceRenderProperties(context, assetInstance, assetInstance.scene[childIndex]);
1833 childIndex = assetInstance.scene[childIndex].nextLodSibling;
1839 else if (entityInstance.isSpawned()) {
1841 const AssetDefinition& definition = assetInstance.asset->definition;
1842 const SceneEntityDefinition& entityDefinition = definition.scene[entityInstance.index];
1844 if (entityInstance.isAsset()) {
1845 entityInstance.assetInstance->clipShapeComponent = assetInstance.clipShapeComponent;
1846 updateAssetInstanceRenderProperties(context, *entityInstance.assetInstance);
1849 else if (entityInstance.isModel()) {
1850 if (entityInstance.entity) {
1851 updateInstancedModelChildEntityRenderProperties(context, assetInstance, *entityInstance.entity.get());
1855 else if (entityInstance.hasChildren()) {
1857 uint32_t childIndex = entityDefinition.firstChild;
1858 while (childIndex != ::NoValue) {
1859 updateEntityInstanceRenderProperties(context, assetInstance, assetInstance.scene[childIndex]);
1860 childIndex = definition.scene[childIndex].nextSibling;
1868 void updateAssetInstanceRenderProperties(Context* context, AssetInstanceData& assetInstance)
1870 for (uint32_t root : assetInstance.scene.roots) {
1871 updateEntityInstanceRenderProperties(context, assetInstance, assetInstance.scene[root]);
1882 for (
auto& assetComponent : assetSystem->pool) {
1883 const auto bbox = assetSystem->getWorldBounds(&assetComponent,
false);
1892 if (!assetComponent)
return false;
1893 const auto bbox = context->assetSystem->getWorldBounds(assetComponent, ignoreVisibility);
1894 if (!isEmpty(bbox)) {
1905Cogs::Core::AssetSystem::~AssetSystem() =
default;
1909 this->context = context;
1911 systemData = std::make_unique<AssetSystemData>();
1915 systemData->entityCreatedId = context->dynamicComponentSystem->registerMessage(
"entityCreated");
1916 systemData->entityDestroyedId = context->dynamicComponentSystem->registerMessage(
"entityDestroyed");
1918 context->
variables->getOrAdd(lodFreezeVariableName, kDefaultLodFreeze);
1919 context->
variables->getOrAdd(cullModelsName, cullModelsValue);
1920 if (
Variable* var = context->
variables->get(maxModelsInFlightVariableName); var->isEmpty()) {
1921 context->
variables->set(maxModelsInFlightVariableName, kDefaultMaxModelsInFlight);
1923 if (
Variable* var = context->
variables->get(maxModelLoadsPerFrameVariableName); var->isEmpty()) {
1924 context->
variables->set(maxModelLoadsPerFrameVariableName, kDefaultMaxModelLoadsPerFrame);
1926 if (
Variable* var = context->
variables->get(maxDistanceVariableName); var->isEmpty()) {
1927 context->
variables->set(maxDistanceVariableName, kDefaultMaxDistance);
1929 if (
Variable* var = context->
variables->get(maxLodDepthVariableName); var->isEmpty()) {
1930 context->
variables->set(maxLodDepthVariableName, kDefaultMaxLodDepth);
1932 context->
variables->getOrAdd(allowMaterialOverride, kDefaultAllowMaterialOverride);
1938 const float qualityScale = context->
qualityService->assetSystemToleranceScale;
1940 bool freeze = context->
variables->get(lodFreezeVariableName, kDefaultLodFreeze);
1942 if (
const Variable* var = context->
variables->get(cullModelsName); !var->isEmpty()) {
1943 cullModelsValue = var->getBool();
1946 pullCameraMatrices(context, systemData.get());
1948 bool allowOverrideMaterial = context->
variables->get(allowMaterialOverride,
true);
1954 AssetSystemVector<AssetUpdate>& newUpdates = systemData->newUpdates; assert(newUpdates.empty());
1955 AssetSystemVector<AssetInstanceData*>& currentOnDemandInstances = systemData->currentOnDemandInstances; assert(currentOnDemandInstances.empty());
1956 for (
auto & assetComponent : pool) {
1960 AssetData& assetData = getData<AssetData>(&assetComponent);
1963 if (assetData.hasChanged(assetComponent.asset)) {
1964 assetData.setAsset(assetComponent.asset);
1968 if (!assetData.instanceData) {
1969 auto instanceData = systemData->instanceData.create();
1970 assetData.instanceData = std::shared_ptr<AssetInstanceData>(instanceData, [&](
AssetInstanceData * d)
1972 auto current = d->next;
1974 auto next = current->next;
1975 systemData->instanceData.destroy(current);
1978 systemData->instanceData.destroy(d);
1986 assetInstance->systemData = systemData.get();
1987 assetInstance->asset = assetData.asset;
1988 assetInstance->container = assetComponent.getContainer();
1989 assetInstance->scene.initialized =
false;
1994 assetInstance->priority = assetComponent.priority;
1997 assetInstance->objectId = renderComponent->objectId;
1998 assetInstance->renderLayers = renderComponent->layer;
2002 if (assetInstance->useOverrideMaterial() || assetInstance->useCloneMaterial()) {
2003 if (assetComponent.material) {
2004 assetInstance->material = assetComponent.material;
2007 if (!systemData->defaultMaterialHandle) {
2008 systemData->defaultMaterialHandle = context->materialInstanceManager->createMaterialInstance(context->materialManager->getDefaultMaterial());
2010 assetInstance->material = systemData->defaultMaterialHandle;
2015 newUpdates.push_back({
2016 assetComponent.getContainer(),
2017 getHandle(&assetComponent),
2018 assetComponent.asset,
2024 if (assetData.asset && assetData.instanceData) {
2030 if (
EntityPtr clipShape = clipRefComp->clipShape.lock(); clipShape) {
2034 bool forward = assetInstance.clipShapeComponent != clipShapeComponent;
2035 assetInstance.clipShapeComponent = clipShapeComponent;
2039 forward = forward || (assetInstance.objectId != renderComponent->objectId);
2040 assetInstance.objectId = renderComponent->objectId;
2042 forward = forward || (assetInstance.renderLayers != renderComponent->layer);
2043 assetInstance.renderLayers = renderComponent->layer;
2048 updateAssetInstanceRenderProperties(context, assetInstance);
2053 if (assetData.asset && assetData.instanceData && assetData.instanceData->onDemand) {
2054 assetData.instanceData->freezeLod = freeze || assetComponent.freezeLod;
2055 assetData.instanceData->forceTolerance = assetComponent.forceTolerance;
2056 assetData.instanceData->tolerance = (assetComponent.forceTolerance ? 1.f : qualityScale) * assetComponent.tolerance;
2057 assetData.instanceData->minDistance = assetComponent.minDistance;
2059 assetData.instanceData->selectedIdRanges.clear();
2060 if (!assetComponent.idRanges.empty()) {
2062 size_t count = assetComponent.idRanges.size() / 2;
2063 std::vector<uint32_t>& selectedIdRanges = assetData.instanceData->selectedIdRanges;
2064 for (
size_t k = 0; k < count; k++) {
2065 const uint32_t rangeMin = assetComponent.idRanges[2 * k + 0];
2066 const uint32_t rangeMax = assetComponent.idRanges[2 * k + 1];
2067 if (rangeMax < rangeMin || (!selectedIdRanges.empty() && rangeMin < selectedIdRanges.back()) ) {
2069 selectedIdRanges.clear();
2072 selectedIdRanges.push_back(rangeMin);
2073 selectedIdRanges.push_back(rangeMax);
2077 currentOnDemandInstances.emplace_back(assetData.instanceData.get());
2086 AssetSystemVector<AssetUpdate>& readyUpdates = systemData->readyUpdates; assert(readyUpdates.empty());
2087 for (
auto & assetUpdate : newUpdates) {
2088 if (!assetUpdate.asset) {
2090 }
else if (assetUpdate.asset && assetUpdate.asset->isLoaded()) {
2092 for (
auto & resourceDefinition : assetUpdate.asset->definition.resources) {
2093 context->assetManager->instantiateResource(assetUpdate.asset, resourceDefinition);
2096 if (!assetUpdate.instanceData->onDemand) {
2098 readyUpdates.emplace_back(assetUpdate);
2105 for (
auto & assetUpdate : readyUpdates) {
2106 auto asset = assetUpdate.asset.resolve();
2107 const size_t entityCount = asset->definition.scene.entities.size();
2108 std::vector<ComponentModel::Entity*> entities(entityCount);
2109 for (
auto & def : asset->definition.scene.entities) {
2110 Entity* parent = assetUpdate.container;
2111 if (def.parentIndex !=
static_cast<uint32_t
>(-1)) {
2112 assert(def.parentIndex < entityCount && entities[def.parentIndex]);
2113 parent = entities[def.parentIndex];
2115 assert(def.index < entityCount && entities[def.index] ==
nullptr);
2116 entities[def.index] = createEntity(context, asset->definition.
scene, def, parent);
2119 readyUpdates.clear();
2124 processInFlightRequests(context, systemData.get());
2129 AssetSystemVector<AssetInstanceData*>& liveAssetInstances = systemData->liveAssetInstances; assert(liveAssetInstances.empty());
2130 liveAssetInstances.reserve(currentOnDemandInstances.size());
2131 for (
auto assetInstance : currentOnDemandInstances) {
2132 assetInstance->stats.frame = {};
2134 if (!assetInstance->scene.initialized) {
2135 initializeAssetInstance(*assetInstance);
2138 if (assetInstance->scene.entities.empty())
continue;
2140 auto sceneComponent = assetInstance->container->getComponent<
SceneComponent>();
2142 const bool visible = sceneComponent ? sceneComponent->
visible :
true;
2143 const bool visibleChanged = visible != assetInstance->visible;
2145 if (!visible && !visibleChanged)
continue;
2147 assetInstance->visible = visible;
2149 liveAssetInstances.emplace_back(assetInstance);
2151 currentOnDemandInstances.clear();
2154 Parallel::forEach(context, liveAssetInstances.size(), [&](
size_t i)
2156 auto assetInstance = liveAssetInstances[i];
2158 if (!assetInstance->freezeLod) {
2159 assetInstance->stats.frustumCullIn = 0;
2160 assetInstance->stats.frustumCullOut = 0;
2161 assetInstance->stats.bboxCullOut = 0;
2162 updateAssetDesiredLevels(context, *assetInstance, 0, 1);
2164 },
"AssetSystem::updateLiveInstances");
2166 for (
auto assetInstance : liveAssetInstances) {
2168 auto processingTime = assetInstance->stats.frame.startProcessing();
2171 auto updateTime = assetInstance->stats.frame.startUpdate();
2174 CpuInstrumentationScope(SCOPE_ASSETSYSTEM,
"AssetSystem::updateEntities");
2177 root.entityPtr = assetInstance->container;
2180 getLodReference(context, *assetInstance, lodReference, assetInstance->container);
2182 for (
size_t i = 0; i < assetInstance->scene.roots.size(); ++i) {
2183 updateEntity(context, lodReference, *assetInstance, assetInstance->scene[assetInstance->scene.roots[i]], &root);
2187 CpuInstrumentationScope(SCOPE_ASSETSYSTEM,
"AssetSystem::calculateCost");
2189 auto costTime = assetInstance->stats.frame.startCostCalculation();
2191#if ENABLE_COST_TRACKING
2192 assetInstance->stats.current = { 0, 0, 0 };
2193 for (
size_t i = 0; i < assetInstance->scene.roots.size(); ++i) {
2194 calculateCost(context, *assetInstance, assetInstance->scene[assetInstance->scene.roots[i]]);
2199 dispatchMessages(context, *assetInstance);
2202 double limit = assetInstance->stats.frameLimits.processingTime;
2203 if (assetInstance->stats.frame.processingTime > limit) {
2204 LOG_TRACE(logger,
"Asset processing time exceeded limit %.3fs.", limit);
2205 LOG_TRACE(logger,
" Lod time: %.3fs", assetInstance->stats.frame.lodTime);
2206 LOG_TRACE(logger,
" Update time: %.3fs", assetInstance->stats.frame.updateTime);
2207 LOG_TRACE(logger,
" Cost time: %.3fs", assetInstance->stats.frame.calculateCostTime);
2208 LOG_TRACE(logger,
" ------------------------");
2209 LOG_TRACE(logger,
" Entities spawned: %d", assetInstance->stats.frame.spawnedEntities);
2210 LOG_TRACE(logger,
" Entities destroyed: %d", assetInstance->stats.frame.destroyedEntities);
2211 LOG_TRACE(logger,
" Models requested: %d", assetInstance->stats.frame.modelsRequested);
2212 LOG_TRACE(logger,
"");
2215 liveAssetInstances.clear();
2217 processPendingRequests(context, systemData.get(),
2218 context->modelManager,
2219 systemData->pendingRequests,
2220 systemData->inFlightRequests,
2221 (
size_t)context->variables->get(maxModelsInFlightVariableName, kDefaultMaxModelsInFlight),
2222 (
size_t)context->variables->get(maxModelLoadsPerFrameVariableName, kDefaultMaxModelLoadsPerFrame));
2227 base::postUpdate(context);
2235 if (pool.size() == 1u) {
2236 assert(bounds ==
nullptr);
2237 bounds = std::make_unique<AssetBounds>(
this);
2238 context->bounds->addBoundsExtension(bounds.get());
2246 base::destroyComponent(component);
2248 if (pool.size() == 0u && bounds) {
2249 context->bounds->removeBoundsExtension(bounds.get());
2256 if (!assetComponent)
return nullptr;
2258 auto & data = getData<AssetData>(assetComponent);
2260 return data.instanceData ? &data.instanceData->stats :
nullptr;
2263const Cogs::Geometry::BoundingBox* Cogs::Core::AssetSystem::getLocalBounds(AssetComponent* assetComponent)
const
2265 if (!assetComponent)
2268 auto& data = getData<AssetData>(assetComponent);
2269 if (!data.instanceData)
return nullptr;
2270 if (data.instanceData->scene.entities.empty())
return nullptr;
2273 return &data.instanceData->scene.getLocalBounds(data.instanceData->scene[0]);
2276std::span<const Cogs::Core::AssetModelRequest* const> Cogs::Core::AssetSystem::getPendingRequests()
const
2278 return systemData->pendingRequests;
2281std::span<const Cogs::Core::AssetModelRequest* const> Cogs::Core::AssetSystem::getInFlightRequests()
const
2283 return systemData->inFlightRequests;
2286Cogs::Geometry::BoundingBox Cogs::Core::AssetSystem::getWorldBounds(AssetComponent* assetComponent,
bool ignoreVisibility)
const
2288 if (!ignoreVisibility) {
2289 auto sc = assetComponent->getComponent<SceneComponent>();
2290 if (sc && !sc->visible)
2291 return Cogs::Geometry::BoundingBox();
2294 auto bbox = getLocalBounds(assetComponent);
2295 if (bbox ==
nullptr || isEmpty(*bbox))
2296 return Cogs::Geometry::BoundingBox();
2298 auto transformComponent = assetComponent->getComponent<TransformComponent>();
2299 if (transformComponent) {
2300 const auto& transform = context->transformSystem->getLocalToWorld(transformComponent);
2301 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.