Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
fcf2c8c
KHR_Interactivity importer: squash all work from importers/khr-intera…
SergioRZMasson Apr 27, 2026
af00b9d
KHR_interactivity: support opaque-reference (ref) spec update
SergioRZMasson Apr 27, 2026
ce12e03
Merge remote-tracking branch 'origin/master' into khr-interactivity-i…
SergioRZMasson Apr 28, 2026
d204462
KHR_interactivity: composite path converter + Babylon scene namespace
SergioRZMasson Apr 28, 2026
82ab3fc
KHR_interactivity: drop inspector-v2 CLI WIP leftovers from prior squash
SergioRZMasson Apr 28, 2026
947ec8b
KHR_interactivity: expand morph-target weights pointer support
SergioRZMasson May 1, 2026
591773b
KHR_interactivity: fix build/lint/format errors from morph-target update
SergioRZMasson May 1, 2026
91d04bc
KHR_interactivity: glTF-side fallback for KHR_materials_volume textur…
SergioRZMasson May 1, 2026
4cb80d5
KHR_interactivity: glTF-side fallback for transmission texture transf…
SergioRZMasson May 1, 2026
3bfe531
KHR_interactivity: bake static ref prefix into relative pointer templ…
SergioRZMasson May 1, 2026
aff0c97
KHR_interactivity: implement ref/eq operation
SergioRZMasson May 1, 2026
4073366
KHR_interactivity: preserve raw anisotropyRotation across pointer/set…
SergioRZMasson May 4, 2026
56d4470
Revert "KHR_interactivity: preserve raw anisotropyRotation across poi…
SergioRZMasson May 4, 2026
4767276
KHR_interactivity: implement math/quatSlerp operation
SergioRZMasson May 4, 2026
bcb8157
KHR_interactivity: expose selectedNode ref output on event/onSelect
SergioRZMasson May 4, 2026
254d986
KHR_interactivity: resolve Babylon-object refs in pointer templates
SergioRZMasson May 4, 2026
2082c69
KHR_interactivity: return ref strings from read-only glTF tree accessors
SergioRZMasson May 5, 2026
f75563f
FlowGraph: normalise trailing slash for ref-typed string equality
SergioRZMasson May 5, 2026
4e75d67
KHR_interactivity: expose hoveredNode ref output on event/onHoverIn a…
SergioRZMasson May 6, 2026
14e7cfb
sandbox: load CDN scripts with crossOrigin=anonymous
SergioRZMasson May 6, 2026
7644ae6
KHR_interactivity: handle ref-typed animation refs in pointer/get and…
SergioRZMasson May 6, 2026
94ae801
KHR_interactivity: resolve trailing-slash refs and animation pointers…
SergioRZMasson May 6, 2026
989093c
KHR_interactivity: support dynamic refs in relative pointer templates
SergioRZMasson May 6, 2026
e04454e
KHR_node_visibility: apply `visible: false` to primitive child meshes…
SergioRZMasson May 6, 2026
be979ed
FlowGraph: type/intToFloat tolerates plain-number inputs in addition …
SergioRZMasson May 7, 2026
0df1961
Revert sandbox/babylonServer/viewer changes (unrelated to KHR_interac…
SergioRZMasson May 13, 2026
755639e
Merge remote-tracking branch 'origin/master' into khr-interactivity-i…
SergioRZMasson May 13, 2026
03c6be1
Normalize objectModelMapping.ts line endings to LF
SergioRZMasson May 13, 2026
2eb2839
Remove ancillary docs/gitignore not needed for the PR
SergioRZMasson May 13, 2026
1467d25
Remove integration tests from PR scope
SergioRZMasson May 13, 2026
5d88220
Fix: ref-typed socket detection in pointer bake (Calculator/Puzzle)
SergioRZMasson May 13, 2026
2614c14
Fix sandbox: register glTF 2.0 extensions + LoadingScreen side-effect
SergioRZMasson May 13, 2026
8c317cb
Add JSDoc comments on public interactivity / object-model declarations
SergioRZMasson May 13, 2026
1dc976b
KHR_interactivity: gracefully handle unsupported operations (spec 3.2.4)
SergioRZMasson Jun 18, 2026
f1fdd55
Merge branch 'master' into khr-interactivity-importer-v2
SergioRZMasson Jun 23, 2026
48347de
KHR_interactivity: event references, event/stopPropagation, math/Tau …
SergioRZMasson Jun 23, 2026
a369965
test(dev-only): KHR_interactivity integration runners + investigation…
SergioRZMasson Jun 23, 2026
c8ffa8c
test(dev-only): route-load GLBs and skip on renderer crash
SergioRZMasson Jun 23, 2026
aa861ff
fix(FlowGraph): refresh math/random per flow activation; fix flow/for…
SergioRZMasson Jun 23, 2026
267872d
docs(dev-only): math/random investigation notes
SergioRZMasson Jun 23, 2026
30aca39
test(dev-only): variable-asserted ref/event coverage (refs, stopPropa…
SergioRZMasson Jun 24, 2026
a11c3a4
test(dev-only): drive frames manually in batch runner so setDelay-gat…
SergioRZMasson Jun 24, 2026
59577f6
feat(FlowGraph): add math/rgbToOkLCh and math/rgbFromOkLCh color conv…
SergioRZMasson Jun 24, 2026
e4a8a9e
docs(dev-only): rgb/OkLCh implementation notes
SergioRZMasson Jun 24, 2026
70a509a
fix(sandbox): import InstancedMesh side-effect so glTF scenes load in…
SergioRZMasson Jun 24, 2026
24cea1c
feat(KHR_interactivity): map event/onSelect so node clicks fire inter…
SergioRZMasson Jun 24, 2026
141d55f
docs(dev-only): event/onSelect investigation notes
SergioRZMasson Jun 24, 2026
d3cf89c
chore: remove dev-only KHR_interactivity integration runners and stud…
SergioRZMasson Jun 24, 2026
7fe798b
Merge origin/master into khr-interactivity-importer-v2
SergioRZMasson Jun 24, 2026
7e77407
refactor(KHR_interactivity): drop duplicate event/onSelect mapping
SergioRZMasson Jun 24, 2026
f6b66d3
Merge remote-tracking branch 'origin/master' into khr-interactivity-i…
SergioRZMasson Jun 29, 2026
d72e38e
feat(KHR_interactivity): support delays/{} and events/{} pointer/get …
SergioRZMasson Jun 29, 2026
5e6a2f0
Merge origin/master into khr-interactivity-importer-v2
SergioRZMasson Jun 29, 2026
4dfb6f8
fix(FlowGraph): evaluate KHR_interactivity Bezier easing parametrically
SergioRZMasson Jun 29, 2026
e0e178c
fix(FlowGraph): correct KHR_interactivity math ops (slerp, quatFromUp…
SergioRZMasson Jun 30, 2026
c0c853a
fix(FlowGraph): store math/combine2x2 and combine3x3 in column-major …
SergioRZMasson Jul 1, 2026
6b2c223
fix(FlowGraph): validate interpolation before cancelling a running one
SergioRZMasson Jul 1, 2026
0effb7b
fix(FlowGraph): compute math/smoothStep per spec (coefficient, not in…
SergioRZMasson Jul 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/** This file must only contain pure code and pure imports */

import { type IFlowGraphBlockConfiguration } from "../../../flowGraphBlock";
import { FlowGraphBlock, type IFlowGraphBlockConfiguration } from "../../../flowGraphBlock";
import { FlowGraphTypes, getRichTypeByFlowGraphType, RichTypeAny, RichTypeBoolean, RichTypeFlowGraphInteger, RichTypeNumber } from "../../../flowGraphRichTypes.pure";
import { FlowGraphBinaryOperationBlock } from "../flowGraphBinaryOperationBlock";
import { FlowGraphConstantOperationBlock } from "../flowGraphConstantOperationBlock";
Expand Down Expand Up @@ -335,6 +335,15 @@ export class FlowGraphPiBlock extends FlowGraphConstantOperationBlock<number> {
}
}

/**
* Tau constant (2 * Pi), the ratio of a circle's circumference to its radius.
*/
export class FlowGraphTauBlock extends FlowGraphConstantOperationBlock<number> {
constructor(config?: IFlowGraphBlockConfiguration) {
super(RichTypeNumber, () => 2 * Math.PI, FlowGraphBlockNames.Tau, config);
}
}

/**
* Positive inf constant.
*/
Expand Down Expand Up @@ -668,6 +677,21 @@ function Interpolate(a: number, b: number, c: number) {
return (1 - c) * a + c * b;
}

/**
* Smooth step (Hermite interpolation) per KHR_interactivity `math/smoothStep`.
* Given the edges `a`/`b` and the value `c`, computes the smooth interpolation
* coefficient `t * t * (3 - 2 * t)` where `t = saturate((c - min(a, b)) / |b - a|)`.
* Note that this returns the coefficient in [0, 1]; it does not interpolate between `a` and `b`.
* @param a first edge
* @param b second edge
* @param c value to interpolate
* @returns the smooth-step interpolation coefficient
*/
function SmoothStep(a: number, b: number, c: number) {
const t = Saturate((c - Math.min(a, b)) / Math.abs(b - a));
return t * t * (3 - 2 * t);
}

/**
* Interpolate block.
*/
Expand All @@ -686,6 +710,42 @@ export class FlowGraphMathInterpolationBlock extends FlowGraphTernaryOperationBl
}
}

/**
* Spherical linear interpolation between two unit quaternions, matching the
* KHR_interactivity `math/quatSlerp` operation. The two inputs are treated as
* Babylon `Quaternion` values regardless of their concrete class (the spec
* defines them as `float4`), and the output is a `Quaternion`.
*
* The underlying implementation is `Quaternion.Slerp`, whose algorithm matches
* the spec step-for-step (dot product, conditional sign-flip on `b`, sin-based
* weighting, and a near-identity short-circuit to a linear blend).
*/
export class FlowGraphMathSlerpBlock extends FlowGraphTernaryOperationBlock<Quaternion, Quaternion, number, Quaternion> {
constructor(config?: IFlowGraphBlockConfiguration) {
super(RichTypeAny, RichTypeAny, RichTypeNumber, RichTypeAny, (a, b, c) => Quaternion.Slerp(a, b, c), FlowGraphBlockNames.MathSlerp, config);
}
}

/**
* Smooth-step block, backing the KHR_interactivity `math/smoothStep` operation.
* Operates component-wise over floatN inputs (the two edges `a`/`b` and the
* value `c`).
*/
export class FlowGraphMathSmoothStepBlock extends FlowGraphTernaryOperationBlock<
FlowGraphMathOperationType,
FlowGraphMathOperationType,
FlowGraphMathOperationType,
FlowGraphMathOperationType
> {
constructor(config?: IFlowGraphBlockConfiguration) {
super(RichTypeAny, RichTypeAny, RichTypeAny, RichTypeAny, (a, b, c) => this._polymorphicSmoothStep(a, b, c), FlowGraphBlockNames.SmoothStep, config);
}

private _polymorphicSmoothStep(a: FlowGraphMathOperationType, b: FlowGraphMathOperationType, c: FlowGraphMathOperationType) {
return ComponentWiseTernaryOperation(a, b, c, SmoothStep);
}
}

/**
* Equals block.
*/
Expand All @@ -697,14 +757,38 @@ export class FlowGraphEqualityBlock extends FlowGraphBinaryOperationBlock<FlowGr
private _polymorphicEq(a: FlowGraphMathOperationType, b: FlowGraphMathOperationType) {
const aClassName = _GetClassNameOf(a);
const bClassName = _GetClassNameOf(b);
if (typeof a !== typeof b) {
return false;
// KHR_interactivity has no dedicated quaternion type: rotations are float4 values. Treat Quaternion and
// Vector4 (both four-component) as comparable so that, for example, a decomposed rotation can be compared
// against a float4 literal.
const isFourComponent = (className?: string) => className === FlowGraphTypes.Vector4 || className === FlowGraphTypes.Quaternion;
if (isFourComponent(aClassName) && isFourComponent(bClassName)) {
return (a as Vector4).equals(b as Vector4);
}
if (_AreSameVectorOrQuaternionClass(aClassName, bClassName) || _AreSameMatrixClass(aClassName, bClassName) || _AreSameIntegerClass(aClassName, bClassName)) {
return (a as Vector3).equals(b as Vector3);
} else {
return a === b;
}
// Handle mixed number/FlowGraphInteger comparison
if (isNumeric(a) && isNumeric(b)) {
return getNumericValue(a as FlowGraphNumber) === getNumericValue(b as FlowGraphNumber);
}
if (typeof a !== typeof b) {
return false;
}
// Both sides are JSON-Pointer-like ref strings ("/foo/0", "/foo/0/", ...).
// KHR_interactivity ref/eq is defined as "refers to the same object",
// so normalise trailing slashes before comparing — different asset
// authors emit refs with and without the trailing slash for the same
// object (e.g. /nodes/420 vs /nodes/420/).
if (typeof a === "string" && typeof b === "string") {
const aStr: string = a;
const bStr: string = b;
if (aStr.length > 0 && aStr[0] === "/" && bStr.length > 0 && bStr[0] === "/") {
const ar = aStr.endsWith("/") ? aStr.slice(0, -1) : aStr;
const br = bStr.endsWith("/") ? bStr.slice(0, -1) : bStr;
return ar === br;
}
}
return a === b;
}
}

Expand Down Expand Up @@ -1295,6 +1379,159 @@ export class FlowGraphOneBitsCounterBlock extends FlowGraphUnaryOperationBlock<F
}
}

