1 /*
2 * Copyright (c) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include <3d/ecs/components/material_component.h>
17 #include <3d/ecs/components/render_handle_component.h>
18 #include <base/containers/refcnt_ptr.h>
19 #include <base/containers/unordered_map.h>
20 #include <base/util/compile_time_hashes.h>
21 #include <base/util/hash.h>
22 #include <core/ecs/intf_ecs.h>
23 #include <core/intf_engine.h>
24 #include <core/property/intf_property_api.h>
25 #include <core/property/property_types.h>
26 #include <render/device/intf_shader_manager.h>
27 #include <render/implementation_uids.h>
28 #include <render/intf_render_context.h>
29
30 #include "util/property_util.h"
31
32 #define IMPLEMENT_MANAGER
33 #include <algorithm>
34 #include <iterator>
35
36 #include "PropertyTools/property_macros.h"
37
38 CORE_BEGIN_NAMESPACE()
39 using BASE_NS::string_view;
40 using BASE_NS::vector;
41 using CORE3D_NS::MaterialComponent;
42
43 /** Extend propertysystem with the types */
44 DECLARE_PROPERTY_TYPE(MaterialComponent::Type);
45 DECLARE_PROPERTY_TYPE(MaterialComponent::TextureTransform);
46 DECLARE_PROPERTY_TYPE(MaterialComponent::TextureInfo);
47 DECLARE_PROPERTY_TYPE(MaterialComponent::Shader);
48 DECLARE_PROPERTY_TYPE(vector<MaterialComponent::TextureTransform>);
49 DECLARE_PROPERTY_TYPE(CORE_NS::IPropertyHandle*);
50
51 // Declare their metadata
52 BEGIN_ENUM(MaterialTypeMetaData, MaterialComponent::Type)
53 DECL_ENUM(MaterialComponent::Type, METALLIC_ROUGHNESS, "Metallic Roughness")
54 DECL_ENUM(MaterialComponent::Type, SPECULAR_GLOSSINESS, "Specular Glossiness")
55 DECL_ENUM(MaterialComponent::Type, UNLIT, "Unlit")
56 DECL_ENUM(MaterialComponent::Type, UNLIT_SHADOW_ALPHA, "Unlit Shadow Alpha")
57 DECL_ENUM(MaterialComponent::Type, CUSTOM, "Custom material")
58 DECL_ENUM(MaterialComponent::Type, CUSTOM_COMPLEX, "Custom complex material")
59 END_ENUM(MaterialTypeMetaData, MaterialComponent::Type)
60
61 BEGIN_ENUM(MaterialLightingFlagBitsMetaData, MaterialComponent::LightingFlagBits)
62 DECL_ENUM(MaterialComponent::LightingFlagBits, SHADOW_RECEIVER_BIT, "Shadow Receiver")
63 DECL_ENUM(MaterialComponent::LightingFlagBits, SHADOW_CASTER_BIT, "Shadow Caster")
64 DECL_ENUM(MaterialComponent::LightingFlagBits, PUNCTUAL_LIGHT_RECEIVER_BIT, "Punctual Light Receiver")
65 DECL_ENUM(MaterialComponent::LightingFlagBits, INDIRECT_LIGHT_RECEIVER_BIT, "Indirect Light Receiver")
66 END_ENUM(MaterialLightingFlagBitsMetaData, MaterialComponent::LightingFlagBits)
67
68 BEGIN_ENUM(ExtraMaterialRenderingFlagBitsMetaData, MaterialComponent::ExtraRenderingFlagBits)
69 DECL_ENUM(MaterialComponent::ExtraRenderingFlagBits, DISCARD_BIT, "Discard Special Materials")
70 DECL_ENUM(MaterialComponent::ExtraRenderingFlagBits, DISABLE_BIT, "Disable Material Rendering")
71 DECL_ENUM(MaterialComponent::ExtraRenderingFlagBits, ALLOW_GPU_INSTANCING_BIT, "Allow GPU Instancing")
72 END_ENUM(ExtraMaterialRenderingFlagBitsMetaData, MaterialComponent::ExtraRenderingFlagBits)
73
74 BEGIN_METADATA(TextureTransformMetaData, MaterialComponent::TextureTransform)
75 DECL_PROPERTY2(MaterialComponent::TextureTransform, translation, "Translation", 0)
76 DECL_PROPERTY2(MaterialComponent::TextureTransform, rotation, "Rotation", 0)
77 DECL_PROPERTY2(MaterialComponent::TextureTransform, scale, "Scale", 0)
78 END_METADATA(TextureTransformMetaData, MaterialComponent::TextureTransform)
79
80 BEGIN_METADATA(TextureInfoMetaData, MaterialComponent::TextureInfo)
81 DECL_PROPERTY2(MaterialComponent::TextureInfo, image, "Texture", 0)
82 DECL_PROPERTY2(MaterialComponent::TextureInfo, sampler, "Sampler", 0)
83 DECL_PROPERTY2(MaterialComponent::TextureInfo, factor, "Factor", 0)
84 DECL_PROPERTY2(MaterialComponent::TextureInfo, transform, "Transform", 0)
85 END_METADATA(TextureInfoMetaData, MaterialComponent::TextureInfo)
86
87 BEGIN_METADATA(MaterialComponentShaderMetaData, MaterialComponent::Shader)
88 DECL_PROPERTY2(MaterialComponent::Shader, shader, "Shader", 0)
89 DECL_PROPERTY2(MaterialComponent::Shader, graphicsState, "Graphics State", 0)
90 END_METADATA(MaterialComponentShaderMetaData, MaterialComponent::Shader)
91
92 constexpr PropertyTypeDecl TEXTURE_INFO_T = PROPERTYTYPE(MaterialComponent::TextureInfo);
93
operator ==(const Property & lhs,const Property & rhs)94 static constexpr inline bool operator==(const Property& lhs, const Property& rhs) noexcept
95 {
96 return (lhs.type == rhs.type) && (lhs.hash == rhs.hash) && (lhs.name == rhs.name);
97 }
98 CORE_END_NAMESPACE()
99
100 CORE3D_BEGIN_NAMESPACE()
101 using BASE_NS::array_view;
102 using BASE_NS::string_view;
103 using BASE_NS::countof;
104 using BASE_NS::vector;
105 using BASE_NS::Uid;
106 using BASE_NS::string;
107 using BASE_NS::refcnt_ptr;
108 using BASE_NS::unordered_map;
109 using BASE_NS::move;
110 using BASE_NS::exchange;
111 using BASE_NS::forward;
112
113 using CORE_NS::Property;
114 using CORE_NS::IPropertyApi;
115 using CORE_NS::IEcs;
116 using CORE_NS::IPropertyHandle;
117 using CORE_NS::IComponentManager;
118 using CORE_NS::Entity;
119 using CORE_NS::PropertyFlags;
120 using CORE_NS::ScopedHandle;
121 using CORE_NS::GetInstance;
122 using CORE_NS::IClassRegister;
123 using CORE_NS::GetManager;
124
125 using RENDER_NS::RenderHandleReference;
126 using RENDER_NS::RenderHandle;
127 using RENDER_NS::IRenderContext;
128 using RENDER_NS::UID_RENDER_CONTEXT;
129 using RENDER_NS::IShaderManager;
130
131 namespace {
132 constexpr string_view PROPERTIES = "properties";
133 constexpr string_view NAME = "name";
134 constexpr string_view DISPLAY_NAME = "displayName";
135 constexpr string_view MATERIAL_COMPONENT_NAME = CORE_NS::GetName<MaterialComponent>();
136
137 // RenderDataDefaultMaterial::MAX_MATERIAL_CUSTOM_PROPERTY_BYTE_SIZE
138 constexpr uint32_t CUSTOM_PROPERTY_POD_CONTAINER_BYTE_SIZE { 256u };
139
140 constexpr uint32_t MODIFIED = 0x80000000;
141
142 template<typename Container, typename Predicate>
FindIf(const Container & container,Predicate && predicate)143 inline typename Container::const_iterator FindIf(const Container& container, Predicate&& predicate)
144 {
145 return std::find_if(container.cbegin(), container.cend(), forward<Predicate>(predicate));
146 }
147
148 template<typename Container, typename Predicate>
FindIf(Container & container,Predicate && predicate)149 inline typename Container::iterator FindIf(Container& container, Predicate&& predicate)
150 {
151 return std::find_if(container.begin(), container.end(), forward<Predicate>(predicate));
152 }
153
FindMaterialComponentJson(const CORE_NS::json::value & metaJson)154 const CORE_NS::json::value* FindMaterialComponentJson(const CORE_NS::json::value& metaJson)
155 {
156 if (auto material = FindIf(metaJson.array_,
157 [](const CORE_NS::json::value& value) {
158 auto nameJson = value.find(NAME);
159 return nameJson && nameJson->is_string() && nameJson->string_ == MATERIAL_COMPONENT_NAME;
160 });
161 material != metaJson.array_.end()) {
162 return &(*material);
163 }
164 return nullptr;
165 }
166
AppendProperties(vector<Property> & newProperties,vector<string> & newStringPropStorage,const CORE_NS::json::value & material,const array_view<const Property> componentMetaData)167 void AppendProperties(vector<Property>& newProperties, vector<string>& newStringPropStorage,
168 const CORE_NS::json::value& material, const array_view<const Property> componentMetaData)
169 {
170 if (const CORE_NS::json::value* propertiesJson = material.find(PROPERTIES);
171 propertiesJson && propertiesJson->is_object()) {
172 for (const auto& propertyObject : propertiesJson->object_) {
173 if (const auto propertyIt =
174 FindIf(componentMetaData, [key = propertyObject.key](const Property& p) { return p.name == key; });
175 propertyIt != componentMetaData.end()) {
176 // if it's an array, make sure sizes match
177 #if (CORE3D_VALIDATION_ENABLED == 1)
178 if (propertyObject.value.is_array() && propertyIt->type.isArray &&
179 propertyObject.value.array_.size() > propertyIt->count) {
180 CORE_LOG_W("CORE3D_VALIDATION: material property metadata count mismatch (%u <= %u)",
181 uint32_t(propertyObject.value.array_.size()), uint32_t(propertyIt->count));
182 }
183 #endif
184 if (propertyObject.value.is_array() && propertyIt->type.isArray &&
185 propertyObject.value.array_.size() <= propertyIt->count) {
186 uint32_t index = 0u;
187 // reserve strings, name and display name (reason for * 2)
188 newStringPropStorage.reserve(propertyObject.value.array_.size() * 2);
189 for (const CORE_NS::json::value& value : propertyObject.value.array_) {
190 // if the array has objects, the metadata should have containerMethods for
191 // the members
192 if (value.is_object() && !value.object_.empty() && propertyIt->metaData.containerMethods) {
193 const auto& containerProperty = propertyIt->metaData.containerMethods->property;
194
195 // calculate offset to current struct in the array
196 const auto offset = propertyIt->offset + containerProperty.size * index;
197 Property newProperty = containerProperty;
198
199 // add the currect struct offset to the member offset
200 newProperty.offset += offset;
201
202 // update 'name' and 'displayName' if needed
203 if (auto name = value.find(NAME); name && name->is_string()) {
204 newStringPropStorage.push_back(string(name->string_));
205 newProperty.name = newStringPropStorage.back();
206 newProperty.hash = BASE_NS::FNV1aHash(newProperty.name.data(), newProperty.name.size());
207 }
208 if (auto displayName = value.find(DISPLAY_NAME); displayName && displayName->is_string()) {
209 newStringPropStorage.push_back(string(displayName->string_));
210 newProperty.displayName = newStringPropStorage.back();
211 }
212 newProperties.push_back(newProperty);
213 } else {
214 // NOTE: handle non-object properties?
215 }
216 ++index;
217 }
218 } else {
219 // NOTE: handle non-array properties?
220 }
221 }
222 }
223 }
224 }
225
MapTextureSlots(array_view<MaterialComponent::TextureInfo> textures,size_t baseOffset,array_view<const Property> newProperties,array_view<const Property> oldProperties)226 void MapTextureSlots(array_view<MaterialComponent::TextureInfo> textures, size_t baseOffset,
227 array_view<const Property> newProperties, array_view<const Property> oldProperties)
228 {
229 MaterialComponent::TextureInfo tmp[MaterialComponent::TextureIndex::TEXTURE_COUNT];
230 for (const auto& newProp : newProperties) {
231 if (newProp.type == CORE_NS::TEXTURE_INFO_T) {
232 if (auto it = std::find(oldProperties.cbegin(), oldProperties.cend(), newProp);
233 it != oldProperties.cend()) {
234 auto& oldProp = *it;
235 // theoretically the offset of TextureInfos can't be less than the offset of the first and this could be
236 // an assertion
237 if (oldProp.offset >= baseOffset && newProp.offset >= baseOffset) {
238 auto oldIndex = (oldProp.offset - baseOffset) / sizeof(MaterialComponent::TextureInfo);
239 auto newIndex = (newProp.offset - baseOffset) / sizeof(MaterialComponent::TextureInfo);
240 if (newIndex < BASE_NS::countof(tmp) && oldIndex < textures.size()) {
241 tmp[newIndex] = BASE_NS::move(textures[oldIndex]);
242 }
243 }
244 }
245 }
246 }
247 std::move(std::begin(tmp), std::end(tmp), std::begin(textures));
248 }
249
UpdateCustomPropertyMetadata(const CORE_NS::json::value & customProperties,CustomPropertyPodContainer & properties)250 void UpdateCustomPropertyMetadata(const CORE_NS::json::value& customProperties, CustomPropertyPodContainer& properties)
251 {
252 for (const CORE_NS::json::value& propertyValue : customProperties.array_) {
253 if (propertyValue.is_object()) {
254 // NOTE: set and binding are currently ignored as the custom material property cannot be mapped
255 // to user defined sets and bindings
256 for (const auto& object : propertyValue.object_) {
257 if (object.key == "data" && object.value.is_array()) {
258 // reserve the property count
259 properties.ReservePropertyCount(object.value.array_.size());
260 for (const auto& dataValue : object.value.array_) {
261 if (dataValue.is_object()) {
262 string_view name;
263 string_view displayName;
264 string_view type;
265 const CORE_NS::json::value* value = nullptr;
266 for (const auto& dataObject : dataValue.object_) {
267 if (dataObject.key == NAME && dataObject.value.is_string()) {
268 name = dataObject.value.string_;
269 } else if (dataObject.key == DISPLAY_NAME && dataObject.value.is_string()) {
270 displayName = dataObject.value.string_;
271 } else if (dataObject.key == "type" && dataObject.value.is_string()) {
272 type = dataObject.value.string_;
273 } else if (dataObject.key == "value") {
274 value = &dataObject.value;
275 }
276 }
277 const CORE_NS::PropertyTypeDecl typeDecl = CustomPropertyPodHelper::GetPropertyTypeDeclaration(type);
278 const size_t align = CustomPropertyPodHelper::GetPropertyTypeAlignment(typeDecl);
279 const size_t offset = [](size_t value, size_t align) -> size_t {
280 if (align == 0U) {
281 return value;
282 }
283 return ((value + align - 1U) / align) * align;
284 }(properties.GetByteSize(), align);
285 properties.AddOffsetProperty(name, displayName, offset, typeDecl);
286 CustomPropertyPodHelper::SetCustomPropertyBlobValue(typeDecl, value, properties, offset);
287 }
288 }
289 }
290 }
291 }
292 }
293 }
294 } // namespace
295
296 class MaterialComponentManager : public IMaterialComponentManager, public IPropertyApi {
297 using ComponentId = IComponentManager::ComponentId;
298
299 public:
300 explicit MaterialComponentManager(IEcs& ecs) noexcept;
301 ~MaterialComponentManager() override;
302
303 // IPropertyApi
304 size_t PropertyCount() const override;
305 const Property* MetaData(size_t index) const override;
306 array_view<const Property> MetaData() const override;
307 IPropertyHandle* Create() const override;
308 IPropertyHandle* Clone(const IPropertyHandle*) const override;
309 void Release(IPropertyHandle*) const override;
310 uint64_t Type() const override;
311
312 // IComponentManager
313 virtual string_view GetName() const override;
314 virtual Uid GetUid() const override;
315 size_t GetComponentCount() const override;
316 const IPropertyApi& GetPropertyApi() const override;
317 Entity GetEntity(ComponentId index) const override;
318 uint32_t GetComponentGeneration(ComponentId index) const override;
319 bool HasComponent(Entity entity) const override;
320 IComponentManager::ComponentId GetComponentId(Entity entity) const override;
321 void Create(Entity entity) override;
322 bool Destroy(Entity entity) override;
323 void Gc() override;
324 void Destroy(array_view<const Entity> gcList) override;
325 vector<Entity> GetAddedComponents() override;
326 vector<Entity> GetRemovedComponents() override;
327 vector<Entity> GetUpdatedComponents() override;
328 CORE_NS::ComponentManagerModifiedFlags GetModifiedFlags() const override;
329 void ClearModifiedFlags() override;
330 uint32_t GetGenerationCounter() const override;
331 void SetData(Entity entity, const IPropertyHandle& dataHandle) override;
332 const IPropertyHandle* GetData(Entity entity) const override;
333 IPropertyHandle* GetData(Entity entity) override;
334 void SetData(ComponentId index, const IPropertyHandle& dataHandle) override;
335 const IPropertyHandle* GetData(ComponentId index) const override;
336 IPropertyHandle* GetData(ComponentId index) override;
337 IEcs& GetEcs() const override;
338
339 // IMaterialComponentManager
340 MaterialComponent Get(ComponentId index) const override;
341 MaterialComponent Get(Entity entity) const override;
342 void Set(ComponentId index, const MaterialComponent& aData) override;
343 void Set(Entity entity, const MaterialComponent& aData) override;
344 ScopedHandle<const MaterialComponent> Read(ComponentId index) const override;
345 ScopedHandle<const MaterialComponent> Read(Entity entity) const override;
346 ScopedHandle<MaterialComponent> Write(ComponentId index) override;
347 ScopedHandle<MaterialComponent> Write(Entity entity) override;
348
349 // internal, non-public
350 void Updated(Entity entity);
351
352 private:
353 bool IsMatchingHandle(const IPropertyHandle& handle);
354
355 BEGIN_PROPERTY(MaterialComponent, componentProperties_)
356 #include <3d/ecs/components/material_component.h>
357 END_PROPERTY();
358 static constexpr array_view<const Property> componentMetaData_ { componentProperties_,
359 countof(componentProperties_) };
360 // clang-format off
361 static constexpr CORE_NS::Property texturesMetaData_[] = {
362 DECL_NAMED_PROPERTY2(baseColor, textures[MaterialComponent::TextureIndex::BASE_COLOR], "Base Color", 0)
363 DECL_NAMED_PROPERTY2(normal, textures[MaterialComponent::TextureIndex::NORMAL], "Normal Map", 0)
364 DECL_NAMED_PROPERTY2(material, textures[MaterialComponent::TextureIndex::MATERIAL], "Material", 0)
365 DECL_NAMED_PROPERTY2(emissive, textures[MaterialComponent::TextureIndex::EMISSIVE], "Emissive", 0)
366 DECL_NAMED_PROPERTY2(ambientOcclusion, textures[MaterialComponent::TextureIndex::AO], "Ambient Occlusion", 0)
367 DECL_NAMED_PROPERTY2(clearcoat, textures[MaterialComponent::TextureIndex::CLEARCOAT], "Clearcoat", 0)
368 DECL_NAMED_PROPERTY2(clearcoatRoughness, textures[MaterialComponent::TextureIndex::CLEARCOAT_ROUGHNESS],
369 "Clearcoat Roughness", 0)
370 DECL_NAMED_PROPERTY2(clearcoatNormal, textures[MaterialComponent::TextureIndex::CLEARCOAT_NORMAL],
371 "Clearcoat Normal", 0)
372 DECL_NAMED_PROPERTY2(sheen, textures[MaterialComponent::TextureIndex::SHEEN], "Sheen", 0)
373 DECL_NAMED_PROPERTY2(transmission, textures[MaterialComponent::TextureIndex::TRANSMISSION], "Transmission", 0)
374 DECL_NAMED_PROPERTY2(specular, textures[MaterialComponent::TextureIndex::SPECULAR], "Specular", 0)
375 };
376 // clang-format on
377 static constexpr array_view<const Property> defaultMetaData_ { texturesMetaData_, countof(texturesMetaData_) };
378
379 static constexpr uint64_t typeHash_ = BASE_NS::CompileTime::FNV1aHash(CORE_NS::GetName<MaterialComponent>().data());
380
381 struct ReferencedProperties {
RefMaterialComponentManager::ReferencedProperties382 void Ref()
383 {
384 ++cnt;
385 }
386
UnrefMaterialComponentManager::ReferencedProperties387 void Unref()
388 {
389 if (--cnt == 0) {
390 delete this;
391 }
392 }
393
394 vector<Property> properties;
395 // need to be stored to have a valid string_view in properties (for name and display name)
396 vector<string> stringStorage;
397 uint32_t cnt { 0u };
398 };
399
400 class ComponentHandle : public IPropertyHandle, IPropertyApi {
401 public:
402 ComponentHandle() = delete;
403 ComponentHandle(MaterialComponentManager* owner, Entity entity) noexcept;
404 ComponentHandle(MaterialComponentManager* owner, Entity entity, const MaterialComponent& data) noexcept;
405 ~ComponentHandle() override = default;
406 ComponentHandle(const ComponentHandle& other) = delete;
407 ComponentHandle(ComponentHandle&& other) noexcept;
408 ComponentHandle& operator=(const ComponentHandle& other);
409 ComponentHandle& operator=(ComponentHandle&& other) noexcept;
410
411 // IPropertyHandle
412 const IPropertyApi* Owner() const override;
413 size_t Size() const override;
414 const void* RLock() const override;
415 void RUnlock() const override;
416 void* WLock() override;
417 void WUnlock() override;
418
419 // IPropertyApi
420 size_t PropertyCount() const override;
421 const Property* MetaData(size_t index) const override;
422 array_view<const Property> MetaData() const override;
423 uint64_t Type() const override;
424 IPropertyHandle* Create() const override;
425 IPropertyHandle* Clone(const IPropertyHandle* src) const override;
426 void Release(IPropertyHandle* handle) const override;
427
428 void UpdateMetadata() const;
429
430 MaterialComponentManager* manager_ { nullptr };
431 Entity entity_;
432 struct CachedShader {
433 // we do not keep reference here, if it's not in use then it could be destroyed
434 RenderHandle shader {};
435 // frame index to see the shader "timestamp"
436 uint64_t frameIndex { 0 };
437 };
438 mutable CachedShader cachedShader_;
439 mutable refcnt_ptr<ReferencedProperties> metaData_;
440 uint32_t generation_ { 0 };
441 mutable uint32_t rLocked_ { 0 };
442 mutable bool wLocked_ { false };
443 bool dirty_ { false };
444 mutable MaterialComponent data_;
445 mutable BASE_NS::unique_ptr<CustomPropertyPodContainer> custom_;
446 };
447
448 IEcs& ecs_;
449 IShaderManager& shaderManager_;
450 IRenderHandleComponentManager* renderHandleManager_ { nullptr };
451
452 uint32_t generationCounter_ { 0 };
453 uint32_t modifiedFlags_ { 0 };
454 unordered_map<Entity, ComponentId> entityComponent_;
455 vector<ComponentHandle> components_;
456 vector<Entity> added_;
457 vector<Entity> removed_;
458 vector<Entity> updated_;
459
460 unordered_map<RenderHandle, refcnt_ptr<ReferencedProperties>> properties_;
461 };
462
MaterialComponentManager(IEcs & ecs)463 MaterialComponentManager::MaterialComponentManager(IEcs& ecs) noexcept
464 : ecs_(ecs), shaderManager_(GetInstance<IRenderContext>(
465 *ecs.GetClassFactory().GetInterface<IClassRegister>(), UID_RENDER_CONTEXT)
466 ->GetDevice()
467 .GetShaderManager()),
468 renderHandleManager_(GetManager<IRenderHandleComponentManager>(ecs))
469 {
470 // Initial reservation for 64 components/entities.
471 // Will resize as needed.
472 constexpr size_t initialComponentReserveSize = 64;
473 components_.reserve(initialComponentReserveSize);
474 entityComponent_.reserve(initialComponentReserveSize);
475
476 auto result =
477 properties_.insert_or_assign(RenderHandle {}, refcnt_ptr<ReferencedProperties>(new ReferencedProperties));
478 auto& properties = result.first->second->properties;
479 properties.insert(properties.end(), componentMetaData_.begin(), componentMetaData_.end());
480 properties.insert(properties.end(), defaultMetaData_.begin(), defaultMetaData_.end());
481 }
482
483 MaterialComponentManager::~MaterialComponentManager() = default;
484
485 // IPropertyApi
PropertyCount() const486 size_t MaterialComponentManager::PropertyCount() const
487 {
488 return componentMetaData_.size();
489 }
490
MetaData(size_t index) const491 const Property* MaterialComponentManager::MetaData(size_t index) const
492 {
493 if (index < componentMetaData_.size()) {
494 return &componentMetaData_[index];
495 }
496 return nullptr;
497 }
498
MetaData() const499 array_view<const Property> MaterialComponentManager::MetaData() const
500 {
501 return componentMetaData_;
502 }
503
Create() const504 IPropertyHandle* MaterialComponentManager::Create() const
505 {
506 return new ComponentHandle(const_cast<MaterialComponentManager*>(this), {}, {});
507 }
508
Clone(const IPropertyHandle * src) const509 IPropertyHandle* MaterialComponentManager::Clone(const IPropertyHandle* src) const
510 {
511 if (src) {
512 auto owner = src->Owner();
513 if (owner == this) {
514 auto* h = static_cast<const ComponentHandle*>(src);
515 return new ComponentHandle(const_cast<MaterialComponentManager*>(this), {}, h->data_);
516 } else if (owner) {
517 return owner->Clone(src);
518 }
519 }
520 return nullptr;
521 }
522
Release(IPropertyHandle * dst) const523 void MaterialComponentManager::Release(IPropertyHandle* dst) const
524 {
525 if (dst) {
526 auto owner = dst->Owner();
527 if (owner == this) {
528 // we can only destroy things we "own" (know)
529 auto* handle = static_cast<ComponentHandle*>(dst);
530 if (auto id = GetComponentId(handle->entity_); id != IComponentManager::INVALID_COMPONENT_ID) {
531 if (&components_[id] == handle) {
532 // This is one of the components (bound to an entity) so do nothing
533 return;
534 }
535 }
536 delete handle;
537 } else if (owner) {
538 owner->Release(dst);
539 }
540 }
541 }
542
Type() const543 uint64_t MaterialComponentManager::Type() const
544 {
545 return typeHash_;
546 }
547
548 // IComponentManager
GetName() const549 string_view MaterialComponentManager::GetName() const
550 {
551 constexpr auto name = CORE_NS::GetName<MaterialComponent>();
552 return name;
553 }
554
GetUid() const555 Uid MaterialComponentManager::GetUid() const
556 {
557 return IMaterialComponentManager::UID;
558 }
559
GetComponentCount() const560 size_t MaterialComponentManager::GetComponentCount() const
561 {
562 return components_.size();
563 }
564
GetPropertyApi() const565 const IPropertyApi& MaterialComponentManager::GetPropertyApi() const
566 {
567 return *this;
568 }
569
GetEntity(ComponentId index) const570 Entity MaterialComponentManager::GetEntity(ComponentId index) const
571 {
572 if (index < components_.size()) {
573 return components_[index].entity_;
574 }
575 return Entity();
576 }
577
GetComponentGeneration(ComponentId index) const578 uint32_t MaterialComponentManager::GetComponentGeneration(ComponentId index) const
579 {
580 if (index < components_.size()) {
581 return components_[index].generation_;
582 }
583 return 0;
584 }
585
HasComponent(Entity entity) const586 bool MaterialComponentManager::HasComponent(Entity entity) const
587 {
588 return GetComponentId(entity) != IComponentManager::INVALID_COMPONENT_ID;
589 }
590
GetComponentId(Entity entity) const591 IComponentManager::ComponentId MaterialComponentManager::GetComponentId(Entity entity) const
592 {
593 if (CORE_NS::EntityUtil::IsValid(entity)) {
594 if (auto it = entityComponent_.find(entity); it != entityComponent_.end()) {
595 return it->second;
596 }
597 }
598 return IComponentManager::INVALID_COMPONENT_ID;
599 }
600
Create(Entity entity)601 void MaterialComponentManager::Create(Entity entity)
602 {
603 if (CORE_NS::EntityUtil::IsValid(entity)) {
604 if (auto it = entityComponent_.find(entity); it == entityComponent_.end()) {
605 entityComponent_.insert({ entity, static_cast<ComponentId>(components_.size()) });
606 auto& component = components_.emplace_back(this, entity);
607 added_.push_back(entity);
608 modifiedFlags_ |= CORE_NS::CORE_COMPONENT_MANAGER_COMPONENT_ADDED_BIT;
609 generationCounter_++;
610
611 // lock/unlock for toggling component updated behavior.
612 component.WLock();
613 component.WUnlock();
614 } else {
615 if (auto dst = CORE_NS::ScopedHandle<MaterialComponent>(&components_[it->second]); dst) {
616 *dst = {};
617 }
618 }
619 }
620 }
621
Destroy(Entity entity)622 bool MaterialComponentManager::Destroy(Entity entity)
623 {
624 if (CORE_NS::EntityUtil::IsValid(entity)) {
625 if (auto it = entityComponent_.find(entity); it != entityComponent_.end()) {
626 components_[it->second].entity_ = {}; // invalid entity. (marks it as ready for re-use)
627 entityComponent_.erase(it);
628 removed_.push_back(entity);
629 modifiedFlags_ |= CORE_NS::CORE_COMPONENT_MANAGER_COMPONENT_REMOVED_BIT;
630 generationCounter_++;
631 return true;
632 }
633 }
634 return false;
635 }
636
Gc()637 void MaterialComponentManager::Gc()
638 {
639 const bool hasRemovedComponents = modifiedFlags_ & CORE_NS::CORE_COMPONENT_MANAGER_COMPONENT_REMOVED_BIT;
640 if (!hasRemovedComponents) {
641 return;
642 }
643 ComponentId componentCount = static_cast<ComponentId>(components_.size());
644 for (ComponentId id = 0; id < componentCount;) {
645 auto* it = &components_[id];
646 // invalid entity.. if so clean garbage
647 if (!CORE_NS::EntityUtil::IsValid(it->entity_)) {
648 // find last valid and swap with it
649 for (ComponentId rid = componentCount - 1; rid > id; rid--) {
650 auto* rit = &components_[rid];
651 // valid entity? if so swap the components.
652 if (CORE_NS::EntityUtil::IsValid(rit->entity_)) {
653 // fix the entityComponent_ map (update the component id for the entity)
654 entityComponent_[rit->entity_] = id;
655 *it = move(*rit);
656 break;
657 }
658 }
659 componentCount--;
660 continue;
661 }
662 id++;
663 }
664 if (components_.size() > componentCount) {
665 auto diff = static_cast<typename decltype(components_)::difference_type>(componentCount);
666 components_.erase(components_.cbegin() + diff, components_.cend());
667 }
668 }
669
Destroy(const array_view<const Entity> gcList)670 void MaterialComponentManager::Destroy(const array_view<const Entity> gcList)
671 {
672 for (const Entity e : gcList) {
673 Destroy(e);
674 }
675 }
676
GetAddedComponents()677 vector<Entity> MaterialComponentManager::GetAddedComponents()
678 {
679 return BASE_NS::move(added_);
680 }
681
GetRemovedComponents()682 vector<Entity> MaterialComponentManager::GetRemovedComponents()
683 {
684 return BASE_NS::move(removed_);
685 }
686
GetUpdatedComponents()687 vector<Entity> MaterialComponentManager::GetUpdatedComponents()
688 {
689 vector<Entity> updated;
690 if (modifiedFlags_ & MODIFIED) {
691 modifiedFlags_ &= ~MODIFIED;
692 updated.reserve(components_.size() / 2); // 2: half
693 for (auto& handle : components_) {
694 if (handle.dirty_) {
695 handle.dirty_ = false;
696 updated.push_back(handle.entity_);
697 }
698 }
699 }
700 return updated;
701 }
702
GetModifiedFlags() const703 CORE_NS::ComponentManagerModifiedFlags MaterialComponentManager::GetModifiedFlags() const
704 {
705 return modifiedFlags_ & ~MODIFIED;
706 }
707
ClearModifiedFlags()708 void MaterialComponentManager::ClearModifiedFlags()
709 {
710 modifiedFlags_ &= MODIFIED;
711 }
712
GetGenerationCounter() const713 uint32_t MaterialComponentManager::GetGenerationCounter() const
714 {
715 return generationCounter_;
716 }
717
SetData(Entity entity,const IPropertyHandle & dataHandle)718 void MaterialComponentManager::SetData(Entity entity, const IPropertyHandle& dataHandle)
719 {
720 if (!IsMatchingHandle(dataHandle)) {
721 return;
722 }
723 if (const auto src = ScopedHandle<const MaterialComponent>(&dataHandle); src) {
724 if (const auto it = entityComponent_.find(entity); it != entityComponent_.end()) {
725 components_[it->second] = static_cast<const ComponentHandle&>(dataHandle);
726 }
727 }
728 }
729
GetData(Entity entity) const730 const IPropertyHandle* MaterialComponentManager::GetData(Entity entity) const
731 {
732 if (CORE_NS::EntityUtil::IsValid(entity)) {
733 if (const auto it = entityComponent_.find(entity); it != entityComponent_.end()) {
734 if (it->second < components_.size()) {
735 return &components_[it->second];
736 }
737 }
738 }
739 return nullptr;
740 }
741
GetData(Entity entity)742 IPropertyHandle* MaterialComponentManager::GetData(Entity entity)
743 {
744 if (CORE_NS::EntityUtil::IsValid(entity)) {
745 if (const auto it = entityComponent_.find(entity); it != entityComponent_.end()) {
746 if (it->second < components_.size()) {
747 return &components_[it->second];
748 }
749 }
750 }
751 return nullptr;
752 }
753
SetData(ComponentId index,const IPropertyHandle & dataHandle)754 void MaterialComponentManager::SetData(ComponentId index, const IPropertyHandle& dataHandle)
755 {
756 if (!IsMatchingHandle(dataHandle)) {
757 // We could verify the metadata here.
758 // And in copy only the matching properties one-by-one also.
759 return;
760 }
761 if (index < components_.size()) {
762 if (const auto src = ScopedHandle<const MaterialComponent>(&dataHandle); src) {
763 components_[index] = static_cast<const ComponentHandle&>(dataHandle);
764 }
765 }
766 }
767
GetData(ComponentId index) const768 const IPropertyHandle* MaterialComponentManager::GetData(ComponentId index) const
769 {
770 if (index < components_.size()) {
771 return &components_[index];
772 }
773 return nullptr;
774 }
775
GetData(ComponentId index)776 IPropertyHandle* MaterialComponentManager::GetData(ComponentId index)
777 {
778 if (index < components_.size()) {
779 return &components_[index];
780 }
781 return nullptr;
782 }
783
GetEcs() const784 IEcs& MaterialComponentManager::GetEcs() const
785 {
786 return ecs_;
787 }
788
789 // IMaterialComponentManager
Get(ComponentId index) const790 MaterialComponent MaterialComponentManager::Get(ComponentId index) const
791 {
792 if (auto handle = Read(index); handle) {
793 return *handle;
794 }
795 return MaterialComponent {};
796 }
797
Get(Entity entity) const798 MaterialComponent MaterialComponentManager::Get(Entity entity) const
799 {
800 if (auto handle = Read(entity); handle) {
801 return *handle;
802 }
803 return MaterialComponent {};
804 }
805
Set(ComponentId index,const MaterialComponent & data)806 void MaterialComponentManager::Set(ComponentId index, const MaterialComponent& data)
807 {
808 if (auto handle = Write(index); handle) {
809 *handle = data;
810 }
811 }
812
Set(Entity entity,const MaterialComponent & data)813 void MaterialComponentManager::Set(Entity entity, const MaterialComponent& data)
814 {
815 if (CORE_NS::EntityUtil::IsValid(entity)) {
816 if (auto handle = Write(entity); handle) {
817 *handle = data;
818 // NOTE: customProperties are valid when updating itself
819 } else {
820 entityComponent_.insert({ entity, static_cast<ComponentId>(components_.size()) });
821 auto& component = components_.emplace_back(this, entity, data);
822 added_.push_back(entity);
823 modifiedFlags_ |= CORE_NS::CORE_COMPONENT_MANAGER_COMPONENT_ADDED_BIT;
824 generationCounter_++;
825
826 // lock/unlock for toggling component updated behavior.
827 component.WLock();
828 component.WUnlock();
829 }
830 }
831 }
832
Read(ComponentId index) const833 ScopedHandle<const MaterialComponent> MaterialComponentManager::Read(ComponentId index) const
834 {
835 return ScopedHandle<const MaterialComponent> { GetData(index) };
836 }
837
Read(Entity entity) const838 ScopedHandle<const MaterialComponent> MaterialComponentManager::Read(Entity entity) const
839 {
840 return ScopedHandle<const MaterialComponent> { GetData(entity) };
841 }
842
Write(ComponentId index)843 ScopedHandle<MaterialComponent> MaterialComponentManager::Write(ComponentId index)
844 {
845 return ScopedHandle<MaterialComponent> { GetData(index) };
846 }
847
Write(Entity entity)848 ScopedHandle<MaterialComponent> MaterialComponentManager::Write(Entity entity)
849 {
850 return ScopedHandle<MaterialComponent> { GetData(entity) };
851 }
852
853 // Internal
Updated(Entity entity)854 void MaterialComponentManager::Updated(Entity entity)
855 {
856 CORE_ASSERT_MSG(CORE_NS::EntityUtil::IsValid(entity), "Invalid ComponentId, bound to INVALID_ENTITY");
857 modifiedFlags_ |= CORE_NS::CORE_COMPONENT_MANAGER_COMPONENT_UPDATED_BIT | MODIFIED;
858 generationCounter_++;
859 }
860
IsMatchingHandle(const IPropertyHandle & dataHandle)861 bool MaterialComponentManager::IsMatchingHandle(const IPropertyHandle& dataHandle)
862 {
863 if (dataHandle.Owner() == this) {
864 return true;
865 }
866 if (dataHandle.Owner() && (dataHandle.Owner()->Type() == typeHash_)) {
867 return true;
868 }
869 return false;
870 }
871
872 // Handle implementation
ComponentHandle(MaterialComponentManager * owner,Entity entity)873 MaterialComponentManager::ComponentHandle::ComponentHandle(MaterialComponentManager* owner, Entity entity) noexcept
874 : ComponentHandle(owner, entity, {})
875 {}
876
ComponentHandle(MaterialComponentManager * owner,Entity entity,const MaterialComponent & data)877 MaterialComponentManager::ComponentHandle::ComponentHandle(
878 MaterialComponentManager* owner, Entity entity, const MaterialComponent& data) noexcept
879 : manager_(owner), entity_(entity), data_(data)
880 {}
881
ComponentHandle(ComponentHandle && other)882 MaterialComponentManager::ComponentHandle::ComponentHandle(ComponentHandle&& other) noexcept
883 : manager_(other.manager_), entity_(exchange(other.entity_, {})), cachedShader_(exchange(other.cachedShader_, {})),
884 metaData_(exchange(other.metaData_, nullptr)), generation_(exchange(other.generation_, 0U)),
885 rLocked_(exchange(other.rLocked_, 0U)), wLocked_(exchange(other.wLocked_, false)),
886 data_(exchange(other.data_, {})), custom_(exchange(other.custom_, {}))
887 {}
888
operator =(ComponentHandle && other)889 MaterialComponentManager::ComponentHandle& MaterialComponentManager::ComponentHandle::operator=(
890 ComponentHandle&& other) noexcept
891 {
892 if (this != &other) {
893 CORE_ASSERT(manager_ == other.manager_);
894 entity_ = exchange(other.entity_, {});
895 cachedShader_ = exchange(other.cachedShader_, {});
896 metaData_ = exchange(other.metaData_, nullptr);
897 generation_ = exchange(other.generation_, 0U);
898 rLocked_ = exchange(other.rLocked_, 0U);
899 wLocked_ = exchange(other.wLocked_, false);
900 dirty_ = exchange(other.dirty_, false);
901 data_ = exchange(other.data_, {});
902 custom_ = exchange(other.custom_, {});
903 }
904 return *this;
905 }
906
907 // ComponentHandle IPropertyHandle
Owner() const908 const IPropertyApi* MaterialComponentManager::ComponentHandle::Owner() const
909 {
910 return this;
911 }
912
Size() const913 size_t MaterialComponentManager::ComponentHandle::Size() const
914 {
915 return sizeof(MaterialComponent);
916 }
917
RLock() const918 const void* MaterialComponentManager::ComponentHandle::RLock() const
919 {
920 CORE_ASSERT(manager_);
921 CORE_ASSERT(!wLocked_);
922 rLocked_++;
923 return &data_;
924 }
925
RUnlock() const926 void MaterialComponentManager::ComponentHandle::RUnlock() const
927 {
928 CORE_ASSERT(manager_);
929 CORE_ASSERT(rLocked_ > 0U);
930 rLocked_--;
931 }
932
WLock()933 void* MaterialComponentManager::ComponentHandle::WLock()
934 {
935 CORE_ASSERT(manager_);
936 CORE_ASSERT(rLocked_ <= 1U && !wLocked_);
937 wLocked_ = true;
938 return &data_;
939 }
940
WUnlock()941 void MaterialComponentManager::ComponentHandle::WUnlock()
942 {
943 CORE_ASSERT(manager_);
944 CORE_ASSERT(wLocked_);
945 wLocked_ = false;
946 // update generation etc..
947 generation_++;
948 if (CORE_NS::EntityUtil::IsValid(entity_)) {
949 dirty_ = true;
950 UpdateMetadata();
951 manager_->Updated(entity_);
952 }
953 }
954
955 // ComponentHandle IPropertyApi
PropertyCount() const956 size_t MaterialComponentManager::ComponentHandle::PropertyCount() const
957 {
958 if (!metaData_) {
959 return manager_->PropertyCount();
960 } else {
961 return metaData_->properties.size();
962 }
963 }
964
MetaData(size_t index) const965 const Property* MaterialComponentManager::ComponentHandle::MetaData(size_t index) const
966 {
967 if (!metaData_) {
968 return manager_->MetaData(index);
969 } else if (index < metaData_->properties.size()) {
970 return &metaData_->properties[index];
971 }
972 return nullptr;
973 }
974
MetaData() const975 array_view<const Property> MaterialComponentManager::ComponentHandle::MetaData() const
976 {
977 if (!metaData_) {
978 return manager_->MetaData();
979 } else {
980 return metaData_->properties;
981 }
982 }
983
Type() const984 uint64_t MaterialComponentManager::ComponentHandle::Type() const
985 {
986 return manager_->Type();
987 }
988
Create() const989 IPropertyHandle* MaterialComponentManager::ComponentHandle::Create() const
990 {
991 return new ComponentHandle(manager_, {}, {});
992 }
993
Clone(const IPropertyHandle * src) const994 IPropertyHandle* MaterialComponentManager::ComponentHandle::Clone(const IPropertyHandle* src) const
995 {
996 if (src) {
997 auto owner = src->Owner();
998 if (owner == this) {
999 auto* h = static_cast<const ComponentHandle*>(src);
1000 return new ComponentHandle(h->manager_, {}, h->data_);
1001 } else if (owner) {
1002 return owner->Clone(src);
1003 }
1004 return manager_->Clone(src);
1005 }
1006 return nullptr;
1007 }
1008
Release(IPropertyHandle * handle) const1009 void MaterialComponentManager::ComponentHandle::Release(IPropertyHandle* handle) const
1010 {
1011 if (handle) {
1012 auto owner = handle->Owner();
1013 if (owner == this) {
1014 auto* componentHandle = static_cast<ComponentHandle*>(handle);
1015 if (auto id = manager_->GetComponentId(componentHandle->entity_);
1016 id != IComponentManager::INVALID_COMPONENT_ID) {
1017 if (manager_->GetData(id) == componentHandle) {
1018 // This is one of the components (bound to an entity) so do nothing
1019 return;
1020 }
1021 }
1022 delete componentHandle;
1023 } else if (owner) {
1024 owner->Release(handle);
1025 }
1026 }
1027 }
1028
1029 // ComponentHandle
operator =(const MaterialComponentManager::ComponentHandle & other)1030 MaterialComponentManager::ComponentHandle& MaterialComponentManager::ComponentHandle::operator=(
1031 const MaterialComponentManager::ComponentHandle& other)
1032 {
1033 if (this != &other) {
1034 // copy the material component, but..
1035 data_ = other.data_;
1036 // ..forget the custom property values and create new ones
1037 data_.customProperties = nullptr;
1038
1039 if (manager_ == other.manager_) {
1040 // for the same manager instance we can use the same metadata
1041 cachedShader_ = other.cachedShader_;
1042 metaData_ = other.metaData_;
1043 } else {
1044 // for a different manager instance find metadata matching the shader or create new metadata
1045 if (!manager_->renderHandleManager_) {
1046 manager_->renderHandleManager_ = GetManager<IRenderHandleComponentManager>(manager_->ecs_);
1047 }
1048
1049 if (manager_->renderHandleManager_) {
1050 auto currentShader =
1051 manager_->renderHandleManager_->GetRenderHandleReference(data_.materialShader.shader);
1052 const auto frameIndex = currentShader ? manager_->shaderManager_.GetFrameIndex(currentShader) : 0U;
1053 const bool updatedShader = other.cachedShader_.frameIndex != frameIndex;
1054 cachedShader_ = { currentShader.GetHandle(), frameIndex };
1055
1056 if (auto pos = manager_->properties_.find(cachedShader_.shader);
1057 (pos != manager_->properties_.end()) && !updatedShader) {
1058 metaData_ = pos->second;
1059 } else {
1060 // start with a blank property collection
1061 auto newMetaData = refcnt_ptr<ReferencedProperties>(new ReferencedProperties);
1062 auto& newProperties = newMetaData->properties;
1063
1064 // insert basic properties from the component
1065 newProperties.insert(newProperties.end(), componentMetaData_.begin(), componentMetaData_.end());
1066 const auto basicProperties = newProperties.size();
1067
1068 // go through the metadata which should be an array of metadata for different component types
1069 if (auto* metaJson = manager_->shaderManager_.GetMaterialMetadata(currentShader);
1070 metaJson && metaJson->is_array()) {
1071 if (auto* material = FindMaterialComponentJson(*metaJson); material) {
1072 // insert properties for textures
1073 AppendProperties(newProperties, newMetaData->stringStorage, *material, componentMetaData_);
1074 }
1075 }
1076
1077 // if the material had metadata with customized TextureInfos try to map old locations to new ones.
1078 if (updatedShader && other.metaData_ && (other.metaData_->properties.size() > basicProperties) &&
1079 (newProperties.size() > basicProperties)) {
1080 auto& oldProperties = other.metaData_->properties;
1081 MapTextureSlots(data_.textures, offsetof(MaterialComponent, textures),
1082 array_view(newProperties.cbegin().ptr() + basicProperties, newProperties.cend().ptr()),
1083 array_view(oldProperties.cbegin().ptr() + basicProperties, oldProperties.cend().ptr()));
1084 }
1085
1086 // replace previous properties with the new updated properties in the cache and this component
1087 // handle
1088 manager_->properties_.insert_or_assign(currentShader.GetHandle(), newMetaData);
1089 metaData_ = move(newMetaData);
1090 }
1091 }
1092 }
1093
1094 // find the custom properties for the shader
1095 const CORE_NS::json::value* propertiesJson = nullptr;
1096 if (data_.materialShader.shader) {
1097 if (!manager_->renderHandleManager_) {
1098 manager_->renderHandleManager_ = GetManager<IRenderHandleComponentManager>(manager_->ecs_);
1099 }
1100
1101 if (manager_->renderHandleManager_) {
1102 auto currentShader =
1103 manager_->renderHandleManager_->GetRenderHandleReference(data_.materialShader.shader);
1104
1105 if (auto* metaJson = manager_->shaderManager_.GetMaterialMetadata(currentShader);
1106 metaJson && metaJson->is_array()) {
1107 if (auto* material = FindMaterialComponentJson(*metaJson); material) {
1108 if (auto* propJson = material->find("customProperties"); propJson && propJson->is_array()) {
1109 propertiesJson = propJson;
1110 }
1111 }
1112 }
1113 }
1114 }
1115
1116 if (metaData_ && propertiesJson) {
1117 // create and fill new POD container
1118 auto newPod = BASE_NS::make_unique<CustomPropertyPodContainer>(CUSTOM_PROPERTY_POD_CONTAINER_BYTE_SIZE);
1119 UpdateCustomPropertyMetadata(*propertiesJson, *newPod);
1120
1121 if (other.custom_) {
1122 newPod->CopyValues(*other.custom_);
1123 }
1124 custom_ = BASE_NS::move(newPod);
1125 data_.customProperties = custom_.get();
1126 } else if (!metaData_) {
1127 if (auto pos = manager_->properties_.find(RenderHandle {}); pos != manager_->properties_.cend()) {
1128 metaData_ = pos->second;
1129 }
1130 }
1131
1132 ++generation_;
1133 dirty_ = true;
1134 manager_->Updated(entity_);
1135 }
1136
1137 return *this;
1138 }
1139
UpdateMetadata() const1140 void MaterialComponentManager::ComponentHandle::UpdateMetadata() const
1141 {
1142 if (!manager_->renderHandleManager_) {
1143 manager_->renderHandleManager_ = GetManager<IRenderHandleComponentManager>(manager_->ecs_);
1144 if (!manager_->renderHandleManager_) {
1145 return;
1146 }
1147 }
1148 const auto currentShader = manager_->renderHandleManager_->GetRenderHandleReference(data_.materialShader.shader);
1149 const auto frameIndex = manager_->shaderManager_.GetFrameIndex(currentShader);
1150 const bool newShader = (cachedShader_.shader != currentShader.GetHandle());
1151 const bool updatedShader = !newShader && (cachedShader_.frameIndex != frameIndex);
1152 if (!metaData_ || newShader || updatedShader) {
1153 cachedShader_ = { currentShader.GetHandle(), frameIndex };
1154
1155 const CORE_NS::json::value* propertiesJson = nullptr;
1156 auto& propertyCache = manager_->properties_;
1157 if ((newShader || updatedShader) && currentShader) {
1158 // start with a blank property collection
1159 auto newMetaData = refcnt_ptr<ReferencedProperties>(new ReferencedProperties);
1160 auto& newProperties = newMetaData->properties;
1161
1162 // insert basic properties from the component
1163 newProperties.insert(newProperties.end(), componentMetaData_.begin(), componentMetaData_.end());
1164 const auto basicProperties = newProperties.size();
1165
1166 // go through the metadata which should be an array of metadata for different component types
1167 if (auto* metaJson = manager_->shaderManager_.GetMaterialMetadata(currentShader);
1168 metaJson && metaJson->is_array()) {
1169 if (auto* material = FindMaterialComponentJson(*metaJson); material) {
1170 // insert properties for textures
1171 AppendProperties(newProperties, newMetaData->stringStorage, *material, componentMetaData_);
1172
1173 if (auto* propJson = material->find("customProperties"); propJson && propJson->is_array()) {
1174 propertiesJson = propJson;
1175 }
1176 }
1177 }
1178 // if the material had metadata with customized TextureInfos try to map old locations to new ones.
1179 if (updatedShader && metaData_ && (metaData_->properties.size() > basicProperties) &&
1180 (newProperties.size() > basicProperties)) {
1181 auto& oldProperties = metaData_->properties;
1182 MapTextureSlots(data_.textures, offsetof(MaterialComponent, textures),
1183 array_view(newProperties.cbegin().ptr() + basicProperties, newProperties.cend().ptr()),
1184 array_view(oldProperties.cbegin().ptr() + basicProperties, oldProperties.cend().ptr()));
1185 }
1186
1187 // replace previous properties with the new updated properties in the cache and this component handle
1188 propertyCache.insert_or_assign(currentShader.GetHandle(), newMetaData);
1189 metaData_ = move(newMetaData);
1190 } else if (auto cached = propertyCache.find(cachedShader_.shader); cached != propertyCache.end()) {
1191 metaData_ = cached->second;
1192 }
1193
1194 data_.customProperties = nullptr;
1195 if (metaData_ && propertiesJson) {
1196 // create and fill new POD container
1197 auto newPod = BASE_NS::make_unique<CustomPropertyPodContainer>(CUSTOM_PROPERTY_POD_CONTAINER_BYTE_SIZE);
1198 UpdateCustomPropertyMetadata(*propertiesJson, *newPod);
1199
1200 if (custom_ && updatedShader) {
1201 newPod->CopyValues(*custom_);
1202 }
1203 custom_ = BASE_NS::move(newPod);
1204 data_.customProperties = custom_.get();
1205 } else if (!metaData_) {
1206 if (auto pos = manager_->properties_.find(RenderHandle {}); pos != manager_->properties_.cend()) {
1207 metaData_ = pos->second;
1208 }
1209 }
1210 }
1211 }
1212
IMaterialComponentManagerInstance(IEcs & ecs)1213 IComponentManager* IMaterialComponentManagerInstance(IEcs& ecs)
1214 {
1215 return new MaterialComponentManager(ecs);
1216 }
1217
IMaterialComponentManagerDestroy(IComponentManager * instance)1218 void IMaterialComponentManagerDestroy(IComponentManager* instance)
1219 {
1220 delete static_cast<MaterialComponentManager*>(instance);
1221 }
1222 CORE3D_END_NAMESPACE()
1223