Cogs.Core
ExpressionComponent.cpp
1#include "ExpressionComponent.h"
2
3#include "Foundation/HashSequence.h"
4#include "Foundation/Logging/Logger.h"
5
6#include "Types.h"
7
8#include "Context.h"
9
10#include "Services/Time.h"
11#include "Utilities/Parsing.h"
12
13using namespace Cogs::Reflection;
14
15namespace
16{
17 Cogs::Logging::Log logger = Cogs::Logging::getLogger("ExpressionComponent");
18}
19
20void Cogs::Core::ExpressionComponent::registerType()
21{
22 Field fields[] = {
23 Field(Name("targets"), &ExpressionComponent::targets),
24 Field(Name("expressions"), &ExpressionComponent::expressions),
25 };
26
27 Method methods[] = {
28 Method(Name("update"), &ExpressionComponent::update),
29 Method(Name("initialize"), &ExpressionComponent::initialize),
30 Method(Name("cleanup"), &ExpressionComponent::cleanup),
31 };
32
33 DynamicComponent::registerDerivedType<ExpressionComponent>().setFields(fields).setMethods(methods);
34}
35
36void Cogs::Core::ExpressionComponent::initialize(Context * context)
37{
38 this->context = context;
39}
40
41void Cogs::Core::ExpressionComponent::update()
42{
43 ec.add("t", context->time->getAnimationTime());
44 ec.add("id", static_cast<double>(getContainer()->getId()));
45
46 if (targets.size() != expressions.size()) {
47 LOG_ERROR(logger, "Invalid field sizes (%zd, %zd)", targets.size(), expressions.size());
48 return;
49 }
50
51 if (targets.size() < compiledExpressions.size()) {
52 for (size_t i = targets.size() - 1; i < compiledExpressions.size(); ++i) {
53 ec.free(compiledExpressions[i].expression);
54 }
55 }
56
57 compiledExpressions.resize(targets.size());
58
59 for (size_t i = 0; i < targets.size(); ++i) {
60 auto & compiledExpression = compiledExpressions[i];
61
62 if (!compiledExpression.expression || hasChanged()) {
63 size_t hash = hashSequence(expressions[i], targets[i]);
64
65 if (compiledExpression.expression && compiledExpression.expression->hash == hash) {
66 continue;
67 }
68
69 if (compiledExpression.expression) {
70 ec.free(compiledExpression.expression);
71 }
72
73 if (!expressions[i].size()) continue;
74
75 ec.add(targets[i], 0);
76 compiledExpression.expression = ec.compile(expressions[i], targets[i]);
77
78 TokenStream tokens;
79 split(targets[i], ".", tokens);
80
81 if (tokens.size() == 2 || tokens.size() == 3) {
82 auto & componentType = TypeDatabase::getType(tokens[0]);
83
84 compiledExpression.componentHandle = getComponentHandle(componentType.getTypeId());
85
86 auto field = componentType.getField(tokens[1]);
87
88 if (!field) {
89 LOG_ERROR(logger, "Could not find field %.*s on type %.*s", StringViewFormat(tokens[1]), StringViewFormat(tokens[0]));
90 continue;
91 }
92
93 auto & fieldType = TypeDatabase::getType(field->getTypeId());
94
95 {
96 bool isValid = fieldType == TypeDatabase::getType<float>() ||
97 fieldType == TypeDatabase::getType<glm::vec2>() ||
98 fieldType == TypeDatabase::getType<glm::vec3>() ||
99 fieldType == TypeDatabase::getType<glm::vec4>() ||
100 fieldType == TypeDatabase::getType<glm::quat>();
101
102 if (!isValid) {
103 LOG_ERROR(logger, "Target field type %s not compatible with expression output.", fieldType.getName().c_str());
104 continue;
105 }
106 }
107
108 compiledExpression.componentId = componentType.getTypeId();
109 compiledExpression.fieldId = componentType.getFieldId(field);
110
111 if (tokens.size() == 3) {
112 auto memberField = fieldType.getField(tokens[2]);
113
114 if (!memberField) {
115 LOG_ERROR(logger, "Could not find field %.*s on type %s", StringViewFormat(tokens[2]), fieldType.getName().c_str());
116 continue;
117 }
118
119 compiledExpression.memberId = fieldType.getFieldId(memberField);
120 }
121 }
122 }
123
124 if (compiledExpression.expression &&
125 compiledExpression.componentId != Reflection::NoType &&
126 compiledExpression.fieldId != Reflection::NoField) {
127 const double value = ec.update(compiledExpression.expression, 0.0);
128
129 auto component = compiledExpression.componentHandle.resolve();
130 assert(component && "Component must be valid pointer.");
131
132 auto field = TypeDatabase::getType(compiledExpression.componentId).getField(compiledExpression.fieldId);
133 assert(field && "Field must be valid pointer.");
134
135 auto & fieldType = TypeDatabase::getType(field->getTypeId());
136 auto fieldValue = field->getPtr<float>(component);
137
138 if (compiledExpression.memberId != Reflection::NoType) {
139 auto memberField = fieldType.getField(compiledExpression.memberId);
140
141 auto memberValue = memberField->getPtr<float>(fieldValue);
142 *memberValue = static_cast<float>(value);
143 } else {
144 *fieldValue = static_cast<float>(value);
145 }
146
147 component->setChanged();
148 }
149 }
150}
151
152void Cogs::Core::ExpressionComponent::cleanup(Context * /*context*/)
153{
154 ec.clear();
155 compiledExpressions.clear();
156}
Log implementation class.
Definition: LogManager.h:140
Field definition describing a single data member of a data structure.
Definition: Field.h:70
Simple method definition.
Definition: Method.h:72
static const Type & getType()
Get the Type of the given template argument.
Definition: TypeDatabase.h:168
const Field * getField(const Name &name) const
Get a pointer to the field info of the field with the given name.
Definition: Type.cpp:53
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Definition: LogManager.h:181
Contains reflection support.
Definition: Component.h:11
constexpr FieldId NoField
No field id.
Definition: Name.h:60
constexpr TypeId NoType
Definition of no type.
Definition: Name.h:51
constexpr size_t hash() noexcept
Simple getter function that returns the initial value for fnv1a hashing.
Definition: HashFunctions.h:62
constexpr size_t hashSequence(const T &t, const U &u)
Hash the last two items in a sequence of objects.
Definition: HashSequence.h:8
Represents an unique name.
Definition: Name.h:70