Cogs.Core
ModelSystem.cpp
1#include "ModelSystem.h"
2#include "Context.h"
3#include "EntityStore.h"
4
5#include "Components/Core/MeshComponent.h"
6#include "Components/Core/TransformComponent.h"
7#include "Components/Core/MeshRenderComponent.h"
8#include "Components/Appearance/MaterialComponent.h"
9#include "Components/Core/SceneComponent.h"
10#include "Components/Core/AnimationComponent.h"
11#include "Components/Core/PropertiesComponent.h"
12
13#include "Systems/Core/TransformSystem.h"
14#include "Systems/Core/SceneSystem.h"
15#include "Systems/Core/MeshSystem.h"
16#include "Systems/Core/RenderSystem.h"
17#include "Systems/Appearance/MaterialSystem.h"
18
19#include "Services/DeferredNameResolution.h"
20
21#include "Resources/ModelManager.h"
22#include "Resources/MaterialManager.h"
23#include "Resources/DefaultMaterial.h"
24#include "Resources/Texture.h"
25
26#include "Foundation/Logging/Logger.h"
27#include "Foundation/Platform/Timer.h"
28
29namespace
30{
31 Cogs::Logging::Log logger = Cogs::Logging::getLogger("ModelSystem");
32}
33
34namespace Cogs
35{
36 namespace Core
37 {
38 void forwardRenderProperties(SceneComponent * sceneComp, const RenderComponent * masterRenderComp)
39 {
40 for (auto & child : sceneComp->children) {
41 auto * childRenderComp = child->getComponent<RenderComponent>();
42
43 if (childRenderComp) {
44 childRenderComp->layer = masterRenderComp->layer;
45 childRenderComp->objectId = masterRenderComp->objectId;
46 childRenderComp->drawOrder = masterRenderComp->drawOrder;
47 childRenderComp->renderFlags |= masterRenderComp->renderFlags & RenderFlags::CastShadows;
48 childRenderComp->setChanged();
49 }
50 forwardRenderProperties(child->getComponent<SceneComponent>(), masterRenderComp);
51 }
52 }
53
54 MaterialInstanceHandle getMaterialInstance(Context * context)
55 {
56 return context->materialInstanceManager->createMaterialInstance(context->materialManager->getDefaultMaterial());
57 }
58
59 void updateMasterMaterial(Context * /*context*/, ModelData & modelData, MaterialInstanceHandle masterMaterial)
60 {
61 modelData.masterMaterial = masterMaterial;
62
63 for (auto & materialInstance : modelData.materials) {
64 if (!materialInstance->isDefaultMaterial()) continue;
65
66 materialInstance->masterInstance = masterMaterial;
67
68 if (masterMaterial->isDefaultMaterial()) {
69 const bool overrideColor = (masterMaterial->getMaterialFlags() & MaterialFlags::OverrideColor) != 0;
70 const bool overrideAlpha = (masterMaterial->getMaterialFlags() & MaterialFlags::OverrideAlpha) != 0;
71
72 if (overrideColor || overrideAlpha) {
73 auto masterColor = masterMaterial->getVec4Property(DefaultMaterial::DiffuseColor);
74
75 materialInstance->setVec4Property(DefaultMaterial::MasterColor, masterColor);
76 materialInstance->setBoolProperty(DefaultMaterial::OverrideColor, overrideColor);
77 materialInstance->setBoolProperty(DefaultMaterial::OverrideAlpha, overrideAlpha);
78
79 if (overrideAlpha && masterColor.a != 1) {
80 materialInstance->setMaterialFlag(MaterialFlags::MasterTransparency);
81 } else {
82 materialInstance->unsetMaterialFlag(MaterialFlags::MasterTransparency);
83 }
84 } else {
85 materialInstance->setBoolProperty(DefaultMaterial::OverrideColor, false);
86 materialInstance->setBoolProperty(DefaultMaterial::OverrideAlpha, false);
87
88 materialInstance->unsetMaterialFlag(MaterialFlags::MasterTransparency);
89 }
90 }
91 }
92 }
93 }
94}
95
97{
98 hierarchyUpdatedMessage = context->dynamicComponentSystem->registerMessage("hierarchyUpdated");
99
101}
102
104{
105 for (auto & component : pool) {
106 auto & modelData = getData<ModelData>(&component);
107
108 auto model = HandleIsValid(component.model) ? context->modelManager->get(component.model) : nullptr;
109
110 if (component.hasChanged() || !modelData.isLoaded() ||
111 (model && (!modelData.isGeneration((uint8_t)model->getGeneration()) || modelData.getResourceId() != model->getId()))) {
112
113 modelData.clearResourcesLoaded();
114 if (model) {
115 if (model->isLoaded()) {
116
117 if (component.waitForSubresources) {
118 bool ready = true;
119 for (auto& mesh : model->meshes) {
120 ready &= mesh->isResident();
121 }
122
123 for (const MaterialInstanceHandle& material : model->materials) {
124 ready &= material->isResident();
125
126 for (const TextureValue& texture : material->textureVariables) {
127 ready &= texture.texture.handle && texture.texture.handle->isResident();
128 }
129 }
130
131 if (!ready) continue;
132 modelData.setResourcesLoaded();
133 }
134
135 loadModel(context, component, modelData, model);
136
137 auto id = component.getContainer()->getId();
138 for (auto c : callbacks) {
139 c.second(context, id);
140 }
141
142 context->dynamicComponentSystem->sendMessage(component.getContainer(), hierarchyUpdatedMessage);
143
144 auto renderComp = component.getComponent<RenderComponent>();
145 if (renderComp) {
146 forwardRenderProperties(component.getComponent<SceneComponent>(), renderComp);
147 }
148
149 }
150 } else if (modelData.isLoaded()) {
151 clearModel(context, component);
152 }
153 }
154
155 auto renderComp = component.getComponent<RenderComponent>();
156 if (renderComp && renderComp->hasChanged()) {
157 forwardRenderProperties(component.getComponent<SceneComponent>(), renderComp);
158 }
159 }
160}
161
163{
164 if (modelComponent == nullptr || !HandleIsValid(modelComponent->model)) return 0.5f;
165
166 ModelData& modelData = getData<ModelData>(modelComponent);
167 if (modelData.isResourcesLoaded()) return 1.f;
168
169 Model* model = context->modelManager->get(modelComponent->model);
170 if (!model->isLoaded()) return 0.f;
171
172 size_t progress = 0;
173 size_t total = model->meshes.size() + model->materials.size();
174 for (const MeshHandle& mesh : model->meshes) {
175 if (mesh->isActive()) progress++;
176 }
177 bool ready = true;
178 for (const MaterialInstanceHandle& material : model->materials) {
179 ready &= material->isResident();
180
181 for (const TextureValue& texture : material->textureVariables) {
182 ready &= texture.texture.handle && texture.texture.handle->isResident();
183 }
184 if (ready) progress++;
185 }
186
187 return static_cast<float>(progress) / static_cast<float>(total);
188}
189
191{
192 for (auto & component : pool) {
193 auto & modelData = getData<ModelData>(&component);
194 auto materialComponent = component.getComponent<MaterialComponent>();
195
196 if (!materialComponent) continue;
197
198 auto& materialData = context->materialSystem->getData(materialComponent);
199
200 if (!materialData.instance) continue;
201
202 if (materialData.instance != modelData.masterMaterial || materialData.instance->hasChanged()) {
203 updateMasterMaterial(context, modelData, materialData.instance);
204 }
205 }
206
208}
209
210void Cogs::Core::ModelSystem::loadModel(Context * context, const ModelComponent & component, ModelData & modelData, Model * model)
211{
212 if (modelData.isLoaded()) {
213 clearModel(context, component);
214 }
215
216 Entity* modelEntity = component.getContainer();
217
218 // Optionally forward modelOrigin to rootEntity coordinate
219 if (component.setCoordinate) {
220
221 // Check if "modelOrigin" is among the first attributes before the attributes of the first part.
222 uint32_t maxProp = model->parts.empty() ? model->properties.size() : model->parts[0].firstProperty;
223 if (uint32_t ix = model->properties.findProperty(0, maxProp, Strings::add("modelOrigin")); ix != PropertyStore::NoProperty) {
224
225 // We have a model origin
226 if (std::span<const double> view = model->properties.getDoubleArray(ix); view.size() == 3) {
227
228 // Find root entity.
229 Entity* root = modelEntity;
230 while (Entity* parent = context->store->getEntityParent(root)) {
231 root = parent;
232 }
233
234 // Set coordinate of root entity
235 if (TransformComponent* trComp = root->getComponent<TransformComponent>(); trComp) {
236 for (glm::dvec3::length_type i = 0; i < 3; i++) {
237 trComp->coordinates[i] = view[i];
238 }
239 trComp->setChanged();
240 }
241 }
242 }
243 }
244
245
246 const MaterialComponent* materialComponent = modelEntity->getComponent<MaterialComponent>();
247
248 MaterialInstanceHandle modelMaterialInstance;
249 if (component.materialInstance) {
250 modelMaterialInstance = component.materialInstance;
251 } else {
252
253 if (materialComponent) {
254 auto& materialData = context->materialSystem->getData(materialComponent);
255 modelMaterialInstance = materialData.instance;
256 }
257 }
258
259 ComponentHandle animationHandle = {};
260 if (model->animation) {
261 auto animationComponent = modelEntity->getComponent<AnimationComponent>();
262
263 if (!animationComponent) {
264 animationComponent = context->store->addComponent<AnimationComponent>(modelEntity);
265 }
266
267 animationComponent->animation = model->animation;
268
269 animationHandle = modelEntity->getComponentHandle<AnimationComponent>();
270 }
271
272 Timer t = Timer::startNew();
273
274 {
275 std::vector<EntityPtr> entities(model->parts.size());
276 std::vector<ComponentHandle> transformComponents(model->parts.size());
277 std::vector<ComponentHandle> sceneComponents(model->parts.size());
278
279 const auto groupId = hash("Group");
280 const auto meshPartId = hash("MeshPart");
281
282 auto store = context->store;
283
284 store->createEntities(model->parts.size(), entities);
285
286 bool shareMaterial = component.shareMaterial;
287 bool inheritMaterial = component.inheritMaterial;
288
289 for (size_t i = 0; i < model->parts.size(); ++i) {
290 const auto & part = model->parts[i];
291 auto & entity = entities[i];
292
293 auto partName = model->getPartName(part);
294 if (!partName.empty()) {
295 entity->setName(partName);
296 context->deferredResolution->scheduleRescan();
297 }
298
299 auto transformComponent = context->transformSystem->createComponent();
300 transformComponents[i] = transformComponent;
301
302 entity->addComponent(transformComponent);
303
304 sceneComponents[i] = context->sceneSystem->createComponent();
305 entity->addComponent(sceneComponents[i]);
306
307 if (part.parentIndex != NoParentPart) {
308 sceneComponents[part.parentIndex].resolveComponent<SceneComponent>()->children.emplace_back(entity);
309 transformComponent.resolveComponent<TransformComponent>()->parent = transformComponents[part.parentIndex];
310 } else {
311 context->store->addChild(component.getContainer(), entity);
312 }
313
314 EntityData* entityData = static_cast<EntityData *>(entity->getUserData());
315 entityData->templateId = (part.meshIndex != static_cast<uint32_t>(-1) ? meshPartId : groupId);
316
317 if (part.meshIndex != static_cast<uint32_t>(-1)) {
318 auto & mesh = model->meshes[part.meshIndex];
319
320 auto mcHandle = context->meshSystem->createComponent();
321 entity->addComponent(mcHandle);
322
323 auto meshComponent = mcHandle.resolveComponent<MeshComponent>();
324 meshComponent->meshHandle = mesh;
325 meshComponent->setChanged();
326
327 auto mrcHandle = context->renderSystem->createComponent();
328 entity->addComponent(mrcHandle);
329
330 auto renderComponent = mrcHandle.resolveComponent<MeshRenderComponent>();
331 if (part.boundsIndex != static_cast<uint32_t>(-1) && model->bounds.size() > part.boundsIndex) {
332 context->renderSystem->setLocalBounds(renderComponent, model->bounds[part.boundsIndex]);
333 } else {
334 // Warn...
335 }
336
337 renderComponent->startIndex = part.startIndex;
338 renderComponent->vertexCount = part.vertexCount;
339 renderComponent->primitiveType = (PrimitiveType::EPrimitiveType)part.primitiveType;
340
341 MaterialInstanceHandle partMaterial = part.materialIndex < model->materials.size()
342 ? model->materials[part.materialIndex]
344 // FIXME: Accessing mesh flags here could cause a race condition. Currently that's not a huge concern as it might happen
345 // just for skinned meshes without materials with the inheritMaterial flag set.
346 // Setting the "Skinned" variant here should be removed once the material properties are set based on RenderMesh layout and properties.
347
348 if(shareMaterial){
349 if(inheritMaterial){
350 renderComponent->material = modelMaterialInstance;
351 }
352 else if (partMaterial) {
353 renderComponent->material = partMaterial;
354 }
355 else {
356 MaterialInstanceHandle master = context->materialManager->getDefaultMaterial();
357 renderComponent->material = context->materialInstanceManager->createMaterialInstance(master);
358 }
359 }
360 else{
361 // Create a clone of master material
362 if(inheritMaterial){
363 auto master = context->materialManager->generateHandle(modelMaterialInstance->material);
364 renderComponent->material = context->materialInstanceManager->createMaterialInstance(master);
365 renderComponent->material->clone(modelMaterialInstance.resolve());
366 }
367 else{
368 MaterialHandle master;
369 if(HandleIsValid(partMaterial)){
370 master = context->materialManager->generateHandle(partMaterial->material);
371 }
372 else{
373 master = context->materialManager->getDefaultMaterial();
374 }
375 renderComponent->material = context->materialInstanceManager->createMaterialInstance(master);
376 modelData.materials.push_back(renderComponent->material);
377 }
378 // Copy over settings from partMaterial
379 if(partMaterial){
380 renderComponent->material->setPermutation(partMaterial->getPermutation());
381 renderComponent->material->instanceFlags = partMaterial->instanceFlags;
382 renderComponent->material->options = partMaterial->options;
383 renderComponent->material->cloneMatchingProperties(partMaterial.resolve());
384 renderComponent->material->cloneMatchingVariants(partMaterial.resolve());
385 }
386 }
387
388 if (animationHandle) {
389 auto animationComponent = context->store->addComponent<AnimationComponent>(entity.get());
390 animationComponent->animation = model->animation;
391 animationComponent->master = animationHandle;
392 animationComponent->setChanged();
393 }
394 }
395
396 if (part.numProperties) {
397 auto propertiesComponent = entity->getComponent<PropertiesComponent>();
398
399 if (!propertiesComponent) {
400 propertiesComponent = context->store->addComponent<PropertiesComponent>(entity.get());
401 }
402
403 propertiesComponent->properties.copyProperties(model->properties, part.firstProperty, part.numProperties);
404 }
405 }
406
407 for (size_t i = 0; i < model->parts.size(); ++i) {
408 const auto & part = model->parts[i];
409
410 context->transformSystem->setLocalTransform(transformComponents[i].resolveComponent<TransformComponent>(), model->getPartTransform(part));
411 }
412 }
413
414 LOG_TRACE(logger, "Model spawn elapsed: %f.", t.elapsedSeconds());
415
416 if (modelMaterialInstance) {
417 updateMasterMaterial(context, modelData, modelMaterialInstance);
418 }
419
420 modelData.setLoaded();
421 modelData.setGeneration((uint8_t)model->getGeneration());
422 modelData.setResourceId(model->getId());
423}
424
425void Cogs::Core::ModelSystem::clearModel(Context * /*context*/, const ModelComponent & component)
426{
427 // Remove all children, clearing the loaded model.
428 auto sceneComponent = component.getComponent<SceneComponent>();
429
430 sceneComponent->children.clear();
431}
432
433int Cogs::Core::ModelSystem::addHierarchyCallback(HierarchyChangedCallback callback)
434{
435 int id = nextId++;
436 callbacks[id] = callback;
437 return id;
438}
439
440void Cogs::Core::ModelSystem::removeHierarchyCallback(int id)
441{
442 callbacks.erase(id);
443}
444
445
446
447
class Entity * getContainer() const
Get the container currently owning this component instance.
Definition: Component.h:151
Container for components, providing composition of dynamic entities.
Definition: Entity.h:18
T * getComponent() const
Get a pointer to the first component implementing the given type in the entity.
Definition: Entity.h:35
ComponentHandle getComponentHandle() const
Get a component handle to the first component implementing the given type.
Definition: Entity.h:90
Context * context
Pointer to the Context instance the system lives in.
void postUpdate()
Perform post update logic in the system.
virtual void initialize(Context *context)
Initialize the system.
void update()
Updates the system state to that of the current frame.
ComponentHandle createComponent() override
ComponentHandle createComponent() override
Create a new component instance.
A Context instance contains all the services, systems and runtime components needed to use Cogs.
Definition: Context.h:83
class EntityStore * store
Entity store.
Definition: Context.h:231
std::unique_ptr< class DeferredNameResolution > deferredResolution
Deferred name resolution service instance.
Definition: Context.h:204
ComponentModel::Component * addComponent(ComponentModel::Entity *entity, Reflection::TypeId typeId)
Add a component of the given type to the entity.
void addChild(ComponentModel::Entity *parent, const EntityPtr &entity)
Add a child to the given parent.
Cogs::ComponentModel::Entity * getEntityParent(const ComponentModel::Entity *entity) const
Gets the parent of the given entity.
void createEntities(size_t count, std::vector< EntityPtr > &entities)
Allocates and initializes a collection of empty entities.
float getLoadProgress(ModelComponent *modelComponent)
void initialize(Context *context) override
Initialize the system.
Definition: ModelSystem.cpp:96
Base component for all rendering content.
ComponentHandle createComponent() override
Create a new component instance.
Contains information on how the entity behaves in the scene.
Log implementation class.
Definition: LogManager.h:139
bool HandleIsValid(const ResourceHandle_t< T > &handle)
Check if the given resource is valid, that is not equal to NoHandle or InvalidHandle.
@ CastShadows
Casts shadows.
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Definition: LogManager.h:180
Contains all Cogs related functionality.
Definition: FieldSetter.h:23
constexpr size_t hash() noexcept
Simple getter function that returns the initial value for fnv1a hashing.
Definition: HashFunctions.h:62
Handle to a Component instance.
Definition: Component.h:67
ComponentType * resolveComponent() const
Definition: Component.h:90
Exposes material properties for legacy entities and code.
@ OverrideAlpha
Override alpha of any inheriting materials.
Definition: Material.h:54
@ OverrideColor
Override color of any inheriting materials.
Definition: Material.h:52
@ MasterTransparency
Material contains transparency.
Definition: Material.h:47
std::vector< TextureValue > textureVariables
Texture property values for this instance.
Contains a model reference to instance as children to the entity the ModelComponent belongs to.
bool inheritMaterial
If the model should inherit the material from the base entity.
MaterialInstanceHandle materialInstance
Explicit material handle.
ModelHandle model
Handle to a model resource that will be instanced onto the entity this ModelComponent belongs to.
bool shareMaterial
If the model should use shared model materials and not create it's own instances.
bool setCoordinate
If model resource has a localOrigin property, forward this value to the TransformComponent's coordina...
Model resources define a template for a set of connected entities, with resources such as meshes,...
Definition: Model.h:56
static constexpr uint32_t NoProperty
Return from findProperty if key not found.
uint32_t getGeneration() const
Get the generation count.
Definition: ResourceBase.h:380
ResourceId getId() const
Get the resource id of this instance.
Definition: ResourceBase.h:215
static const ResourceHandle_t NoHandle
Handle representing a default (or none if default not present) resource.
TextureWithSampler texture
Value of the property for the material instance this belongs to.
TextureHandle handle
Handle to a texture resource, or TextureHandle::NoHandle if texture should be disabled.
EPrimitiveType
Primitive type enumeration.
Definition: Common.h:114