diff --git a/src/BasicBehaveEngine/BasicBehaveEngine.ts b/src/BasicBehaveEngine/BasicBehaveEngine.ts index e3ed1dd..cf66bf8 100644 --- a/src/BasicBehaveEngine/BasicBehaveEngine.ts +++ b/src/BasicBehaveEngine/BasicBehaveEngine.ts @@ -34,6 +34,7 @@ import {Divide} from "./nodes/math/arithmetic/Divide"; import {Remainder} from "./nodes/math/arithmetic/Remainder"; import {Min} from "./nodes/math/arithmetic/Min"; import {Max} from "./nodes/math/arithmetic/Max"; +import {Mix} from "./nodes/math/arithmetic/Mix"; import {Clamp} from "./nodes/math/arithmetic/Clamp"; import {DegreeToRadians} from "./nodes/math/trigonometry/DegreeToRadians"; import {RadiansToDegrees} from "./nodes/math/trigonometry/RadiansToDegrees"; @@ -344,6 +345,7 @@ export class BasicBehaveEngine implements IBehaveEngine { this.registerBehaveEngineNode("math/rem", Remainder); this.registerBehaveEngineNode("math/min", Min); this.registerBehaveEngineNode("math/max", Max); + this.registerBehaveEngineNode("math/mix", Mix); this.registerBehaveEngineNode("math/clamp", Clamp); this.registerBehaveEngineNode("math/rad", DegreeToRadians); this.registerBehaveEngineNode("math/deg", RadiansToDegrees); diff --git a/src/BasicBehaveEngine/nodes/math/arithmetic/Mix.ts b/src/BasicBehaveEngine/nodes/math/arithmetic/Mix.ts new file mode 100644 index 0000000..70172a0 --- /dev/null +++ b/src/BasicBehaveEngine/nodes/math/arithmetic/Mix.ts @@ -0,0 +1,60 @@ +import {BehaveEngineNode, IBehaviourNodeProps} from "../../../BehaveEngineNode"; + +export class Mix extends BehaveEngineNode { + REQUIRED_VALUES = [{ id: "a" }, { id: "b" }, { id: "c"}] + + constructor(props: IBehaviourNodeProps) { + super(props); + this.name = "MixNode"; + this.validateValues(this.values); + } + + private mix = (a: number, b: number, t: number): number => { + return a + t * (b - a); + } + + override processNode(flowSocket?: string) { + const {a, b, c} = this.evaluateAllValues(this.REQUIRED_VALUES.map(val => val.id)); + this.graphEngine.processNodeStarted(this); + const typeIndexA = this.values['a'].type! + const typeA: string = this.getType(typeIndexA); + const typeIndexB = this.values['b'].type! + const typeB: string = this.getType(typeIndexB); + + if (typeA !== typeB) { + throw Error("input types not equivalent") + } + let val: any; + + switch (typeA) { + case "float": + val = [this.mix(a,b,c)]; + break; + case "float2": + val = [ + this.mix(a[0], b[0], c), + this.mix(a[1], b[1], c) + ] + break; + case "float3": + val = [ + this.mix(a[0], b[0], c), + this.mix(a[1], b[1], c), + this.mix(a[2], b[2], c), + ] + break; + case "float4": + val = [ + this.mix(a[0], b[0], c), + this.mix(a[1], b[1], c), + this.mix(a[2], b[2], c), + this.mix(a[3], b[3], c), + ] + break + default: + throw Error("Invalid type") + } + + return {'value': {id: "value", value: val, type: typeIndexA}} + } +} diff --git a/src/authoring/AuthoringNodeSpecs.ts b/src/authoring/AuthoringNodeSpecs.ts index d267aaa..45b9384 100644 --- a/src/authoring/AuthoringNodeSpecs.ts +++ b/src/authoring/AuthoringNodeSpecs.ts @@ -2165,6 +2165,58 @@ export const arithmeticNodes = [ ] } }, + { + type: "math/mix", + description: "Linear interpolation operation", + configuration: [], + input: { + flows: [], + values: [ + { + id: "a", + description: "First Arg", + types: [ + "float", + "float2", + "float3", + "float4" + ] + }, + { + id: "b", + description: "Second Arg", + types: [ + "float", + "float2", + "float3", + "float4" + ] + }, + { + id: "c", + description: "Unclamped interpolation coefficient", + types: [ + "float" + ] + } + ] + }, + output: { + flows: [], + values: [ + { + id: "value", + description: "mix of a and b", + types: [ + "float", + "float2", + "float3", + "float4" + ] + } + ] + } + }, { type: "math/clamp", description: "Clamp operation", diff --git a/tst/nodes.test.ts b/tst/nodes.test.ts index 028acd6..530c9a9 100644 --- a/tst/nodes.test.ts +++ b/tst/nodes.test.ts @@ -33,6 +33,7 @@ import {Divide} from "../src/BasicBehaveEngine/nodes/math/arithmetic/Divide"; import {Remainder} from "../src/BasicBehaveEngine/nodes/math/arithmetic/Remainder"; import {Max} from "../src/BasicBehaveEngine/nodes/math/arithmetic/Max"; import {Min} from "../src/BasicBehaveEngine/nodes/math/arithmetic/Min"; +import {Mix} from "../src/BasicBehaveEngine/nodes/math/arithmetic/Mix"; import {DegreeToRadians} from "../src/BasicBehaveEngine/nodes/math/trigonometry/DegreeToRadians"; import {RadiansToDegrees} from "../src/BasicBehaveEngine/nodes/math/trigonometry/RadiansToDegrees"; import {Sine} from "../src/BasicBehaveEngine/nodes/math/trigonometry/Sine"; @@ -1167,6 +1168,34 @@ describe('nodes', () => { expect(val['val'].value[2]).toBe(-10); }); + it("math/mix", () => { + let mix: Mix = new Mix({ + ...defaultProps, + values: [ + { id: 'a', value: [1], type: 2 }, + { id: 'b', value: [2], type: 2 }, + { id: 'c', value: [0.5], type: 2 } + ] + }); + + let val = mix.processNode(); + expect(val['val'].value[0]).toBe(1.5); + + mix = new Mix({ + ...defaultProps, + values: [ + { id: 'a', value: [0, 1, 3], type: 4 }, + { id: 'b', value: [1, 2, 4], type: 4 }, + { id: 'c', value: [0.5], type: 2 } + ] + }); + + val = mix.processNode(); + expect(val['val'].value[0]).toBe(0.5); + expect(val['val'].value[1]).toBe(1.5); + expect(val['val'].value[2]).toBe(3.5); + }); + it("math/rad", () => { let rad: DegreeToRadians = new DegreeToRadians({ ...defaultProps,