Skip to content

Commit 31c7628

Browse files
committed
uniforms logic + draggable number callback + options in button ui + renamed math group to operators and there I will put math and logic nodes. + version in output js
1 parent ec40aba commit 31c7628

9 files changed

Lines changed: 175 additions & 14 deletions

File tree

src/Editor.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1041,7 +1041,7 @@ export class Editor {
10411041
* Save current layout as JSON embeded in a comment and export all the material nodes as javascript to be used as-is.
10421042
*/
10431043
save() {
1044-
const formatVersion = "0.0.1";
1044+
const formatVersion = import.meta.env.APP_VERSION;
10451045
const signature = `//
10461046
// ▗▄▄▄▖▗▖ ▗▖▗▄▄▖ ▗▄▄▄▖▗▄▄▄▖ ▗▖ ▗▄▄▖
10471047
// █ ▐▌ ▐▌▐▌ ▐▌▐▌ ▐▌ ▐▌▐▌

src/EditorNodes.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,10 @@ export const NodeTypes : NodeGroupType[] = [
2626
]
2727
},
2828
{
29-
group:"Math",
29+
group:"Operators",
3030
color:Theme.config.groupMath as string,
3131
nodes:[
32-
{ TypeClass:MathNode, name:"Operation", id:"math-operation"}
32+
{ TypeClass:MathNode, name:"Math", id:"math-operation"}
3333
]
3434
},
3535
{

src/components/Button.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,27 @@
11
import { Theme } from "../colors/Theme";
22
import { InteractiveLayoutElement } from "../layout/InteractiveLayoutElement";
33

4+
type ButtonOptions = {
5+
fullWidth?:boolean
6+
}
7+
48
export class Button extends InteractiveLayoutElement
59
{
610
private xpadding = 5;
11+
protected options:ButtonOptions;
712

8-
constructor( protected _label:string , protected onClick?:VoidFunction )
13+
constructor( protected _label:string , protected onClick?:VoidFunction, options?:Partial<ButtonOptions> )
914
{
1015
super();
16+
this.options = {
17+
fullWidth:false,
18+
...options
19+
};
1120
}
1221

1322
override width(ctx: CanvasRenderingContext2D): number {
1423

15-
return ctx.measureText(this.label).width + this.xpadding*2;
24+
return this.options.fullWidth? 0 : ctx.measureText(this.label).width + this.xpadding*2;
1625
}
1726

1827
override render(ctx: CanvasRenderingContext2D, maxWidth: number, maxHeight: number): void {

src/components/DraggableNumber.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { DraggableValue } from "./DraggableValue";
22

33
export class DraggableNumber extends DraggableValue {
4-
constructor( name:string ="" ) {
5-
super(name, false, Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER, 1 );
4+
constructor( name:string ="", onChange?:(newValue:number)=>void ) {
5+
super(name, false, Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER, 1, onChange );
66
}
77
}

src/export/Script.ts

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import * as TSL from 'three/tsl';
22
import * as THREE from 'three/webgpu';
33

4+
5+
export type UniformType = "boolean" | "number" | "Color" | "Vector2" | "Vector3" | "Vector4" | "Matrix3" | "Matrix4";
6+
47
/**
58
* Handles the creation of the TSL output string
69
*/
@@ -10,6 +13,12 @@ export class Script {
1013

1114
protected imports:Record<string, Set<string>>;
1215
protected definitions:[ name:string, value:string][] ;
16+
17+
/**
18+
* array of [ index the definition in the definitions array, the variable type of the uniform ]
19+
*/
20+
protected uniforms:[ name:string, type:UniformType, initialValue:string ][]=[];
21+
1322
protected imagePaths:[ path:string, previewImage?:string, textureSetup?:( refName:string)=>string ][] ;
1423
protected moduleName2Ref:Record<string, unknown>;
1524

@@ -39,6 +48,25 @@ export class Script {
3948
return varName;
4049
}
4150

51+
/**
52+
* Defines a uniform variable.
53+
*
54+
*
55+
* @see https://github.com/mrdoob/three.js/wiki/Three.js-Shading-Language#uniform
56+
* @param varName
57+
* @param initialValue must ve a valid uniform value.
58+
* @returns the name of the variable holding the uniform.
59+
*/
60+
defineUniform( varName:string, type:UniformType, initialValue:string ) {
61+
62+
if( !this.uniforms.find( u=>u[0]==varName) )
63+
{
64+
this.uniforms.push( [ varName, type, initialValue ] );
65+
}
66+
67+
return "$uniforms."+varName;
68+
}
69+
4270
/**
4371
* Module to import
4472
* @param dep
@@ -92,10 +120,12 @@ export class Script {
92120

93121
toString( lastExpression:string="", forExport = false ) {
94122

95-
let output = ``;
123+
let output = `\n`;
96124

97125
if( forExport )
98126
{
127+
output += "import * as THREE from 'three/webgpu';\n";
128+
99129
//
100130
// Imports...
101131
//
@@ -113,6 +143,13 @@ export class Script {
113143
`;
114144
}
115145

146+
//
147+
// uniforms
148+
//
149+
output += "\n" + (forExport?"export":"") + " const $uniforms = {\n" +
150+
this.uniforms.map( uniform => `${uniform[0]} : uniform( ${uniform[2]} )`).join(",")
151+
+"\n}\n";
152+
116153
//
117154
// image loaders...
118155
//
@@ -157,4 +194,28 @@ export class Script {
157194

158195
return eval( this.toString( returnThisRef, false ) );
159196
}
197+
198+
public static makeValidVariableName(input:string) {
199+
// Return empty string if input is not a string or is empty
200+
if (typeof input !== 'string' || input.trim() === '') {
201+
return 'foo';
202+
}
203+
204+
let str = input.trim();
205+
206+
// Remove or replace invalid characters
207+
str = str.replace(/[^a-zA-Z0-9_$]/g, '_');
208+
209+
// If starts with a number, prepend an underscore
210+
if (/^\d/.test(str)) {
211+
str = '_' + str;
212+
}
213+
214+
// If empty after cleaning or starts with invalid character, use default name
215+
if (!str || !/^[a-zA-Z_$]/.test(str)) {
216+
return 'validVar';
217+
}
218+
219+
return "$"+str;
220+
}
160221
}

