Cogs.Core
EntityDefinition.cpp
1#include "EntityDefinition.h"
2
3#include "Types.h"
4#include "Context.h"
5#include "EntityStore.h"
6
7#include "Resources/MaterialManager.h"
8#include "Resources/ModelManager.h"
9#include "Resources/MeshManager.h"
10#include "Resources/TextureManager.h"
11#include "Resources/AssetManager.h"
12
13#include "Services/DeferredNameResolution.h"
14
15#include "Serialization/ResourceReader.h"
16
17#include "Foundation/Logging/Logger.h"
18#include "Foundation/Reflection/TypeDatabase.h"
19
20namespace
21{
22 Cogs::Logging::Log logger = Cogs::Logging::getLogger("EntityDefinition");
23}
24
25namespace Cogs
26{
27 namespace Core
28 {
29 template<typename T>
30 void applyFieldValue(void * object, const Reflection::Field * field, T & value)
31 {
32 // Discard const, our T might be const but the object is not.
33 auto fieldPointer = field->getPtr<typename std::remove_reference<typename std::remove_const<T>::type>::type>(object);
34
35 *fieldPointer = value;
36 }
37
38 void updateMaterialInstance(Context * context, MaterialInstance * materialInstance, const MaterialInstanceDefinition & materialInstanceValue)
39 {
40 if (!materialInstance) {
41 LOG_ERROR(logger, "Cannot update invalid material instance.");
42 return;
43 }
44
45 auto material = materialInstance->material;
46
47 if (materialInstanceValue.permutation.size()) {
48 materialInstance->setPermutation(materialInstanceValue.permutation);
49 }
50
51 for (auto & v : materialInstanceValue.variants) {
52 materialInstance->setVariant(v.first, v.second);
53 }
54
55 for (auto & o : materialInstanceValue.options) {
56 materialInstance->setOption(o.first, o.second);
57 }
58
59 auto logError = [](const char * name, const char * type) {
60 LOG_ERROR(logger, "Material property %s of type %s not found.", name, type);
61 };
62
63 for (auto & p : materialInstanceValue.properties) {
64 auto & value = p.second;
65
66 switch (value.type) {
67 case DefaultValueType::Float:
68 {
69 auto key = material->getFloatKey(p.first);
70 if (key != NoProperty) {
71 materialInstance->setFloatProperty(key, value.floatValue);
72 } else {
73 logError(p.first.c_str(), "float");
74 }
75 }
76 break;
77
78 case DefaultValueType::Int:
79 {
80 auto key = material->getIntKey(p.first);
81 if (key != NoProperty) {
82 materialInstance->setIntProperty(key, value.intValue);
83 } else {
84 logError(p.first.c_str(), "int");
85 }
86 }
87 break;
88
89 case DefaultValueType::Bool:
90 {
91 auto key = material->getBoolKey(p.first);
92 if (key != NoProperty) {
93 materialInstance->setBoolProperty(key, value.boolValue);
94 } else {
95 logError(p.first.c_str(), "bool");
96 }
97 }
98 break;
99
100 case DefaultValueType::Vec2:
101 {
102 auto key = material->getVec2Key(p.first);
103 if (key != NoProperty) {
104 materialInstance->setVec2Property(key, value.float2);
105 } else {
106 logError(p.first.c_str(), "float2");
107 }
108 }
109 break;
110
111 case DefaultValueType::Vec3:
112 {
113 auto key = material->getVec3Key(p.first);
114 if (key != NoProperty) {
115 materialInstance->setVec3Property(key, value.float3);
116 } else {
117 logError(p.first.c_str(), "float3");
118 }
119 }
120 break;
121
122 case DefaultValueType::Vec4:
123 {
124 auto key = material->getVec4Key(p.first);
125 if (key != NoProperty) {
126 materialInstance->setVec4Property(key, value.float4);
127 } else {
128 logError(p.first.c_str(), "float4");
129 }
130 }
131 break;
132
133 case DefaultValueType::Texture:
134 {
135 auto key = material->getTextureKey(p.first);
136
137 if (key != NoProperty) {
138 materialInstance->setTextureProperty(key, context->textureManager->getTexture(p.second.value));
139 }
140 }
141 break;
142 default:
143 LOG_ERROR(logger, "Unknown type for material property \"%s\".", p.first.c_str());
144 break;
145 }
146 }
147 }
148
149 void applyMaterialInstanceValue(Context * context, void * component, const Reflection::Field * field, const FieldValue & value)
150 {
151 auto materialInstanceDefinition = value.asMaterialInstance();
152
153 auto materialInstance = context->assetManager->instantiateMaterialInstance(AssetHandle::NoHandle, *materialInstanceDefinition);
154
155 auto fieldPointer = field->getPtr<MaterialInstanceHandle>(component);
156 if (!field->getDimensions()) {
157 *fieldPointer = materialInstance;
158 } else {
159 *fieldPointer = materialInstance;
160 }
161 }
162 }
163}
164
165void Cogs::Core::applyFieldValues(Context * context, std::span<FieldValue> values, ComponentModel::Entity * entity)
166{
167 for (auto & value : values) {
168 if (value.componentId == Reflection::NoType) {
169 LOG_ERROR(logger, "Could not apply field values for invalid type.");
170
171 continue;
172 }
173
174 if (!entity->getComponentHandle(value.componentId)) {
175 context->store->addComponent(entity, value.componentId);
176 }
177
178 auto & type = Reflection::TypeDatabase::getType(value.componentId);
179
180 if (value.fieldId == Reflection::NoField) continue;
181
182 auto field = type.getField(value.fieldId);
183
184 if (!field) {
185 LOG_ERROR(logger, "Field %s not found on type %s.", field->getName().c_str(), type.getName().c_str());
186
187 continue;
188 }
189
190 auto component = entity->getComponent<ComponentModel::Component>(type);
191
192 if (!component) {
193 LOG_ERROR(logger, "Component of type %s not found on entity.", type.getName().c_str());
194
195 continue;
196 }
197
198 switch (value.type) {
199 case DefaultValueType::String:
200 applyFieldValue(component, field, value.value);
201 break;
202 case DefaultValueType::MultiString:
203 applyFieldValue(component, field, value.values);
204 break;
205 case DefaultValueType::IntArray:
206 applyFieldValue(component, field, value.intValues);
207 break;
208 case DefaultValueType::FloatArray:
209 applyFieldValue(component, field, value.floatValues);
210 break;
211 case DefaultValueType::Vec2Array:
212 applyFieldValue(component, field, value.vec2Values);
213 break;
214 case DefaultValueType::Vec3Array:
215 applyFieldValue(component, field, value.vec3Values);
216 break;
217 case DefaultValueType::Vec4Array:
218 applyFieldValue(component, field, value.vec4Values);
219 break;
220 case DefaultValueType::Bool:
221 applyFieldValue(component, field, value.boolValue);
222 break;
223 case DefaultValueType::Int:
224 applyFieldValue(component, field, value.intValue);
225 break;
226 case DefaultValueType::Enum:
227 applyFieldValue(component, field, value.intValue);
228 break;
229 case DefaultValueType::Float:
230 applyFieldValue(component, field, value.floatValue);
231 break;
232 case DefaultValueType::Vec2:
233 applyFieldValue(component, field, value.float2);
234 break;
235 case DefaultValueType::Vec3:
236 applyFieldValue(component, field, value.float3);
237 break;
238 case DefaultValueType::Vec4:
239 applyFieldValue(component, field, value.float4);
240 break;
241 case DefaultValueType::Mat4:
242 applyFieldValue(component, field, value.mat4);
243 break;
244 case DefaultValueType::DVec2:
245 applyFieldValue(component, field, value.double2);
246 break;
247 case DefaultValueType::DVec3:
248 applyFieldValue(component, field, value.double3);
249 break;
250 case DefaultValueType::DVec4:
251 applyFieldValue(component, field, value.double4);
252 break;
253 case DefaultValueType::Quaternion:
254 applyFieldValue(component, field, value.quat);
255 break;
256 case DefaultValueType::MaterialInstance:
257 applyMaterialInstanceValue(context, component, field, value);
258 break;
259 case DefaultValueType::Model:
260 {
261 if (value.value.front() == '$') {
262 auto name = value.value.substr(1);
263 auto model = context->modelManager->getByName(name);
264 if (model) {
265 applyFieldValue(component, field, model);
266 } else {
267 LOG_ERROR(logger, "Could not find handle for model \"%s\".", name.c_str());
268 }
269 } else {
270 auto model = context->modelManager->loadModel(value.value, NoResourceId, ModelLoadFlags::None);
271 applyFieldValue(component, field, model);
272 }
273 }
274 break;
275
276 case DefaultValueType::Asset:
277 {
278 if (value.value.front() == '$') {
279 auto asset = context->assetManager->getByName(value.value.substr(1));
280 applyFieldValue(component, field, asset);
281 } else {
282 auto asset = context->assetManager->loadAsset(value.value, NoResourceId, AssetLoadFlags::None);
283 applyFieldValue(component, field, asset);
284 }
285 }
286 break;
287 case DefaultValueType::Gui:
288 {
289 auto * mgr = context->engine->getResourceManagerByValueType((int)DefaultValueType::Gui);
290 if (mgr) {
291 mgr->applyFieldValue(component, field, value);
292 }
293 else {
294 LOG_ERROR(logger, "No manager for DefaultValueType::Gui");
295 }
296 }
297 break;
298 case DefaultValueType::Mesh:
299 {
300 auto mesh = context->meshManager->getMesh(value.value);
301 applyFieldValue(component, field, mesh);
302 }
303 break;
304 case DefaultValueType::Texture:
305 {
306 auto texture = context->textureManager->getTexture(value.value);
307 applyFieldValue(component, field, texture);
308 }
309 break;
310 case DefaultValueType::Entity:
311 if (value.value.empty()) {
312 WeakEntityPtr ptr;
313 applyFieldValue(component, field, ptr);
314 }
315 else if (1 < value.value.size() && value.value[0] != '$') {
316 LOG_WARNING(logger, "Missing '$' in entity field \"%s\".", value.value.c_str());
317 }
318 else {
319 auto entity_ = context->store->findEntity(StringView(value.value).substr(1));
320 if (entity_) {
321 if (field->getTypeId() == Reflection::TypeDatabase::getType<EntityPtr>().getTypeId()) {
322 applyFieldValue(component, field, entity_);
323 }
324 else if (field->getTypeId() == Reflection::TypeDatabase::getType<WeakEntityPtr>().getTypeId()) {
325 WeakEntityPtr weakEntity = entity_;
326 applyFieldValue(component, field, weakEntity);
327 }
328 }
329 else {
330 auto handle = component->getComponentHandle(component->getTypeId());
331 context->deferredResolution->scheduleResolution(handle,
332 field,
333 value.value.substr(1));
334 }
335 }
336 break;
337 case DefaultValueType::EntityArray:
338 {
339 std::vector<EntityPtr> entities(value.values.size());
340 for (size_t i = 0; i < value.values.size(); i++) {
341 if (value.values[i].size() < 2 || value.values[i][0] != '$') {
342 LOG_WARNING(logger, "Missing '$' name in entity field \"%s\".", value.values[i].c_str());
343 continue;
344 }
345
346 entities[i] = context->store->findEntity(StringView(value.values[i]).substr(1));
347 if (!entities[i]) {
348 auto handle = component->getComponentHandle(component->getTypeId());
349 context->deferredResolution->scheduleResolution(handle,
350 field,
351 static_cast<int>(i),
352 value.values[i].substr(1));
353 }
354 }
355 if (field->getTypeId() == Reflection::TypeDatabase::getType<std::vector<EntityPtr>>().getTypeId()) {
356 applyFieldValue(component, field, entities);
357 }
358 else if (field->getTypeId() == Reflection::TypeDatabase::getType<std::vector<WeakEntityPtr>>().getTypeId()) {
359 std::vector<WeakEntityPtr> weakEntities;
360 for (auto & e : entities) weakEntities.push_back(e);
361 applyFieldValue(component, field, weakEntities);
362 }
363 }
364 break;
365 case DefaultValueType::Extension:
366 {
367 auto ext = getExtensionReader(field->getTypeId());
368
369 if (ext) {
370 ext->setCallback(component, field, value);
371 }
372 }
373 break;
374 default:
375 LOG_WARNING(logger, "Unhandled value type for field %s.", field->getName().c_str());
376 break;
377 }
378
379 component->setFieldChanged(value.fieldId);
380 }
381}
Base class for Component instances.
Definition: Component.h:143
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
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
std::unique_ptr< class Engine > engine
Engine instance.
Definition: Context.h:222
ComponentModel::Component * addComponent(ComponentModel::Entity *entity, Reflection::TypeId typeId)
Add a component of the given type to the entity.
EntityPtr findEntity(const StringView &name, const ComponentModel::Entity *root=nullptr, EntityFind findOptions=EntityFind::Default) const
Finds an entity with the given name.
Log implementation class.
Definition: LogManager.h:139
Field definition describing a single data member of a data structure.
Definition: Field.h:68
const Name & getName() const
Get the name of the field.
Definition: Field.h:136
size_t getDimensions() const
Get the array dimensions of the field. Returns zero if the field is not an array.
Definition: Field.h:156
TypeId getTypeId() const
Get the type id of the field.
Definition: Field.h:140
FieldValueType * getPtr(void *container) const
Get a pointer to this field on the given container.
Definition: Field.h:112
Provides a weakly referenced view over the contents of a string.
Definition: StringView.h:24
constexpr StringView substr(size_t offset, size_t count=NoPosition) const noexcept
Get the given sub string.
Definition: StringView.h:258
void updateMaterialInstance(Context *context, MaterialInstance *materialInstance, const MaterialInstanceDefinition &materialInstanceValue)
Apply material instance values.
void applyFieldValues(class Context *context, std::span< FieldValue > entityDefinition, ComponentModel::Entity *entity)
Apply defaults from the given entityDefinition to the given entity.
std::weak_ptr< ComponentModel::Entity > WeakEntityPtr
Weak Smart pointer for Entity access.
Definition: EntityPtr.h:18
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Definition: LogManager.h:180
Contains all Cogs related functionality.
Definition: FieldSetter.h:23
Material instances represent a specialized Material combined with state for all its buffers and prope...
void setFloatProperty(const VariableKey key, float value)
Set the float property with the given key to value.
void setVec3Property(const VariableKey key, glm::vec3 value)
Set the vec3 property with the given key to value.
void setOption(const StringView &key, const StringView &value)
Sets the option with the given key to a value parsed from the value string.
void setTextureProperty(const StringView &key, TextureHandle value)
Set the texture property with the given key to the texture resource held by value.
void setVec2Property(const VariableKey key, glm::vec2 value)
Set the vec2 property with the given key to value.
void setVec4Property(const VariableKey key, glm::vec4 value)
Set the vec4 property with the given key to value.
void setBoolProperty(const VariableKey key, bool value)
Set the bool property with the given key to value.
Material * material
Material resource this MaterialInstance is created from.
static const ResourceHandle_t NoHandle
Handle representing a default (or none if default not present) resource.
const char * c_str() const
Gets the name as a null-terminated string.
Definition: Name.h:116