/**
* Converts a linear sRGB color to OkLCh (the polar form of the Oklab color space).
* Uses the canonical matrices from Björn Ottosson's Oklab definition (also adopted by CSS Color 4).
* The RGB inputs are treated as linear; hue is returned in radians.
* @param r linear red component
* @param g linear green component
* @param b linear blue component
* @returns the OkLCh lightness (l), chroma (c) and hue (h, radians)
*/
function _RgbToOkLch(r: number, g: number, b: number): { l: number; c: number; h: number } {
// Linear sRGB -> LMS cone responses.
const long = 0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b;
const medium = 0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b;
const short = 0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b;
// Non-linearity (cube root) then LMS' -> Oklab.
const lRoot = Math.cbrt(long);
const mRoot = Math.cbrt(medium);
const sRoot = Math.cbrt(short);
const okL = 0.2104542553 * lRoot + 0.793617785 * mRoot - 0.0040720468 * sRoot;
const okA = 1.9779984951 * lRoot - 2.428592205 * mRoot + 0.4505937099 * sRoot;
const okB = 0.0259040371 * lRoot + 0.7827717662 * mRoot - 0.808675766 * sRoot;
// Oklab -> OkLCh (polar form).
return { l: okL, c: Math.hypot(okA, okB), h: Math.atan2(okB, okA) };
}

