diff --git a/tsl-testing/TSLCore.test.ts b/tsl-testing/TSLCore.test.ts index 131af8336..c1dbac1fd 100644 --- a/tsl-testing/TSLCore.test.ts +++ b/tsl-testing/TSLCore.test.ts @@ -2,16 +2,19 @@ import { expect, test } from 'vitest'; import { bool, bvec2, + bvec3, color, float, int, ivec2, + ivec3, mat2, mat3, mat4, nodeObject, uint, uvec2, + uvec3, vec2, vec3, vec4, @@ -21,6 +24,9 @@ import * as THREE from 'three/webgpu'; const renderer = new THREE.WebGPURenderer(); const nodeBuilder: THREE.NodeBuilder = renderer.backend.createNodeBuilder(null!, renderer); +const scene = new THREE.Scene(); +const camera = new THREE.PerspectiveCamera(); + test('nodeObject(Node)', () => { const testVec3 = vec3(1, 0, 2); const result: typeof testVec3 = nodeObject(testVec3); @@ -110,6 +116,9 @@ function assertConstNode( expect(node.node.getNodeType(nodeBuilder)).toBe(nodeType); if (typeof expectedType === 'string') expect(node.node.value).toBeTypeOf(expectedType as 'string'); else expect(node.node.value).toBeInstanceOf(expectedType); + + scene.backgroundNode = node.debug(); + renderer.render(scene, camera); } function assertConvertNode(node: THREE.VarNode>, nodeType: string) { @@ -117,6 +126,9 @@ function assertConvertNode(node: THREE.VarNode(node: THREE.VarNode>, nodeType: string) { @@ -124,9 +136,14 @@ function assertJoinNode(node: THREE.VarNode { +test('color', async () => { + await renderer.init(); + let node: THREE.VarNode<'color', THREE.ConstNode<'color', THREE.Color>> = color(); assertConstNode(node, 'color', THREE.Color); @@ -139,11 +156,13 @@ test('color', () => { node = color(new THREE.Color()); assertConstNode(node, 'color', THREE.Color); - node = color(0, 100, 255); + node = color(0, 0.5, 1); assertConstNode(node, 'color', THREE.Color); }); -test('float', () => { +test('float', async () => { + await renderer.init(); + let node: THREE.VarNode<'float', THREE.ConstNode<'float', number>> = float(); assertConstNode(node, 'float', 'number'); @@ -163,7 +182,9 @@ test('float', () => { assertConvertNode(convertNode, 'float'); }); -test('int', () => { +test('int', async () => { + await renderer.init(); + let node: THREE.VarNode<'int', THREE.ConstNode<'int', number>> = int(); assertConstNode(node, 'int', 'number'); @@ -183,7 +204,9 @@ test('int', () => { assertConvertNode(convertNode, 'int'); }); -test('uint', () => { +test('uint', async () => { + await renderer.init(); + let node: THREE.VarNode<'uint', THREE.ConstNode<'uint', number>> = uint(); assertConstNode(node, 'uint', 'number'); @@ -203,7 +226,9 @@ test('uint', () => { assertConvertNode(convertNode, 'uint'); }); -test('bool', () => { +test('bool', async () => { + await renderer.init(); + let node: THREE.VarNode<'bool', THREE.ConstNode<'bool', boolean>> = bool(); assertConstNode(node, 'bool', 'boolean'); @@ -223,7 +248,9 @@ test('bool', () => { assertConvertNode(convertNode, 'bool'); }); -test('vec2', () => { +test('vec2', async () => { + await renderer.init(); + let node: THREE.VarNode<'vec2', THREE.ConstNode<'vec2', THREE.Vector2>> = vec2(); assertConstNode(node, 'vec2', THREE.Vector2); @@ -261,7 +288,9 @@ test('vec2', () => { assertJoinNode(joinNode, 'vec2'); }); -test('ivec2', () => { +test('ivec2', async () => { + await renderer.init(); + let node: THREE.VarNode<'ivec2', THREE.ConstNode<'ivec2', THREE.Vector2>> = ivec2(); assertConstNode(node, 'ivec2', THREE.Vector2); @@ -299,7 +328,9 @@ test('ivec2', () => { assertJoinNode(joinNode, 'ivec2'); }); -test('uvec2', () => { +test('uvec2', async () => { + await renderer.init(); + let node: THREE.VarNode<'uvec2', THREE.ConstNode<'uvec2', THREE.Vector2>> = uvec2(); assertConstNode(node, 'uvec2', THREE.Vector2); @@ -338,6 +369,8 @@ test('uvec2', () => { }); test('bvec2', async () => { + await renderer.init(); + let node: THREE.VarNode<'bvec2', THREE.ConstNode<'bvec2', THREE.Vector2>> = bvec2(); assertConstNode(node, 'bvec2', THREE.Vector2); @@ -375,7 +408,9 @@ test('bvec2', async () => { assertJoinNode(joinNode, 'bvec2'); }); -test('vec3', () => { +test('vec3', async () => { + await renderer.init(); + let node: THREE.VarNode<'vec3', THREE.ConstNode<'vec3', THREE.Vector3>> = vec3(); assertConstNode(node, 'vec3', THREE.Vector3); @@ -390,9 +425,182 @@ test('vec3', () => { node = vec3(new THREE.Vector3()); assertConstNode(node, 'vec3', THREE.Vector3); + + let convertNode: THREE.VarNode<'vec3', THREE.ConvertNode<'vec3'>> = vec3(float(5)); + assertConvertNode(convertNode, 'vec3'); + + convertNode = vec3(vec3(1, 2, 3)); + assertConvertNode(convertNode, 'vec3'); + + convertNode = vec3(ivec3(1, 2, 3)); + assertConvertNode(convertNode, 'vec3'); + + convertNode = vec3(uvec3(1, 2, 3)); + assertConvertNode(convertNode, 'vec3'); + + convertNode = vec3(bvec3(false, true, false)); + assertConvertNode(convertNode, 'vec3'); + + let joinNode: THREE.VarNode<'vec3', THREE.JoinNode<'vec3'>> = vec3(float(5), 3, 1); + assertJoinNode(joinNode, 'vec3'); + + joinNode = vec3(uint(5), 1, 3); + assertJoinNode(joinNode, 'vec3'); + + joinNode = vec3(bool(false), true, false); + assertJoinNode(joinNode, 'vec3'); + + joinNode = vec3(new THREE.Vector2(), float(5)); + assertJoinNode(joinNode, 'vec3'); + + joinNode = vec3(new THREE.Vector2(), 5); + assertJoinNode(joinNode, 'vec3'); + + joinNode = vec3(5, vec2(1, 2)); + assertJoinNode(joinNode, 'vec3'); }); -test('vec4', () => { +test('ivec3', async () => { + await renderer.init(); + + let node: THREE.VarNode<'ivec3', THREE.ConstNode<'ivec3', THREE.Vector3>> = ivec3(); + assertConstNode(node, 'ivec3', THREE.Vector3); + + node = ivec3(1); + assertConstNode(node, 'ivec3', THREE.Vector3); + + node = ivec3(1, 2); + assertConstNode(node, 'ivec3', THREE.Vector3); + + node = ivec3(1, 2, 3); + assertConstNode(node, 'ivec3', THREE.Vector3); + + node = ivec3(new THREE.Vector3()); + assertConstNode(node, 'ivec3', THREE.Vector3); + + let convertNode: THREE.VarNode<'ivec3', THREE.ConvertNode<'ivec3'>> = ivec3(int(5)); + assertConvertNode(convertNode, 'ivec3'); + + convertNode = ivec3(vec3(1, 2, 3)); + assertConvertNode(convertNode, 'ivec3'); + + convertNode = ivec3(ivec3(1, 2, 3)); + assertConvertNode(convertNode, 'ivec3'); + + convertNode = ivec3(uvec3(1, 2, 3)); + assertConvertNode(convertNode, 'ivec3'); + + convertNode = ivec3(bvec3(false, true, false)); + assertConvertNode(convertNode, 'ivec3'); + + let joinNode: THREE.VarNode<'ivec3', THREE.JoinNode<'ivec3'>> = ivec3(int(5), 3, 1); + assertJoinNode(joinNode, 'ivec3'); + + joinNode = ivec3(new THREE.Vector2(), int(5)); + assertJoinNode(joinNode, 'ivec3'); + + joinNode = ivec3(new THREE.Vector2(), 5); + assertJoinNode(joinNode, 'ivec3'); + + joinNode = ivec3(5, ivec2(1, 2)); + assertJoinNode(joinNode, 'ivec3'); +}); + +test('uvec3', async () => { + await renderer.init(); + + let node: THREE.VarNode<'uvec3', THREE.ConstNode<'uvec3', THREE.Vector3>> = uvec3(); + assertConstNode(node, 'uvec3', THREE.Vector3); + + node = uvec3(1); + assertConstNode(node, 'uvec3', THREE.Vector3); + + node = uvec3(1, 2); + assertConstNode(node, 'uvec3', THREE.Vector3); + + node = uvec3(1, 2, 3); + assertConstNode(node, 'uvec3', THREE.Vector3); + + node = uvec3(new THREE.Vector3()); + assertConstNode(node, 'uvec3', THREE.Vector3); + + let convertNode: THREE.VarNode<'uvec3', THREE.ConvertNode<'uvec3'>> = uvec3(uint(5)); + assertConvertNode(convertNode, 'uvec3'); + + convertNode = uvec3(vec3(1, 2, 3)); + assertConvertNode(convertNode, 'uvec3'); + + convertNode = uvec3(ivec3(1, 2, 3)); + assertConvertNode(convertNode, 'uvec3'); + + convertNode = uvec3(uvec3(1, 2, 3)); + assertConvertNode(convertNode, 'uvec3'); + + convertNode = uvec3(bvec3(false, true, false)); + assertConvertNode(convertNode, 'uvec3'); + + let joinNode: THREE.VarNode<'uvec3', THREE.JoinNode<'uvec3'>> = uvec3(uint(5), 3, 1); + assertJoinNode(joinNode, 'uvec3'); + + joinNode = uvec3(new THREE.Vector2(), uint(5)); + assertJoinNode(joinNode, 'uvec3'); + + joinNode = uvec3(new THREE.Vector2(), 5); + assertJoinNode(joinNode, 'uvec3'); + + joinNode = uvec3(5, uvec2(1, 2)); + assertJoinNode(joinNode, 'uvec3'); +}); + +test('bvec3', async () => { + await renderer.init(); + + let node: THREE.VarNode<'bvec3', THREE.ConstNode<'bvec3', THREE.Vector3>> = bvec3(); + assertConstNode(node, 'bvec3', THREE.Vector3); + + node = bvec3(false); + assertConstNode(node, 'bvec3', THREE.Vector3); + + node = bvec3(false, true); + assertConstNode(node, 'bvec3', THREE.Vector3); + + node = bvec3(false, true, false); + assertConstNode(node, 'bvec3', THREE.Vector3); + + node = bvec3(new THREE.Vector3()); + assertConstNode(node, 'bvec3', THREE.Vector3); + + let convertNode: THREE.VarNode<'bvec3', THREE.ConvertNode<'bvec3'>> = bvec3(bool(true)); + assertConvertNode(convertNode, 'bvec3'); + + convertNode = bvec3(vec3(1, 2, 3)); + assertConvertNode(convertNode, 'bvec3'); + + convertNode = bvec3(ivec3(1, 2, 3)); + assertConvertNode(convertNode, 'bvec3'); + + convertNode = bvec3(uvec3(1, 2, 3)); + assertConvertNode(convertNode, 'bvec3'); + + convertNode = bvec3(bvec3(false, true, false)); + assertConvertNode(convertNode, 'bvec3'); + + let joinNode: THREE.VarNode<'bvec3', THREE.JoinNode<'bvec3'>> = bvec3(bool(false), true, false); + assertJoinNode(joinNode, 'bvec3'); + + joinNode = bvec3(new THREE.Vector2(), bool(true)); + assertJoinNode(joinNode, 'bvec3'); + + joinNode = bvec3(new THREE.Vector2(), true); + assertJoinNode(joinNode, 'bvec3'); + + joinNode = bvec3(false, bvec2(true, false)); + assertJoinNode(joinNode, 'bvec3'); +}); + +test('vec4', async () => { + await renderer.init(); + let node: THREE.VarNode<'vec4', THREE.ConstNode<'vec4', THREE.Vector4>> = vec4(); assertConstNode(node, 'vec4', THREE.Vector4); @@ -412,12 +620,16 @@ test('vec4', () => { assertConstNode(node, 'vec4', THREE.Vector4); }); -test('mat2', () => { +test('mat2', async () => { + await renderer.init(); + let node: THREE.VarNode<'mat2', THREE.ConstNode<'mat2', THREE.Matrix2>> = mat2(new THREE.Matrix2()); assertConstNode(node, 'mat2', THREE.Matrix2); }); -test('mat3', () => { +test('mat3', async () => { + await renderer.init(); + let node: THREE.VarNode<'mat3', THREE.ConstNode<'mat3', THREE.Matrix3>> = mat3(new THREE.Matrix3()); assertConstNode(node, 'mat3', THREE.Matrix3); @@ -425,7 +637,9 @@ test('mat3', () => { assertConstNode(node, 'mat3', THREE.Matrix3); }); -test('mat4', () => { +test('mat4', async () => { + await renderer.init(); + let node: THREE.VarNode<'mat4', THREE.ConstNode<'mat4', THREE.Matrix4>> = mat4(new THREE.Matrix4()); assertConstNode(node, 'mat4', THREE.Matrix4); diff --git a/tsl-testing/node-type.test.ts b/tsl-testing/node-type.test.ts index adbf65564..52fac8da3 100644 --- a/tsl-testing/node-type.test.ts +++ b/tsl-testing/node-type.test.ts @@ -1,5 +1,5 @@ import { expect, test } from 'vitest'; -import { Fn, normalWorldGeometry, time, vec2, vec3, vec4 } from 'three/tsl'; +import { Fn, vec2, vec3, vec4 } from 'three/tsl'; import * as THREE from 'three/webgpu'; const renderer = new THREE.WebGPURenderer(); diff --git a/types/three/src/nodes/tsl/TSLCore.d.ts b/types/three/src/nodes/tsl/TSLCore.d.ts index a60a3fb5e..3b7c77562 100644 --- a/types/three/src/nodes/tsl/TSLCore.d.ts +++ b/types/three/src/nodes/tsl/TSLCore.d.ts @@ -1804,6 +1804,16 @@ declare module "../core/Node.js" { } } +/** + * Can be implicitly converted to a float + */ +type ScalarNode = Node<"float"> | Node<"int"> | Node<"uint"> | Node<"bool">; + +/** + * Can be implicitly converted to a float + */ +type Scalar = ScalarNode | number | boolean; + interface ColorFunction { // The first branch in `ConvertType` will forward the parameters to the `Color` constructor if there are no // parameters or all the parameters are non-objects @@ -1833,7 +1843,7 @@ interface FloatFunction { (value?: number): VarNode<"float", ConstNode<"float", number>>; // ConvertNode - (node: Node<"float"> | Node<"int"> | Node<"uint"> | Node<"bool">): VarNode<"float", ConvertNode<"float">>; + (node: ScalarNode): VarNode<"float", ConvertNode<"float">>; } export const float: FloatFunction; @@ -1843,7 +1853,7 @@ interface IntFunction { (value?: number): VarNode<"int", ConstNode<"int", number>>; // ConvertNode - (node: Node<"float"> | Node<"int"> | Node<"uint"> | Node<"bool">): VarNode<"int", ConvertNode<"int">>; + (node: ScalarNode): VarNode<"int", ConvertNode<"int">>; } export const int: IntFunction; @@ -1853,7 +1863,7 @@ interface UintFunction { (value?: number): VarNode<"uint", ConstNode<"uint", number>>; // ConvertNode - (node: Node<"float"> | Node<"int"> | Node<"uint"> | Node<"bool">): VarNode<"uint", ConvertNode<"uint">>; + (node: ScalarNode): VarNode<"uint", ConvertNode<"uint">>; } export const uint: UintFunction; @@ -1863,7 +1873,7 @@ interface BoolFunction { (value?: boolean): VarNode<"bool", ConstNode<"bool", boolean>>; // ConvertNode - (node: Node<"float"> | Node<"int"> | Node<"uint"> | Node<"bool">): VarNode<"bool", ConvertNode<"bool">>; + (node: ScalarNode): VarNode<"bool", ConvertNode<"bool">>; } export const bool: BoolFunction; @@ -1879,12 +1889,12 @@ interface Vec2Function { // ConstNode (value: Vector2): VarNode<"vec2", ConstNode<"vec2", Vector2>>; // ConvertNode - (node: Node<"float">): VarNode<"vec2", ConvertNode<"vec2">>; + (node: ScalarNode): VarNode<"vec2", ConvertNode<"vec2">>; (node: Node<"vec2"> | Node<"ivec2"> | Node<"uvec2"> | Node<"bvec2">): VarNode<"vec2", ConvertNode<"vec2">>; // The fall-through branch will be triggered if there is more than one parameter, and one of the parameters is an // object - (x: Node<"float"> | number, y: Node<"float"> | number): VarNode<"vec2", JoinNode<"vec2">>; + (x: Scalar, y: Scalar): VarNode<"vec2", JoinNode<"vec2">>; } export const vec2: Vec2Function; @@ -1960,18 +1970,105 @@ interface Vec3Function { // The second branch does not apply because `cacheMap` is `null` // The third branch will be triggered if there is a single parameter + // ConstNode (value: Vector3): VarNode<"vec3", ConstNode<"vec3", Vector3>>; - (node: Node): Node<"vec3">; + // ConvertNode + (node: ScalarNode): VarNode<"vec3", ConvertNode<"vec3">>; + (node: Node<"vec3"> | Node<"ivec3"> | Node<"uvec3"> | Node<"bvec3">): VarNode<"vec3", ConvertNode<"vec3">>; // The fall-through branch will be triggered if there is more than one parameter, and one of the parameters is an // object - (x: Node | number, y: Node | number, z?: Node | number): Node<"vec3">; + ( + x: Scalar, + y: Scalar, + z: Scalar, + ): VarNode<"vec3", JoinNode<"vec3">>; + (xy: Node<"vec2"> | Vector2, z: Scalar): VarNode<"vec3", JoinNode<"vec3">>; + (x: Scalar, yz: Node<"vec2"> | Vector2): VarNode<"vec3", JoinNode<"vec3">>; } export const vec3: Vec3Function; -export const ivec3: (node: Node) => Node<"ivec3">; -export const uvec3: (node: Node) => Node<"uvec3">; -export const bvec3: (node: Node) => Node<"bvec3">; + +interface Ivec3Function { + // The first branch in `ConvertType` will forward the parameters to the `Vector3` constructor if there are no + // parameters or all the parameters are non-objects + (x?: number, y?: number, z?: number): VarNode<"ivec3", ConstNode<"ivec3", Vector3>>; + + // The second branch does not apply because `cacheMap` is `null` + + // The third branch will be triggered if there is a single parameter + // ConstNode + (value: Vector3): VarNode<"ivec3", ConstNode<"ivec3", Vector3>>; + // ConvertNode + (node: Node<"int">): VarNode<"ivec3", ConvertNode<"ivec3">>; + (node: Node<"vec3"> | Node<"ivec3"> | Node<"uvec3"> | Node<"bvec3">): VarNode<"ivec3", ConvertNode<"ivec3">>; + + // The fall-through branch will be triggered if there is more than one parameter, and one of the parameters is an + // object + ( + x: Node<"int"> | number, + y: Node<"int"> | number, + z: Node<"int"> | number, + ): VarNode<"ivec3", JoinNode<"ivec3">>; + (xy: Node<"ivec2"> | Vector2, z: Node<"int"> | number): VarNode<"ivec3", JoinNode<"ivec3">>; + (x: Node<"int"> | number, yz: Node<"ivec2"> | Vector2): VarNode<"ivec3", JoinNode<"ivec3">>; +} + +export const ivec3: Ivec3Function; + +interface Uvec3Function { + // The first branch in `ConvertType` will forward the parameters to the `Vector3` constructor if there are no + // parameters or all the parameters are non-objects + (x?: number, y?: number, z?: number): VarNode<"uvec3", ConstNode<"uvec3", Vector3>>; + + // The second branch does not apply because `cacheMap` is `null` + + // The third branch will be triggered if there is a single parameter + // ConstNode + (value: Vector3): VarNode<"uvec3", ConstNode<"uvec3", Vector3>>; + // ConvertNode + (node: Node<"uint">): VarNode<"uvec3", ConvertNode<"uvec3">>; + (node: Node<"vec3"> | Node<"ivec3"> | Node<"uvec3"> | Node<"bvec3">): VarNode<"uvec3", ConvertNode<"uvec3">>; + + // The fall-through branch will be triggered if there is more than one parameter, and one of the parameters is an + // object + ( + x: Node<"uint"> | number, + y: Node<"uint"> | number, + z: Node<"uint"> | number, + ): VarNode<"uvec3", JoinNode<"uvec3">>; + (xy: Node<"uvec2"> | Vector2, z: Node<"uint"> | number): VarNode<"uvec3", JoinNode<"uvec3">>; + (x: Node<"uint"> | number, yz: Node<"uvec2"> | Vector2): VarNode<"uvec3", JoinNode<"uvec3">>; +} + +export const uvec3: Uvec3Function; + +interface Bvec3Function { + // The first branch in `ConvertType` will forward the parameters to the `Vector3` constructor if there are no + // parameters or all the parameters are non-objects + (x?: boolean, y?: boolean, z?: boolean): VarNode<"bvec3", ConstNode<"bvec3", Vector3>>; + + // The second branch does not apply because `cacheMap` is `null` + + // The third branch will be triggered if there is a single parameter + // ConstNode + (value: Vector3): VarNode<"bvec3", ConstNode<"bvec3", Vector3>>; + // ConvertNode + (node: Node<"bool">): VarNode<"bvec3", ConvertNode<"bvec3">>; + (node: Node<"vec3"> | Node<"ivec3"> | Node<"uvec3"> | Node<"bvec3">): VarNode<"bvec3", ConvertNode<"bvec3">>; + + // The fall-through branch will be triggered if there is more than one parameter, and one of the parameters is an + // object + ( + x: Node<"bool"> | boolean, + y: Node<"bool"> | boolean, + z: Node<"bool"> | boolean, + ): VarNode<"bvec3", JoinNode<"bvec3">>; + (xy: Node<"bvec2"> | Vector2, z: Node<"bool"> | boolean): VarNode<"bvec3", JoinNode<"bvec3">>; + (x: Node<"bool"> | boolean, yz: Node<"bvec2"> | Vector2): VarNode<"bvec3", JoinNode<"bvec3">>; +} + +export const bvec3: Bvec3Function; interface Vec4Function { // The first branch in `ConvertType` will forward the parameters to the `Vector4` constructor if there are no