Skip to content

Commit e129194

Browse files
authored
Simplify binder logic (playcanvas#2401)
* simplify binder * invoke setter when driving vector members
1 parent 9cedb85 commit e129194

File tree

1 file changed

+113
-96
lines changed

1 file changed

+113
-96
lines changed

src/framework/components/anim/binder.js

Lines changed: 113 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,66 @@ function AnimComponentBinder(animComponent, graph) {
2121
AnimComponentBinder.prototype = Object.create(DefaultAnimBinder.prototype);
2222
AnimComponentBinder.prototype.constructor = AnimComponentBinder;
2323

24+
AnimComponentBinder._packFloat = function (values) {
25+
return values[0];
26+
};
27+
28+
AnimComponentBinder._packBoolean = function (values) {
29+
return !!values[0];
30+
};
31+
32+
AnimComponentBinder._packVec2 = function () {
33+
var v = new Vec2();
34+
return function (values) {
35+
v.x = values[0];
36+
v.y = values[1];
37+
return v;
38+
};
39+
}();
40+
41+
AnimComponentBinder._packVec3 = function () {
42+
var v = new Vec3();
43+
return function (values) {
44+
v.x = values[0];
45+
v.y = values[1];
46+
v.z = values[2];
47+
return v;
48+
};
49+
}();
50+
51+
AnimComponentBinder._packVec4 = function () {
52+
var v = new Vec4();
53+
return function (values) {
54+
v.x = values[0];
55+
v.y = values[1];
56+
v.z = values[2];
57+
v.w = values[3];
58+
return v;
59+
};
60+
}();
61+
62+
AnimComponentBinder._packColor = function () {
63+
var v = new Color();
64+
return function (values) {
65+
v.r = values[0];
66+
v.g = values[1];
67+
v.b = values[2];
68+
v.a = values[3];
69+
return v;
70+
};
71+
}();
72+
73+
AnimComponentBinder._packQuat = function () {
74+
var v = new Quat();
75+
return function (values) {
76+
v.x = values[0];
77+
v.y = values[1];
78+
v.z = values[2];
79+
v.w = values[3];
80+
return v;
81+
};
82+
}();
83+
2484
Object.assign(AnimComponentBinder.prototype, {
2585
resolve: function (path) {
2686
var pathSections = this.propertyLocator.decode(path);
@@ -78,81 +138,56 @@ Object.assign(AnimComponentBinder.prototype, {
78138
return currEntity._parent.findByPath(entityHierarchy.join('/'));
79139
},
80140

81-
_setComponentProperty: function (propertyComponent, propertyHierarchy) {
82-
// trigger the component property setter function
83-
propertyComponent[propertyHierarchy[0]] = propertyComponent[propertyHierarchy[0]]; // eslint-disable-line no-self-assign
141+
// resolve an object path
142+
_resolvePath: function (object, path, resolveLeaf) {
143+
var steps = path.length - (resolveLeaf ? 0 : 1);
144+
for (var i = 0; i < steps; i++) {
145+
object = object[path[i]];
146+
}
147+
return object;
84148
},
85149

86-
_floatSetter: function (propertyComponent, propertyHierarchy) {
87-
var propertyParent = this._getProperty(propertyComponent, propertyHierarchy, true);
88-
var propertyKey = propertyHierarchy[propertyHierarchy.length - 1];
89-
var setter = function (values) {
90-
propertyParent[propertyKey] = values[0];
91-
this._setComponentProperty(propertyComponent, propertyHierarchy);
92-
};
93-
return setter.bind(this);
94-
},
95-
_booleanSetter: function (propertyComponent, propertyHierarchy) {
96-
var propertyParent = this._getProperty(propertyComponent, propertyHierarchy, true);
97-
var propertyKey = propertyHierarchy[propertyHierarchy.length - 1];
98-
var setter = function (values) {
99-
propertyParent[propertyKey] = !!values[0];
100-
this._setComponentProperty(propertyComponent, propertyHierarchy);
101-
};
102-
return setter.bind(this);
103-
},
104-
_colorSetter: function (propertyComponent, propertyHierarchy) {
105-
var color = this._getProperty(propertyComponent, propertyHierarchy);
106-
var setter = function (values) {
107-
if (values[0]) color.r = values[0];
108-
if (values[1]) color.g = values[1];
109-
if (values[2]) color.b = values[2];
110-
if (values[3]) color.a = values[3];
111-
this._setComponentProperty(propertyComponent, propertyHierarchy);
112-
};
113-
return setter.bind(this);
114-
},
115-
_vecSetter: function (propertyComponent, propertyHierarchy) {
116-
var vector = this._getProperty(propertyComponent, propertyHierarchy);
117-
var setter = function (values) {
118-
if (values[0]) vector.x = values[0];
119-
if (values[1]) vector.y = values[1];
120-
if (values[2]) vector.z = values[2];
121-
if (values[3]) vector.w = values[3];
122-
this._setComponentProperty(propertyComponent, propertyHierarchy);
123-
};
124-
return setter.bind(this);
125-
},
150+
// construct a setter function for the property located at 'path' from the base object. packFunc
151+
// is a function which takes the animation values array and packages them for the target property
152+
// in the correct format (i.e. vec2, quat, color etc).
153+
_setter: function (object, path, packFunc) {
154+
var obj = this._resolvePath(object, path);
155+
var key = path[path.length - 1];
126156

127-
_getProperty: function (propertyComponent, propertyHierarchy, returnParent) {
128-
var property = propertyComponent;
129-
var steps = propertyHierarchy.length;
130-
if (returnParent) {
131-
steps--;
157+
// if the object has a setter function, use it
158+
var setterFunc = "set" + key.substring(0, 1).toUpperCase() + key.substring(1);
159+
if (obj[setterFunc]) {
160+
var func = obj[setterFunc].bind(obj);
161+
return function (values) {
162+
func(packFunc(values));
163+
};
132164
}
133-
for (var i = 0; i < steps; i++) {
134-
property = property[propertyHierarchy[i]];
165+
166+
var prop = obj[key];
167+
168+
// if the target property has a copy function, use it (vec3, color, quat)
169+
if (typeof prop === 'object' && prop.hasOwnProperty('copy')) {
170+
return function (values) {
171+
prop.copy(packFunc(values));
172+
};
135173
}
136-
return property;
137-
},
138174

139-
_getEntityProperty: function (propertyHierarchy) {
140-
var entityProperties = [
141-
'localScale',
142-
'localPosition',
143-
'localRotation',
144-
'localEulerAngles',
145-
'position',
146-
'rotation',
147-
'eulerAngles'
148-
];
149-
var entityProperty;
150-
for (var i = 0; i < entityProperties.length; i++) {
151-
if (propertyHierarchy.indexOf(entityProperties[i]) !== -1) {
152-
entityProperty = entityProperties[i];
153-
}
175+
// when animating individual members of vec/colour/quaternion, we must also invoke the
176+
// object's setter. this is required by some component properties which have custom
177+
// handlers which propagate the changes correctly.
178+
if ([Vec2, Vec3, Vec4, Color, Quat].indexOf(obj.constructor) !== -1 && path.length > 1) {
179+
var parent = path.length > 2 ? this._resolvePath(object, path.slice(0, -1)) : object;
180+
var objKey = path[path.length - 2];
181+
return function (values) {
182+
obj[key] = packFunc(values);
183+
parent[objKey] = obj;
184+
};
154185
}
155-
return entityProperty;
186+
187+
// otherwise set the property directly (float, boolean)
188+
return function (values) {
189+
obj[key] = packFunc(values);
190+
};
156191
},
157192

158193
_createAnimTargetForProperty: function (propertyComponent, propertyHierarchy) {
@@ -167,7 +202,7 @@ Object.assign(AnimComponentBinder.prototype, {
167202
}
168203
}
169204

170-
var property = this._getProperty(propertyComponent, propertyHierarchy);
205+
var property = this._resolvePath(propertyComponent, propertyHierarchy, true);
171206

172207
if (typeof property === 'undefined')
173208
return null;
@@ -177,37 +212,37 @@ Object.assign(AnimComponentBinder.prototype, {
177212
var animDataComponents;
178213

179214
if (typeof property === 'number') {
180-
setter = this._floatSetter(propertyComponent, propertyHierarchy);
215+
setter = this._setter(propertyComponent, propertyHierarchy, AnimComponentBinder._packFloat);
181216
animDataType = 'vector';
182217
animDataComponents = 1;
183218
} else if (typeof property === 'boolean') {
184-
setter = this._booleanSetter(propertyComponent, propertyHierarchy);
219+
setter = this._setter(propertyComponent, propertyHierarchy, AnimComponentBinder._packBoolean);
185220
animDataType = 'vector';
186221
animDataComponents = 1;
187222
} else if (typeof property === 'object') {
188223
switch (property.constructor) {
189224
case Vec2:
190-
setter = this._vecSetter(propertyComponent, propertyHierarchy);
225+
setter = this._setter(propertyComponent, propertyHierarchy, AnimComponentBinder._packVec2);
191226
animDataType = 'vector';
192227
animDataComponents = 2;
193228
break;
194229
case Vec3:
195-
setter = this._vecSetter(propertyComponent, propertyHierarchy);
230+
setter = this._setter(propertyComponent, propertyHierarchy, AnimComponentBinder._packVec3);
196231
animDataType = 'vector';
197232
animDataComponents = 3;
198233
break;
199234
case Vec4:
200-
setter = this._vecSetter(propertyComponent, propertyHierarchy);
235+
setter = this._setter(propertyComponent, propertyHierarchy, AnimComponentBinder._packVec4);
201236
animDataType = 'vector';
202237
animDataComponents = 4;
203238
break;
204239
case Color:
205-
setter = this._colorSetter(propertyComponent, propertyHierarchy);
240+
setter = this._setter(propertyComponent, propertyHierarchy, AnimComponentBinder._packColor);
206241
animDataType = 'vector';
207242
animDataComponents = 4;
208243
break;
209244
case Quat:
210-
setter = this._vecSetter(propertyComponent, propertyHierarchy);
245+
setter = this._setter(propertyComponent, propertyHierarchy, AnimComponentBinder._packQuat);
211246
animDataType = 'quaternion';
212247
animDataComponents = 4;
213248
break;
@@ -216,33 +251,15 @@ Object.assign(AnimComponentBinder.prototype, {
216251
}
217252
}
218253

219-
// for entity properties we cannot just set their values, we must also call the values setter function.
220-
var entityProperty = this._getEntityProperty(propertyHierarchy);
221-
if (entityProperty) {
222-
// create the function name of the entity properties setter
223-
var entityPropertySetterFunctionName = "set" +
224-
entityProperty.substring(0, 1).toUpperCase() +
225-
entityProperty.substring(1);
226-
// record the function for entities animated property
227-
var entityPropertySetterFunction = propertyComponent[entityPropertySetterFunctionName].bind(propertyComponent);
228-
// store the property
229-
var propertyObject = this._getProperty(propertyComponent, [entityProperty]);
230-
var entityPropertySetter = function (values) {
231-
// first set new values on the property as before
232-
setter(values);
233-
// call the setter function for entities animated property using the newly set property value
234-
entityPropertySetterFunction(propertyObject);
235-
};
236-
return new AnimTarget(entityPropertySetter.bind(this), animDataType, animDataComponents);
237-
} else if (propertyHierarchy.indexOf('material') !== -1) {
254+
// materials must have update called after changing settings
255+
if (propertyHierarchy.indexOf('material') !== -1) {
238256
return new AnimTarget(function (values) {
239257
setter(values);
240258
propertyComponent.material.update();
241259
}, animDataType, animDataComponents);
242260
}
243261

244262
return new AnimTarget(setter, animDataType, animDataComponents);
245-
246263
}
247264
});
248265

0 commit comments

Comments
 (0)