/**
* Converts an OkLCh color to linear sRGB. Inverse of {@link _RgbToOkLch}; hue is in radians.
* @param l OkLCh lightness
* @param c OkLCh chroma
* @param h OkLCh hue in radians
* @returns the linear sRGB red (r), green (g) and blue (b) components
*/
function _OkLchToRgb(l: number, c: number, h: number): { r: number; g: number; b: number } {
// OkLCh -> Oklab.
const okA = c * Math.cos(h);
const okB = c * Math.sin(h);
// Oklab -> LMS' then cube to LMS.
const lPrime = l + 0.3963377774 * okA + 0.2158037573 * okB;
const mPrime = l - 0.1055613458 * okA - 0.0638541728 * okB;
const sPrime = l - 0.0894841775 * okA - 1.291485548 * okB;
const long = lPrime * lPrime * lPrime;
const medium = mPrime * mPrime * mPrime;
const short = sPrime * sPrime * sPrime;
// LMS -> linear sRGB.
return {
r: 4.0767416621 * long - 3.3077115913 * medium + 0.2309699292 * short,
g: -1.2684380046 * long + 2.6097574011 * medium - 0.3413193965 * short,
b: -0.0041960863 * long - 0.7034186147 * medium + 1.707614701 * short,
};
}

/**
* Block that converts a linear sRGB color (r, g, b) to OkLCh (l, c, h). Hue is in radians.
*/
export class FlowGraphRGBToOkLChBlock extends FlowGraphBlock {
/**
* Input connection: the linear red component.
*/
public readonly r: FlowGraphDataConnection<number>;
/**
* Input connection: the linear green component.
*/
public readonly g: FlowGraphDataConnection<number>;
/**
* Input connection: the linear blue component.
*/
public readonly b: FlowGraphDataConnection<number>;
/**
* Output connection: the OkLCh lightness.
*/
public readonly l: FlowGraphDataConnection<number>;
/**
* Output connection: the OkLCh chroma.
*/
public readonly c: FlowGraphDataConnection<number>;
/**
* Output connection: the OkLCh hue, in radians.
*/
public readonly h: FlowGraphDataConnection<number>;

constructor(config?: IFlowGraphBlockConfiguration) {
super(config);
this.r = this.registerDataInput("r", RichTypeNumber, 0);
this.g = this.registerDataInput("g", RichTypeNumber, 0);
this.b = this.registerDataInput("b", RichTypeNumber, 0);
this.l = this.registerDataOutput("l", RichTypeNumber, 0);
this.c = this.registerDataOutput("c", RichTypeNumber, 0);
this.h = this.registerDataOutput("h", RichTypeNumber, 0);
}

public override _updateOutputs(context: FlowGraphContext): void {
const { l, c, h } = _RgbToOkLch(this.r.getValue(context), this.g.getValue(context), this.b.getValue(context));
this.l.setValue(l, context);
this.c.setValue(c, context);
this.h.setValue(h, context);
}

public override getClassName(): string {
return FlowGraphBlockNames.RGBToOkLCh;
}
}

