Skip to content

Commit efcf01f

Browse files
authored
Various fixes for glTF interactivity (#16768)
* Added support for operations involving `Quaternion` and `Vector4` types in `FlowGraphAddBlock`, `FlowGraphSubtractBlock`, `FlowGraphMultiplyBlock`, and `FlowGraphDivideBlock`. Returns `Vector4` when the inputs are mixed types. * Added logic to stop running animations when setting properties in `FlowGraphSetPropertyBlock`, preventing conflicts with ongoing animations. This is buried in [the spec](https://github.com/KhronosGroup/glTF/blob/interactivity/extensions/2.0/Khronos/KHR_interactivity/Specification.adoc) `4.1.4.2.4, step 5` and `4.1.4.2.5, step 8`. * Fixed incorrect code that causes a script error in `FlowGraphSetVariableBlock` that stop animations targeting the same variable.
1 parent 862f455 commit efcf01f

File tree

10 files changed

+69
-55
lines changed

10 files changed

+69
-55
lines changed

packages/dev/core/src/FlowGraph/Blocks/Data/Math/flowGraphMathBlocks.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import type { FlowGraphMathOperationType, FlowGraphNumber } from "core/FlowGraph
1515
import { _AreSameIntegerClass, _AreSameMatrixClass, _AreSameVectorOrQuaternionClass, _GetClassNameOf, getNumericValue, isNumeric } from "core/FlowGraph/utils";
1616

1717
/**
18-
* A configuration interface for math blocks
18+
* A configuration interface for math blocks
1919
*/
2020
export interface IFlowGraphMathBlockConfiguration extends IFlowGraphBlockConfiguration {
2121
/**
@@ -61,6 +61,10 @@ export class FlowGraphAddBlock extends FlowGraphBinaryOperationBlock<FlowGraphMa
6161
if (_AreSameVectorOrQuaternionClass(aClassName, bClassName) || _AreSameMatrixClass(aClassName, bClassName) || _AreSameIntegerClass(aClassName, bClassName)) {
6262
// cast to vector3, but any other cast will be fine
6363
return (a as Vector3).add(b as Vector3);
64+
} else if (aClassName === FlowGraphTypes.Quaternion || bClassName === FlowGraphTypes.Vector4) {
65+
return new Vector4((a as Quaternion).x, (a as Quaternion).y, (a as Quaternion).z, (a as Quaternion).w).addInPlace(b as Vector4);
66+
} else if (aClassName === FlowGraphTypes.Vector4 || bClassName === FlowGraphTypes.Quaternion) {
67+
return (a as Vector4).add(b as Quaternion);
6468
} else {
6569
// at this point at least one of the variables is a number.
6670
if (this.config?.preventIntegerFloatArithmetic && typeof a !== typeof b) {
@@ -97,6 +101,10 @@ export class FlowGraphSubtractBlock extends FlowGraphBinaryOperationBlock<FlowGr
97101
if (_AreSameVectorOrQuaternionClass(aClassName, bClassName) || _AreSameIntegerClass(aClassName, bClassName) || _AreSameMatrixClass(aClassName, bClassName)) {
98102
// cast to vector3, but it can be casted to any vector type
99103
return (a as Vector3).subtract(b as Vector3);
104+
} else if (aClassName === FlowGraphTypes.Quaternion || bClassName === FlowGraphTypes.Vector4) {
105+
return new Vector4((a as Quaternion).x, (a as Quaternion).y, (a as Quaternion).z, (a as Quaternion).w).subtractInPlace(b as Vector4);
106+
} else if (aClassName === FlowGraphTypes.Vector4 || bClassName === FlowGraphTypes.Quaternion) {
107+
return (a as Vector4).subtract(b as Quaternion);
100108
} else {
101109
// at this point at least one of the variables is a number.
102110
if (this.config?.preventIntegerFloatArithmetic && typeof a !== typeof b) {
@@ -130,6 +138,10 @@ export class FlowGraphMultiplyBlock extends FlowGraphBinaryOperationBlock<FlowGr
130138
if (_AreSameVectorOrQuaternionClass(aClassName, bClassName) || _AreSameIntegerClass(aClassName, bClassName)) {
131139
// cast to vector3, but it can be casted to any vector type
132140
return (a as Vector3).multiply(b as Vector3);
141+
} else if (aClassName === FlowGraphTypes.Quaternion || bClassName === FlowGraphTypes.Vector4) {
142+
return new Vector4((a as Quaternion).x, (a as Quaternion).y, (a as Quaternion).z, (a as Quaternion).w).multiplyInPlace(b as Vector4);
143+
} else if (aClassName === FlowGraphTypes.Vector4 || bClassName === FlowGraphTypes.Quaternion) {
144+
return (a as Vector4).multiply(b as Quaternion);
133145
} else if (_AreSameMatrixClass(aClassName, bClassName)) {
134146
if (this.config?.useMatrixPerComponent) {
135147
// this is the definition of multiplication of glTF interactivity
@@ -194,6 +206,10 @@ export class FlowGraphDivideBlock extends FlowGraphBinaryOperationBlock<FlowGrap
194206
aClone.z /= (b as Quaternion).z;
195207
aClone.w /= (b as Quaternion).w;
196208
return aClone;
209+
} else if (aClassName === FlowGraphTypes.Quaternion || bClassName === FlowGraphTypes.Vector4) {
210+
return new Vector4((a as Quaternion).x, (a as Quaternion).y, (a as Quaternion).z, (a as Quaternion).w).divideInPlace(b as Vector4);
211+
} else if (aClassName === FlowGraphTypes.Vector4 || bClassName === FlowGraphTypes.Quaternion) {
212+
return (a as Vector4).divide(b as Quaternion);
197213
} else if (_AreSameMatrixClass(aClassName, bClassName)) {
198214
if (this.config?.useMatrixPerComponent) {
199215
// get a's m as array, and divide each component with b's m

packages/dev/core/src/FlowGraph/Blocks/Event/flowGraphMeshPickEventBlock.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,14 +113,14 @@ export class FlowGraphMeshPickEventBlock extends FlowGraphEventBlock {
113113
/**
114114
* @internal
115115
*/
116-
public _preparePendingTasks(_context: FlowGraphContext): void {
116+
public override _preparePendingTasks(_context: FlowGraphContext): void {
117117
// no-op
118118
}
119119

120120
/**
121121
* @internal
122122
*/
123-
public _cancelPendingTasks(_context: FlowGraphContext): void {
123+
public override _cancelPendingTasks(_context: FlowGraphContext): void {
124124
// no-op
125125
}
126126

packages/dev/core/src/FlowGraph/Blocks/Event/flowGraphReceiveCustomEventBlock.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ export class FlowGraphReceiveCustomEventBlock extends FlowGraphEventBlock {
4545
}
4646
}
4747

48-
public _preparePendingTasks(context: FlowGraphContext): void {
48+
public override _preparePendingTasks(context: FlowGraphContext): void {
4949
const observable = context.configuration.coordinator.getCustomEventObservable(this.config.eventId);
5050
// check if we are not exceeding the max number of events
5151
if (observable && observable.hasObservers() && observable.observers.length > FlowGraphCoordinator.MaxEventsPerType) {
@@ -62,7 +62,7 @@ export class FlowGraphReceiveCustomEventBlock extends FlowGraphEventBlock {
6262
});
6363
context._setExecutionVariable(this, "_eventObserver", eventObserver);
6464
}
65-
public _cancelPendingTasks(context: FlowGraphContext): void {
65+
public override _cancelPendingTasks(context: FlowGraphContext): void {
6666
const observable = context.configuration.coordinator.getCustomEventObservable(this.config.eventId);
6767
if (observable) {
6868
const eventObserver = context._getExecutionVariable<Nullable<Observer<any[]>>>(this, "_eventObserver", null);

packages/dev/core/src/FlowGraph/Blocks/Event/flowGraphSceneTickEventBlock.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ export class FlowGraphSceneTickEventBlock extends FlowGraphEventBlock {
6262
/**
6363
* @internal
6464
*/
65-
public _cancelPendingTasks(_context: FlowGraphContext) {
65+
public override _cancelPendingTasks(_context: FlowGraphContext) {
6666
// no-op
6767
}
6868

packages/dev/core/src/FlowGraph/Blocks/Execution/Animation/flowGraphPlayAnimationBlock.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ export class FlowGraphPlayAnimationBlock extends FlowGraphAsyncExecutionBlock {
8787
* @internal
8888
* @param context
8989
*/
90-
public _preparePendingTasks(context: FlowGraphContext): void {
90+
public override _preparePendingTasks(context: FlowGraphContext): void {
9191
const ag = this.animationGroup.getValue(context);
9292
const animation = this.animation.getValue(context);
9393
if (!ag && !animation) {
@@ -217,7 +217,7 @@ export class FlowGraphPlayAnimationBlock extends FlowGraphAsyncExecutionBlock {
217217
* @internal
218218
* Stop any currently running animations.
219219
*/
220-
public _cancelPendingTasks(context: FlowGraphContext): void {
220+
public override _cancelPendingTasks(context: FlowGraphContext): void {
221221
const ag = this.currentAnimationGroup.getValue(context);
222222
if (ag) {
223223
this._stopAnimationGroup(context, ag);

packages/dev/core/src/FlowGraph/Blocks/Execution/ControlFlow/flowGraphSetDelayBlock.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ export class FlowGraphSetDelayBlock extends FlowGraphAsyncExecutionBlock {
4040
this.lastDelayIndex = this.registerDataOutput("lastDelayIndex", RichTypeFlowGraphInteger, new FlowGraphInteger(-1));
4141
}
4242

43-
public _preparePendingTasks(context: FlowGraphContext): void {
43+
public override _preparePendingTasks(context: FlowGraphContext): void {
4444
const duration = this.duration.getValue(context);
4545
if (duration < 0 || isNaN(duration) || !isFinite(duration)) {
4646
return this._reportError(context, "Invalid duration in SetDelay block");
@@ -72,7 +72,7 @@ export class FlowGraphSetDelayBlock extends FlowGraphAsyncExecutionBlock {
7272
this._updateGlobalTimers(context);
7373
}
7474

75-
public _cancelPendingTasks(context: FlowGraphContext): void {
75+
public override _cancelPendingTasks(context: FlowGraphContext): void {
7676
const timers = context._getExecutionVariable(this, "pendingDelays", [] as AdvancedTimer[]);
7777
for (const timer of timers) {
7878
timer?.dispose();

packages/dev/core/src/FlowGraph/Blocks/Execution/flowGraphSetPropertyBlock.ts

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,19 +67,43 @@ export class FlowGraphSetPropertyBlock<P extends any, O extends FlowGraphAssetTy
6767
try {
6868
const target = this.object.getValue(context);
6969
const value = this.value.getValue(context);
70+
const propertyName = this.propertyName.getValue(context);
71+
72+
this._stopRunningAnimations(context, target, propertyName);
7073

7174
const setFunction = this.customSetFunction.getValue(context);
7275
if (setFunction) {
73-
setFunction(target, this.propertyName.getValue(context), value, context);
76+
setFunction(target, propertyName, value, context);
7477
} else {
75-
this._setPropertyValue(target, this.propertyName.getValue(context), value);
78+
this._setPropertyValue(target, propertyName, value);
7679
}
7780
} catch (e) {
7881
this._reportError(context, e);
7982
}
8083
this.out._activateSignal(context);
8184
}
8285

86+
private _stopRunningAnimations(context: FlowGraphContext, target: any, propertyName: string) {
87+
const currentlyRunningAnimationGroups = context._getGlobalContextVariable("currentlyRunningAnimationGroups", []) as number[];
88+
for (const uniqueId of currentlyRunningAnimationGroups) {
89+
const animationGroup = context.assetsContext.animationGroups.find((animationGroup) => animationGroup.uniqueId === uniqueId);
90+
if (animationGroup) {
91+
for (const targetedAnimations of animationGroup.targetedAnimations) {
92+
if (targetedAnimations.target === target && targetedAnimations.animation.targetProperty === propertyName) {
93+
animationGroup.stop(true);
94+
animationGroup.dispose();
95+
96+
const index = currentlyRunningAnimationGroups.indexOf(uniqueId);
97+
if (index !== -1) {
98+
currentlyRunningAnimationGroups.splice(index, 1);
99+
context._setGlobalContextVariable("currentlyRunningAnimationGroups", currentlyRunningAnimationGroups);
100+
}
101+
}
102+
}
103+
}
104+
}
105+
}
106+
83107
private _setPropertyValue(target: AssetType<O>, propertyName: string, value: P): void {
84108
const path = propertyName.split(".");
85109
let obj = target as any;

packages/dev/core/src/FlowGraph/Blocks/Execution/flowGraphSetVariableBlock.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -60,16 +60,16 @@ export class FlowGraphSetVariableBlock<T> extends FlowGraphExecutionBlockWithOut
6060
// check if there is an animation(group) running on this variable. If there is, stop the animation - a value was force-set.
6161
const currentlyRunningAnimationGroups = context._getGlobalContextVariable("currentlyRunningAnimationGroups", []) as number[];
6262
for (const animationUniqueId of currentlyRunningAnimationGroups) {
63-
const animation = context.assetsContext.animationGroups[animationUniqueId];
64-
// check if there is a target animation that has the target set to be the context
65-
for (const targetAnimation of animation.targetedAnimations) {
66-
if (targetAnimation.target === context) {
63+
const animationGroup = context.assetsContext.animationGroups.find((animationGroup) => animationGroup.uniqueId == animationUniqueId);
64+
if (animationGroup) {
65+
// check if there is a target animation that has the target set to be the context
66+
for (const targetAnimation of animationGroup.targetedAnimations) {
6767
// check if the target property is the variable we are setting
6868
if (targetAnimation.target === context) {
6969
// check the variable name
7070
if (targetAnimation.animation.targetProperty === variableName) {
7171
// stop the animation
72-
animation.stop();
72+
animationGroup.stop();
7373
// remove the animation from the currently running animations
7474
const index = currentlyRunningAnimationGroups.indexOf(animationUniqueId);
7575
if (index > -1) {

packages/dev/core/src/FlowGraph/flowGraphRichTypes.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -88,12 +88,14 @@ export const RichTypeColor4: RichType<Color4> = new RichType(FlowGraphTypes.Colo
8888

8989
export const RichTypeQuaternion: RichType<Quaternion> = new RichType(FlowGraphTypes.Quaternion, Quaternion.Identity(), Constants.ANIMATIONTYPE_QUATERNION);
9090
RichTypeQuaternion.typeTransformer = (value: any) => {
91-
if (value.getClassName && value.getClassName() === FlowGraphTypes.Vector4) {
92-
return Quaternion.FromArray(value.asArray());
93-
} else if (value.getClassName && value.getClassName() === FlowGraphTypes.Vector3) {
94-
return Quaternion.FromEulerVector(value);
95-
} else if (value.getClassName && value.getClassName() === FlowGraphTypes.Matrix) {
96-
return Quaternion.FromRotationMatrix(value);
91+
if (value.getClassName) {
92+
if (value.getClassName() === FlowGraphTypes.Vector4) {
93+
return Quaternion.FromArray(value.asArray());
94+
} else if (value.getClassName() === FlowGraphTypes.Vector3) {
95+
return Quaternion.FromEulerVector(value);
96+
} else if (value.getClassName() === FlowGraphTypes.Matrix) {
97+
return Quaternion.FromRotationMatrix(value);
98+
}
9799
}
98100
return value;
99101
};

packages/dev/loaders/src/glTF/2.0/Extensions/KHR_interactivity/declarationMapper.ts

Lines changed: 4 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -796,38 +796,6 @@ const gltfToFlowGraphMapping: { [key: string]: IGLTFToFlowGraphMapping } = {
796796
},
797797
},
798798
},
799-
"math/compose": {
800-
blocks: [FlowGraphBlockNames.MatrixCompose],
801-
configuration: {},
802-
inputs: {
803-
values: {
804-
translation: { name: "position", gltfType: "float3" },
805-
rotation: { name: "rotationQuaternion", gltfType: "float4" },
806-
scale: { name: "scaling", gltfType: "float3" },
807-
},
808-
},
809-
outputs: {
810-
values: {
811-
value: { name: "output" },
812-
},
813-
},
814-
},
815-
"math/decompose": {
816-
blocks: [FlowGraphBlockNames.MatrixDecompose],
817-
configuration: {},
818-
inputs: {
819-
values: {
820-
a: { name: "input" },
821-
},
822-
},
823-
outputs: {
824-
values: {
825-
translation: { name: "position" },
826-
rotation: { name: "rotationQuaternion" },
827-
scale: { name: "scaling" },
828-
},
829-
},
830-
},
831799
"math/not": {
832800
blocks: [FlowGraphBlockNames.BitwiseNot],
833801
inputs: {
@@ -1622,6 +1590,10 @@ const gltfToFlowGraphMapping: { [key: string]: IGLTFToFlowGraphMapping } = {
16221590
},
16231591
};
16241592

1593+
// aliases for backwards compatibility
1594+
gltfToFlowGraphMapping["math/compose"] = gltfToFlowGraphMapping["math/matCompose"];
1595+
gltfToFlowGraphMapping["math/decompose"] = gltfToFlowGraphMapping["math/matDecompose"];
1596+
16251597
function getSimpleInputMapping(type: FlowGraphBlockNames, inputs: string[] = ["a"], inferType?: boolean): IGLTFToFlowGraphMapping {
16261598
return {
16271599
blocks: [type],

0 commit comments

Comments
 (0)