src/nodes/WinNode.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ export class WinNode extends Node {
3737
if( nameCount[name] )
3838
{
3939
// increment counter and append the trailing count to make the name unique...
40-
name += (++nameCount[name]).toString().padEnd(4,"0");
40+
name += (++nameCount[name]).toString().padStart(3,"0");
4141
}
4242
else
4343
{

src/nodes/input/UniformBaseNode.ts

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import { Button } from "../../components/Button";
2+
import { Script, UniformType } from "../../export/Script";
3+
import { LayoutElement } from "../../layout/LayoutElement";
4+
import { Output } from "../../properties/Output";
5+
import { InputBaseNode } from "./InputBaseNode";
6+
7+
8+
9+
export class UniformBaseNode extends InputBaseNode {
10+
11+
private static names:string[]=[];
12+
13+
protected varName?:string;
14+
15+
constructor( protected jsType:UniformType, childs:LayoutElement[]) {
16+
17+
childs.unshift(new Output("Value", 1));
18+
19+
childs.push( new Button("set name", ()=>this.setVariableName(), { fullWidth:true }))
20+
21+
super("Uniform", childs);
22+
23+
UniformBaseNode.names.push( this.uniformVarName );
24+
this.setTitle( this.uniformVarName );
25+
}
26+
27+
private get uniformVarName() {
28+
return this.varName ?? this.nodeName
29+
}
30+
31+
private setVariableName( inputValue?:string ) {
32+
let newName = prompt("Set the name of the uniform (must ve a valid js variable name)", inputValue ?? this.uniformVarName );
33+
if( newName )
34+
{
35+
const currentIndex = UniformBaseNode.names.indexOf( this.uniformVarName );
36+
37+
const cleanName = Script.makeValidVariableName(newName);
38+
39+
//
40+
// check for collisions
41+
//
42+
if( UniformBaseNode.names.filter( (name, i)=>name==newName && i!=currentIndex).length>0 )
43+
{
44+
alert(`Name [${newName}] is taken, pick another...`);
45+
this.setVariableName( newName )
46+
return;
47+
}
48+
49+
UniformBaseNode.names.splice( currentIndex, 1, cleanName );
50+
this.setTitle( cleanName.replace("$","$ ") );
51+
this.varName = cleanName;
52+
}
53+
}
54+
55+
override writeScript(script: Script): string {
56+
script.importModule("uniform");
57+
58+
return script.defineUniform( this.uniformVarName, this.jsType, this.getUniformJsValue() );
59+
}
60+
61+
/**
62+
* A class that extends this one must override this value.
63+
*
64+
* Should return the javascript string representing the value of this uniform...
65+
* @see https://github.com/mrdoob/three.js/wiki/Three.js-Shading-Language#uniform
66+
*/
67+
protected getUniformJsValue():string {
68+
throw new Error("implement me...")
69+
}
70+
71+
override onRemoved(): void {
72+
super.onRemoved();
73+
UniformBaseNode.names.splice( UniformBaseNode.names.indexOf( this.uniformVarName ), 1);
74+
}
75+
76+
}

src/nodes/input/ValueNode.ts

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,24 @@ import { DraggableNumber } from "../../components/DraggableNumber";
22
import { DraggableValue } from "../../components/DraggableValue";
33
import { Output } from "../../properties/Output";
44
import { InputBaseNode } from "./InputBaseNode";
5+
import { UniformBaseNode } from "./UniformBaseNode";
6+
7+
/**
8+
* A number, either a float or an int...
9+
*/
10+
export class ValueNode extends UniformBaseNode {
11+
12+
protected inputValue: DraggableNumber;
513

6-
export class ValueNode extends InputBaseNode {
714
constructor() {
815

9-
super("Value", [
10-
new Output("Value", 1),
11-
new DraggableNumber()
12-
]);
16+
super("number", [ new DraggableNumber("", () => this.update())]);
17+
18+
this.inputValue = this.getChildOfType(DraggableNumber)!;
19+
}
20+
21+
22+
protected override getUniformJsValue(): string {
23+
return this.inputValue.stringValue;
1324
}
1425
}

src/nodes/operators/MathNode.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,10 @@ export class MathNode extends WinNode {
6767
this.onComboChange(0)
6868
}
6969

70+
override width(ctx: CanvasRenderingContext2D): number {
71+
return 100
72+
}
73+
7074
protected onComboChange( newIndex:number )
7175
{
7276
const op = this.operators[newIndex];

0 commit comments

Comments
 (0)