/**
* Block that converts an OkLCh color (l, c, h) to linear sRGB (r, g, b). Hue is in radians.
*/
export class FlowGraphRGBFromOkLChBlock extends FlowGraphBlock {
/**
* Input connection: the OkLCh lightness.
*/
public readonly l: FlowGraphDataConnection<number>;
/**
* Input connection: the OkLCh chroma.
*/
public readonly c: FlowGraphDataConnection<number>;
/**
* Input connection: the OkLCh hue, in radians.
*/
public readonly h: FlowGraphDataConnection<number>;
/**
* Output connection: the linear red component.
*/
public readonly r: FlowGraphDataConnection<number>;
/**
* Output connection: the linear green component.
*/
public readonly g: FlowGraphDataConnection<number>;
/**
* Output connection: the linear blue component.
*/
public readonly b: FlowGraphDataConnection<number>;

constructor(config?: IFlowGraphBlockConfiguration) {
super(config);
this.l = this.registerDataInput("l", RichTypeNumber, 0);
this.c = this.registerDataInput("c", RichTypeNumber, 0);
this.h = this.registerDataInput("h", RichTypeNumber, 0);
this.r = this.registerDataOutput("r", RichTypeNumber, 0);
this.g = this.registerDataOutput("g", RichTypeNumber, 0);
this.b = this.registerDataOutput("b", RichTypeNumber, 0);
}

public override _updateOutputs(context: FlowGraphContext): void {
const { r, g, b } = _OkLchToRgb(this.l.getValue(context), this.c.getValue(context), this.h.getValue(context));
this.r.setValue(r, context);
this.g.setValue(g, context);
this.b.setValue(b, context);
}

public override getClassName(): string {
return FlowGraphBlockNames.RGBFromOkLCh;
}
}

let _Registered = false;
/**
* Register side effects for flowGraphMathBlocks.
Expand All @@ -1313,6 +1550,7 @@ export function RegisterFlowGraphMathBlocks(): void {
RegisterClass(FlowGraphBlockNames.Random, FlowGraphRandomBlock);
RegisterClass(FlowGraphBlockNames.E, FlowGraphEBlock);
RegisterClass(FlowGraphBlockNames.PI, FlowGraphPiBlock);
RegisterClass(FlowGraphBlockNames.Tau, FlowGraphTauBlock);
RegisterClass(FlowGraphBlockNames.Inf, FlowGraphInfBlock);
RegisterClass(FlowGraphBlockNames.NaN, FlowGraphNaNBlock);
RegisterClass(FlowGraphBlockNames.Abs, FlowGraphAbsBlock);
Expand All @@ -1329,6 +1567,10 @@ export function RegisterFlowGraphMathBlocks(): void {
RegisterClass(FlowGraphBlockNames.Clamp, FlowGraphClampBlock);
RegisterClass(FlowGraphBlockNames.Saturate, FlowGraphSaturateBlock);
RegisterClass(FlowGraphBlockNames.MathInterpolation, FlowGraphMathInterpolationBlock);
RegisterClass(FlowGraphBlockNames.MathSlerp, FlowGraphMathSlerpBlock);
RegisterClass(FlowGraphBlockNames.SmoothStep, FlowGraphMathSmoothStepBlock);
RegisterClass(FlowGraphBlockNames.RGBToOkLCh, FlowGraphRGBToOkLChBlock);
RegisterClass(FlowGraphBlockNames.RGBFromOkLCh, FlowGraphRGBFromOkLChBlock);
RegisterClass(FlowGraphBlockNames.Equality, FlowGraphEqualityBlock);
RegisterClass(FlowGraphBlockNames.LessThan, FlowGraphLessThanBlock);
RegisterClass(FlowGraphBlockNames.LessThanOrEqual, FlowGraphLessThanOrEqualBlock);
Expand Down
Loading