@hology/core 0.0.222 → 0.0.224
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +14 -14
- package/README.md +2 -2
- package/dist/effects/sequence/sequence-player.js +1 -1
- package/dist/gameplay/actors/camera/third-person-camera-component.d.ts +1 -0
- package/dist/gameplay/actors/camera/third-person-camera-component.js +1 -1
- package/dist/gameplay/input/input-service.js +1 -1
- package/dist/gameplay/services/world.d.ts +6 -0
- package/dist/gameplay/services/world.js +1 -1
- package/dist/rendering.d.ts +19 -5
- package/dist/rendering.js +1 -1
- package/dist/scene/materializer.d.ts +3 -0
- package/dist/scene/materializer.js +1 -1
- package/dist/shader/graph/compiler.d.ts +1 -0
- package/dist/shader/graph/compiler.js +1 -1
- package/dist/test/first-person-camera-component.test.js +1 -1
- package/dist/test/input.test.js +1 -1
- package/dist/test/shader-graph.test.js +1 -1
- package/package.json +1 -1
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{BackSide as t,Color as e,DataTexture as a,DoubleSide as r,FrontSide as u,Matrix4 as i,RGBAFormat as o,Texture as n,Vector2 as s,Vector3 as l,Vector4 as p}from"three";import{BooleanNode as c,FloatNode as h,Mat4Node as v,NodeShaderMaterial as y,RgbNode as d,RgbaNode as m,Sampler2DNode as f,Texture2dLookupNode as g,UniformFloatNode as b,Vec2Node as x,Vec3Node as w,Vec4Node as S,abs as T,acos as I,asin as O,attributes as M,atan as N,atan2 as C,autoVarying as E,bulge as G,bool as U,ceil as B,clamp as $,colorToNormal as k,cos as z,cross as V,degrees as P,distance as A,dot as j,exp as D,exp2 as L,float as R,floor as q,fract as F,inversesqrt as _,batchingMatrix as W,colorGradientSampler as H,curveSampler as Y,ifDefApply as J,sampleNamedEasingCurve as K,length as Q,LayerMixMode as X,log as Z,log2 as tt,mat4 as et,max as at,min as rt,mix as ut,mixColorsByLayer as it,particleUniforms as ot,mod as nt,normalize as st,pow as lt,radians as pt,rgba as ct,rgb as ht,saturate as vt,select as yt,sign as dt,sin as mt,smoothstep as ft,sqrt as gt,standardMaterial as bt,scaleTransform as xt,step as wt,tan as St,textureSampler2d as Tt,timeUniforms as It,toonMaterial as Ot,translate as Mt,transformed as Nt,triplanarMapping as Ct,twirl as Et,uniformFloat as Gt,uniforms as Ut,varyingAttributes as Bt,vec2 as $t,vec3 as kt,vec4 as zt,getPaintedMaterialLayerWeight as Vt}from"../../shader-nodes/index.js";import{SpriteNodeShaderMaterial as Pt,getSpritePosition as At}from"../sprite-shader.js";import{oneMinus as jt}from"../../shader-nodes/index.js";import{vectorToRadial as Dt,remap as Lt,hueShift as Rt,blendColor as qt,desaturateColor as Ft}from"../../shader-nodes/math.js";import{rectangleFloat as _t,roundedRectangleFloat as Wt}from"../../shader-nodes/shapes.js";import{edgeDepthEffect as Ht,fresnelEffect as Yt,depthFadeEffect as Jt}from"../../shader-nodes/effects.js";import{fragmentLinearEyeDepth as Kt,sampleSceneDepth as Qt,sampleSceneLinearEyeDepth as Xt,screenUV as Zt,nearUniformName as te,farUniformName as ee}from"../../shader-nodes/depth.js";import{sampleSceneColor as ae}from"../../shader-nodes/scene-sample.js";import{decalDiscard as re,decalUV as ue}from"../../shader-nodes/decal.js";import{Trail as ie,trailUV as oe}from"../../effects/vfx/trail-renderer.js";import{SimplexNoiseNode as ne,Voronoi2d as se,rotateAxis as le}from"../../shader-nodes/index.js";import{flipbookUv as pe}from"../../shader-nodes/texture-sequence.js";import{parallaxOcclusionMapping as ce}from"../../shader-nodes/pom.js";import{defaultInlineLiteralValueForPort as he,inlineLiteralTypeForPort as ve,SHADER_GRAPH_NODE_DEFINITIONS as ye}from"./registry.js";import{shaderGraphParameterTypeToValueType as de}from"./parameters.js";import{varying as me,reflect as fe,refract as ge,dFdx as be,dFdy as xe,fwidth as we}from"three-shader-graph";import{decalNormal as Se}from"../../shader-nodes/decal.js";import{Curve2 as Te}from"../../utils/curve.js";const Ie=(()=>{const t=new a(new Uint8Array([255,255,255,255]),1,1,o);return t.name="Missing shader graph texture parameter fallback",t.needsUpdate=!0,t})(),Oe=new Set;export class ShaderGraphCompileError extends Error{constructor(t,e,a){super(t),this.nodeId=e,this.port=a,this.name="ShaderGraphCompileError"}}export const shaderGraphPostProcessWeightUniformName="hology_post_process_weight";export class ShaderGraphCompiler{constructor(t,e={}){this.graph=t,this.options=e,this.compiled=new Map,this.compiling=new Set,this.nodesById=new Map((t.nodes??[]).map(t=>[t.id,t]))}validate(){this.compile()}compile(){if(1!==this.graph.version)throw new ShaderGraphCompileError(`Unsupported shader graph version ${this.graph.version}`);return this.validateEdges(),this.validateNamedReroutes(),{output:this.compileOutput(),nodes:this.compiled}}validateEdges(){for(const t of this.graph.edges??[]){if(!this.nodesById.has(t.from.nodeId))throw new ShaderGraphCompileError(`Edge references missing source node "${t.from.nodeId}"`,t.from.nodeId,t.from.port);if(!this.nodesById.has(t.to.nodeId))throw new ShaderGraphCompileError(`Edge references missing target node "${t.to.nodeId}"`,t.to.nodeId,t.to.port)}}validateNamedReroutes(){for(const t of this.graph.nodes??[])"utility.namedRerouteUsage"===t.kind&&this.namedRerouteDeclaration(t)}compileOutput(){if("postProcess"===this.graph.target)return this.compilePostProcessOutput();const t=this.graph.outputs?.color,e={color:null!=t?this.toColorOutput(this.resolveOutputBinding(t,"color")):ct("#ffffff",1),transparent:Ce(this.graph)},a=this.graph.outputs?.opacity;null!=a&&(e.opacity=this.expectType(this.resolveOutputBinding(a,"opacity"),["float"],"opacity").value);const r=this.graph.outputs?.roughness;null!=r&&(e.roughness=this.expectType(this.resolveOutputBinding(r,"roughness"),["float"],"roughness").value);const u=this.graph.outputs?.metalness;null!=u&&(e.metalness=this.floatOutput(u,"metalness"));const i=this.graph.outputs?.normal;null!=i&&(e.normal=this.toVec3Output(this.resolveOutputBinding(i,"normal"),"normal"));const o=this.graph.outputs?.emissive;null!=o&&(e.emissive=this.toVec3Output(this.resolveOutputBinding(o,"emissive"),"emissive"));const n=this.graph.outputs?.ambientOcclusion;null!=n&&(e.ambientOcclusion=this.floatOutput(n,"ambientOcclusion"));const s=this.graph.outputs?.ambientOcclusionIntensity;null!=s&&(e.ambientOcclusionIntensity=this.floatOutput(s,"ambientOcclusionIntensity"));const l=this.graph.outputs?.sheenColor;null!=l&&(e.sheenColor=this.toVec3Output(this.resolveOutputBinding(l,"sheenColor"),"sheenColor"));const p=this.graph.outputs?.sheenRoughness;null!=p&&(e.sheenRoughness=this.floatOutput(p,"sheenRoughness"));const c=this.graph.outputs?.anisotropy;null!=c&&(e.anisotropy=this.floatOutput(c,"anisotropy"));const h=this.graph.outputs?.anisotropyDirection;null!=h&&(e.anisotropyDirection=this.expectType(this.resolveOutputBinding(h,"anisotropyDirection"),["vec2"],"anisotropyDirection").value);const v=this.graph.outputs?.bakedLight;null!=v&&(e.bakedLight=this.toVec3Output(this.resolveOutputBinding(v,"bakedLight"),"bakedLight"));const y=this.graph.outputs?.transform;null!=y&&"decal"!==this.graph.target&&(e.transform=this.toMat4Output(this.resolveOutputBinding(y,"transform"),"transform"));const d=this.graph.outputs?.discard;null!=d&&(e.discard=this.expectType(this.resolveOutputBinding(d,"discard"),["boolean"],"discard").value);const m=this.graph.outputs?.transparent;null!=m&&null!=m.value&&(e.transparent=Boolean(m.value));const f=this.graph.outputs?.alphaTest;return null!=f&&null!=f.value?e.alphaTest=Number(f.value):null!=this.graph.materialOptions?.alphaTest&&(e.alphaTest=this.graph.materialOptions.alphaTest),this.applyShadingModel(e)}compilePostProcessOutput(){const t=this.graph.outputs?.color,e=null!=t?this.toColorOutput(this.resolveOutputBinding(t,"color")):ae(Zt),a=this.graph.outputs?.opacity;return Me(e,null!=a?this.expectType(this.resolveOutputBinding(a,"opacity"),["float"],"opacity").value:void 0)}applyShadingModel(t){const e=null!=t.opacity?function(t,e){if(t instanceof h)return ct(kt(t,t,t),e);if(t instanceof x)return ct(kt(t.x,t.y,0),e);if(t instanceof S)return ct(t.rgb,t.w.multiply(e));return ct(t,e)}(t.color,t.opacity):t.color;switch(this.graph.shadingModel??"standard"){case"standard":{const a={color:Be(e),roughness:t.roughness??R(.5),...null!=t.metalness?{metalness:t.metalness}:{},..."decal"===this.graph.target?{normal:st(me(Ut.normalMatrix).multiplyVec(Se))}:{},...null!=t.normal?{normal:t.normal}:{},...null!=t.emissive?{emissive:$e(t.emissive)}:{},...null!=t.ambientOcclusion?{ambientOcclusion:t.ambientOcclusion}:{},...null!=t.ambientOcclusionIntensity?{ambientOcclusionIntensity:t.ambientOcclusionIntensity}:{},...null!=t.sheenColor?{sheenColor:t.sheenColor.rgb}:{},...null!=t.sheenRoughness?{sheenRoughness:t.sheenRoughness}:{},...null!=t.anisotropy?{anisotropy:t.anisotropy}:{},...null!=t.anisotropyDirection?{anisotropyDirection:t.anisotropyDirection}:{},...null!=t.bakedLight?{bakedLight:t.bakedLight}:{}};return{...t,color:bt(a)}}case"toon":{const a={color:Be(e),...null!=t.normal?{normal:t.normal}:{},...null!=t.emissive?{emissive:$e(t.emissive)}:{},...null!=t.ambientOcclusion?{ambientOcclusion:t.ambientOcclusion}:{},...null!=t.ambientOcclusionIntensity?{ambientOcclusionIntensity:t.ambientOcclusionIntensity}:{},...null!=t.bakedLight?{bakedLight:t.bakedLight}:{}};return{...t,color:Ot(a)}}case"unlit":return{...t,color:e}}}compileNode(t){const e=this.compiled.get(t);if(null!=e)return e;const a=this.nodesById.get(t);if(null==a)throw new ShaderGraphCompileError(`Missing shader graph node "${t}"`,t);if(null==ye[a.kind])throw new ShaderGraphCompileError(`Unsupported shader graph node "${a.kind}"`,a.id);if(this.compiling.has(t))throw new ShaderGraphCompileError(`Shader graph contains a cycle involving node "${t}"`,t);this.compiling.add(t);try{const e=this.compileNodeInternal(a);return this.compiled.set(t,e),e}finally{this.compiling.delete(t)}}compileNodeInternal(t){switch(t.kind){case"input.parameter":return this.output(t,this.compileParameterNode(t));case"input.slider":return this.output(t,{value:{type:"float",value:R(qe(ze(t,"value",.5)))}});case"constant.boolean":return this.output(t,{value:{type:"boolean",value:U(Boolean(ke(t,"value",!1)))}});case"constant.float":return this.output(t,{value:{type:"float",value:R(ze(t,"value",0))}});case"constant.vec2":return this.output(t,{value:{type:"vec2",value:We(ke(t,"value",[0,0]))}});case"constant.vec3":return this.output(t,{value:{type:"vec3",value:He(ke(t,"value",[0,0,0]))}});case"constant.rgba":return this.output(t,{value:{type:"rgba",value:Ye(ke(t,"value","#ffffff"),ze(t,"alpha",1))}});case"builtin.uv":{const e=this.defaultUV();return this.output(t,{value:{type:"vec2",value:!0===ke(t,"flipY",!1)?$t(e.x,jt(e.y)):e},u:{type:"float",value:e.x},v:{type:"float",value:e.y}})}case"builtin.screenUv":return this.output(t,{value:{type:"vec2",value:Zt}});case"builtin.color":{const e=E(M.color);return this.output(t,{value:{type:"rgba",value:e},rgb:{type:"rgb",value:e.rgb},r:{type:"float",value:e.r},g:{type:"float",value:e.g},b:{type:"float",value:e.b},a:{type:"float",value:e.a}})}case"builtin.position":{const e=E(M.position);return this.output(t,{value:{type:"vec3",value:e},x:{type:"float",value:e.x},y:{type:"float",value:e.y},z:{type:"float",value:e.z}})}case"builtin.worldPosition":{const e=E(Nt.worldPosition);return this.output(t,{value:{type:"vec3",value:e},x:{type:"float",value:e.x},y:{type:"float",value:e.y},z:{type:"float",value:e.z}})}case"builtin.objectPosition":{let e=Ut.instanceMatrix.multiplyVec(zt(0,0,0,1));e=J("USE_BATCHING",e,t=>W.multiplyVec(t));const a=E(Ut.modelMatrix.multiplyVec(e).xyz);return this.output(t,{value:{type:"vec3",value:a},x:{type:"float",value:a.x},y:{type:"float",value:a.y},z:{type:"float",value:a.z}})}case"builtin.viewDir":{const e=E(Nt.viewDir);return this.output(t,{value:{type:"vec3",value:e},x:{type:"float",value:e.x},y:{type:"float",value:e.y},z:{type:"float",value:e.z}})}case"builtin.normal":{const e=E(M.normal);return this.output(t,{value:{type:"vec3",value:e},x:{type:"float",value:e.x},y:{type:"float",value:e.y},z:{type:"float",value:e.z}})}case"builtin.time":return this.output(t,{value:{type:"float",value:It.elapsed}});case"builtin.camera":{const e=Gt(te),a=Gt(ee),r=Ut.cameraPosition;return this.output(t,{position:{type:"vec3",value:r},x:{type:"float",value:r.x},y:{type:"float",value:r.y},z:{type:"float",value:r.z},near:{type:"float",value:e},far:{type:"float",value:a}})}case"builtin.viewMatrix":return this.output(t,{value:{type:"mat4",value:Ut.viewMatrix}});case"builtin.projectionMatrix":return this.output(t,{value:{type:"mat4",value:Ut.projectionMatrix}});case"builtin.modelViewMatrix":return this.output(t,{value:{type:"mat4",value:Ut.modelViewMatrix}});case"builtin.particle":return this.output(t,{energy:{type:"float",value:ot.energy},color:{type:"rgb",value:ot.color},opacity:{type:"float",value:ot.opacity},time:{type:"float",value:ot.time}});case"math.add":case"math.subtract":case"math.multiply":case"math.divide":return this.output(t,{value:this.compileBinaryMath(t)});case"math.oneMinus":{const e=this.expectNumeric(this.input(t,"value"));return this.output(t,{value:{type:e.type,value:jt(e.value)}})}case"math.abs":return this.output(t,{value:this.compileUnaryMath(t,T)});case"math.sign":return this.output(t,{value:this.compileUnaryMath(t,dt)});case"math.floor":return this.output(t,{value:this.compileUnaryMath(t,q)});case"math.ceil":return this.output(t,{value:this.compileUnaryMath(t,B)});case"math.fract":return this.output(t,{value:this.compileUnaryMath(t,F)});case"math.sin":return this.output(t,{value:this.compileUnaryMath(t,mt)});case"math.cos":return this.output(t,{value:this.compileUnaryMath(t,z)});case"math.tan":return this.output(t,{value:this.compileUnaryMath(t,St)});case"math.asin":return this.output(t,{value:this.compileUnaryMath(t,O)});case"math.acos":return this.output(t,{value:this.compileUnaryMath(t,I)});case"math.atan":return this.output(t,{value:this.compileUnaryMath(t,N)});case"math.atan2":return this.output(t,{value:this.compileAtan2(t)});case"math.radians":return this.output(t,{value:this.compileUnaryMath(t,pt)});case"math.degrees":return this.output(t,{value:this.compileUnaryMath(t,P)});case"math.sqrt":return this.output(t,{value:this.compileUnaryMath(t,gt)});case"math.inverseSqrt":return this.output(t,{value:this.compileUnaryMath(t,_)});case"math.exp":return this.output(t,{value:this.compileUnaryMath(t,D)});case"math.exp2":return this.output(t,{value:this.compileUnaryMath(t,L)});case"math.log":return this.output(t,{value:this.compileUnaryMath(t,Z)});case"math.log2":return this.output(t,{value:this.compileUnaryMath(t,tt)});case"math.saturate":return this.output(t,{value:this.compileUnaryMath(t,vt)});case"math.length":{const e=this.expectNumeric(this.input(t,"value"));return this.output(t,{value:{type:"float",value:Q(e.value)}})}case"math.normalize":return this.output(t,{value:this.compileUnaryMath(t,st)});case"math.clamp":{const e=this.expectNumeric(this.input(t,"value")),a=this.expectNumeric(this.input(t,"min",{type:"float",value:R(0)})),r=this.expectNumeric(this.input(t,"max",{type:"float",value:R(1)}));return this.output(t,{value:{type:e.type,value:$(e.value,a.value,r.value)}})}case"math.min":return this.output(t,{value:this.compileBinaryFunction(t,rt)});case"math.max":return this.output(t,{value:this.compileBinaryFunction(t,at)});case"math.pow":return this.output(t,{value:this.compileBinaryFunction(t,lt)});case"math.mod":return this.output(t,{value:this.compileBinaryFunction(t,nt)});case"math.step":return this.output(t,{value:this.compileBinaryFunction(t,wt,!0)});case"math.smoothstep":{const e=this.expectNumeric(this.input(t,"edge0",{type:"float",value:R(0)})),a=this.expectNumeric(this.input(t,"edge1",{type:"float",value:R(1)})),r=this.expectNumeric(this.input(t,"value"));return this.output(t,{value:{type:r.type,value:ft(e.value,a.value,r.value)}})}case"math.mix":{const e=this.expectNumeric(this.input(t,"a")),a=this.expectNumeric(this.input(t,"b")),r=this.expectNumeric(this.input(t,"t",{type:"float",value:R(.5)})),u=je(e.type,a.type,t.id);return this.output(t,{value:{type:u,value:ut(e.value,a.value,r.value)}})}case"math.remap":{const e=this.expectType(this.input(t,"value"),["float"],t.id,"value").value,a=this.floatInput(t,"inMin",-1),r=this.floatInput(t,"inMax",1),u=this.floatInput(t,"outMin",0),i=this.floatInput(t,"outMax",1);return this.output(t,{value:{type:"float",value:Lt(e,a,r,u,i)}})}case"math.select":{const e=this.boolInput(t,"condition",!1),a=this.expectNumeric(this.input(t,"a")),r=this.expectNumeric(this.input(t,"b")),u=je(a.type,r.type,t.id);return this.output(t,{value:{type:u,value:yt(e,a.value,r.value)}})}case"math.reflect":{const e=this.expectType(this.input(t,"incident"),["vec2","vec3","vec4","rgb","rgba"],t.id,"incident"),a=this.expectType(this.input(t,"normal"),["vec2","vec3","vec4","rgb","rgba"],t.id,"normal");return this.output(t,{value:{type:e.type,value:fe(e.value,a.value)}})}case"math.refract":{const e=this.expectType(this.input(t,"incident"),["vec2","vec3","vec4","rgb","rgba"],t.id,"incident"),a=this.expectType(this.input(t,"normal"),["vec2","vec3","vec4","rgb","rgba"],t.id,"normal"),r=this.floatInput(t,"ior",1.5);return this.output(t,{value:{type:e.type,value:ge(e.value,a.value,r)}})}case"math.derivative":{const e=this.expectNumeric(this.input(t,"value")),a=String(ke(t,"mode","fwidth"));let r;return r="dFdx"===a?be(e.value):"dFdy"===a?xe(e.value):we(e.value),this.output(t,{value:{type:e.type,value:r}})}case"layer.mixPainted":return this.output(t,{value:this.compilePaintedLayerMix(t)});case"math.dot":{const e=this.expectNumeric(this.input(t,"a")),a=this.expectNumeric(this.input(t,"b"));return je(e.type,a.type,t.id),this.output(t,{value:{type:"float",value:j(e.value,a.value)}})}case"math.distance":{const e=this.expectNumeric(this.input(t,"a")),a=this.expectNumeric(this.input(t,"b"));return je(e.type,a.type,t.id),this.output(t,{value:{type:"float",value:A(e.value,a.value)}})}case"math.cross":{const e=this.expectType(this.input(t,"a"),["vec3","rgb"],t.id,"a"),a=this.expectType(this.input(t,"b"),["vec3","rgb"],t.id,"b");return this.output(t,{value:{type:"vec3",value:V(e.value,a.value)}})}case"compare.greaterThan":case"compare.greaterThanOrEqual":case"compare.lessThan":case"compare.lessThanOrEqual":case"compare.equal":case"compare.notEqual":return this.output(t,{value:{type:"boolean",value:this.compileComparison(t)}});case"boolean.not":{const e=this.boolInput(t,"value",!1);return this.output(t,{value:{type:"boolean",value:yt(e,U(!1),U(!0))}})}case"boolean.and":return this.output(t,{value:{type:"boolean",value:this.compileBooleanBinary(t,"and")}});case"boolean.or":return this.output(t,{value:{type:"boolean",value:this.compileBooleanBinary(t,"or")}});case"compose.vec2":return this.output(t,{value:{type:"vec2",value:$t(this.floatInput(t,"x",0),this.floatInput(t,"y",0))}});case"compose.vec3":return this.output(t,{value:{type:"vec3",value:kt(this.floatInput(t,"x",0),this.floatInput(t,"y",0),this.floatInput(t,"z",0))}});case"compose.vec4":return this.output(t,{value:{type:"vec4",value:zt(this.floatInput(t,"x",0),this.floatInput(t,"y",0),this.floatInput(t,"z",0),this.floatInput(t,"w",0))}});case"compose.rgba":return this.output(t,{value:{type:"rgba",value:zt(this.floatInput(t,"r",1),this.floatInput(t,"g",1),this.floatInput(t,"b",1),this.floatInput(t,"a",1))}});case"decompose.component":{const e=this.expectNumeric(this.input(t,"value")),a=String(ke(t,"component","x"));return this.output(t,{value:{type:"float",value:Le(e,a,t.id)}})}case"decompose.split":{const e=this.expectNumeric(this.input(t,"value"));return this.output(t,function(t,e){const a={},r=(r,u)=>{a[r]={type:"float",value:Le(t,u,e)}};switch(t.type){case"vec2":return r("x","x"),r("y","y"),r("u","x"),r("v","y"),a;case"vec3":return r("x","x"),r("y","y"),r("z","z"),a;case"rgb":return r("r","r"),r("g","g"),r("b","b"),r("x","x"),r("y","y"),r("z","z"),a;case"vec4":return r("x","x"),r("y","y"),r("z","z"),r("w","w"),a;case"rgba":return r("r","r"),r("g","g"),r("b","b"),r("a","a"),r("x","x"),r("y","y"),r("z","z"),r("w","w"),a;case"float":return a.x={type:"float",value:t.value},a}}(e,t.id))}case"color.gradient":{const e=this.expectType(this.input(t,"t",{type:"float",value:R(.5)}),["float"],t.id,"t"),a=H(function(t){const e=ke(t,"stops",[]);if(!Array.isArray(e))return[];const a=e.map((t,a)=>{if(null==t||"object"!=typeof t||Array.isArray(t))return;const r=t,u="string"==typeof r.color?r.color:"#ffffff";return{position:Fe(r.position,Je(a,e.length)),color:u,alpha:Fe(r.alpha,1)}}).filter(t=>null!=t);return a.length>0?a:[]}(t)).sample(e.value);return this.output(t,{value:{type:"rgba",value:a}})}case"utility.curve":{const e=this.expectType(this.input(t,"t",{type:"float",value:R(.5)}),["float"],t.id,"t"),a=ke(t,"curve","Linear"),r=K(a,e.value)??Y(Te.decode(a)).sample(e.value);return this.output(t,{value:{type:"float",value:r}})}case"utility.namedRerouteDeclaration":return this.output(t,{value:this.input(t,"value")});case"utility.namedRerouteUsage":{const e=this.compileNode(this.namedRerouteDeclaration(t).id);return this.output(t,{value:e.outputs.value})}case"color.hueShift":{const e=this.expectType(this.input(t,"color"),["rgba","rgb","vec4","vec3"],t.id,"color"),a=e.value,r="rgba"===e.type||"vec4"===e.type?a.rgb??a.xyz:a,u=this.floatInput(t,"shift",0),i=Rt(r,u);return"rgba"===e.type||"vec4"===e.type?this.output(t,{value:{type:e.type,value:zt(i,a.a??a.w)}}):this.output(t,{value:{type:e.type,value:i}})}case"color.blend":{const e=this.expectType(this.input(t,"base"),["rgba","rgb","vec4","vec3"],t.id,"base"),a=this.expectType(this.input(t,"blend"),["rgba","rgb","vec4","vec3"],t.id,"blend"),r=String(ke(t,"mode","multiply")),u=e.value,i=a.value,o="rgba"===e.type||"vec4"===e.type?u.rgb??u.xyz:u,n="rgba"===a.type||"vec4"===a.type?i.rgb??i.xyz:i;let s=qt(o,n,r);if("rgba"===a.type||"vec4"===a.type){const t=i.a??i.w;s=ut(o,s,t)}if("rgba"===e.type||"vec4"===e.type){const a=u.a??u.w;return this.output(t,{value:{type:e.type,value:zt(s,a)}})}return this.output(t,{value:{type:e.type,value:s}})}case"color.desaturate":{const e=this.expectType(this.input(t,"color"),["rgba","rgb","vec4","vec3"],t.id,"color"),a=e.value,r="rgba"===e.type||"vec4"===e.type?a.rgb??a.xyz:a,u=this.floatInput(t,"fraction",1),i=Ft(r,u);return"rgba"===e.type||"vec4"===e.type?this.output(t,{value:{type:e.type,value:zt(i,a.a??a.w)}}):this.output(t,{value:{type:e.type,value:i}})}case"color.unpackNormal":{const e=this.expectType(this.input(t,"color"),["vec3","vec4","rgb","rgba"],t.id,"color"),a=this.floatInput(t,"scale",1),r=this.optionalInput(t,"normal");return this.output(t,{value:{type:"vec3",value:k(e.value,a,r?this.expectType(r,["vec3"],t.id,"normal").value:void 0)}})}case"texture.sampler2d":{const e=this.input(t,"texture");return this.output(t,{value:this.toSampler(e,t.id)})}case"texture.sample2d":{const e=this.toSampler(this.input(t,"sampler"),t.id),a=this.expectType(this.input(t,"uv",{type:"vec2",value:this.defaultUV()}),["vec2"],t.id,"uv"),r=e.value.sample(a.value);return this.output(t,{rgba:{type:"rgba",value:r},rgb:{type:"rgb",value:r.rgb},r:{type:"float",value:r.r},g:{type:"float",value:r.g},b:{type:"float",value:r.b},a:{type:"float",value:r.a}})}case"texture.triplanarMapping":{const e=this.input(t,"sampler"),a=this.toSampler(e,t.id),r=this.expectType(this.input(t,"scale",{type:"float",value:R(1)}),["float"],t.id,"scale"),u=this.expectType(this.input(t,"normal",{type:"vec3",value:Bt.normal}),["vec3"],t.id,"normal"),i=this.expectType(this.input(t,"position",{type:"vec3",value:Bt.position}),["vec3"],t.id,"position"),o=Ct(a.value,r.value,u.value,i.value);return this.output(t,{rgba:{type:"rgba",value:o},rgb:{type:"rgb",value:o.rgb},r:{type:"float",value:o.r},g:{type:"float",value:o.g},b:{type:"float",value:o.b},a:{type:"float",value:o.a}})}case"scene.sampleColor":{const e=this.expectType(this.input(t,"uv",{type:"vec2",value:Zt}),["vec2"],t.id,"uv"),a=ae(e.value);return this.output(t,{rgba:{type:"rgba",value:a},rgb:{type:"rgb",value:a.rgb},r:{type:"float",value:a.r},g:{type:"float",value:a.g},b:{type:"float",value:a.b},a:{type:"float",value:a.a}})}case"scene.sampleDepth":{const e=this.expectType(this.input(t,"uv",{type:"vec2",value:Zt}),["vec2"],t.id,"uv");return this.output(t,{depth:{type:"float",value:Qt(e.value)},linearEyeDepth:{type:"float",value:Xt(e.value)}})}case"scene.fragmentDepth":return this.output(t,{linearEyeDepth:{type:"float",value:Kt}});case"transform.translate":{const e=this.expectType(this.input(t,"offset",{type:"vec3",value:kt(0,0,0)}),["vec3","rgb"],t.id,"offset");return this.output(t,{value:{type:"mat4",value:Mt(e.value)}})}case"transform.scale":{const e=this.expectType(this.input(t,"scale",{type:"vec3",value:kt(1,1,1)}),["vec3","rgb"],t.id,"scale").value;return this.output(t,{value:{type:"mat4",value:xt(e.x,e.y,e.z)}})}case"transform.rotateAxis":{const e=this.expectType(this.input(t,"axis",{type:"vec3",value:kt(0,1,0)}),["vec3","rgb"],t.id,"axis"),a=this.expectType(this.input(t,"angle",{type:"float",value:R(0)}),["float"],t.id,"angle");return this.output(t,{value:{type:"mat4",value:le(e.value,a.value)}})}case"uv.rotate":{const e=this.expectType(this.input(t,"uv",{type:"vec2",value:this.defaultUV()}),["vec2"],t.id,"uv").value,a=this.expectType(this.input(t,"angle",{type:"float",value:R(0)}),["float"],t.id,"angle").value,r=this.expectType(this.input(t,"center",{type:"vec2",value:$t(.5,.5)}),["vec2"],t.id,"center").value;return this.output(t,{value:{type:"vec2",value:De(e,a,r)}})}case"uv.twirl":{const e=this.expectType(this.input(t,"uv",{type:"vec2",value:this.defaultUV()}),["vec2"],t.id,"uv").value,a=this.expectType(this.input(t,"strength",{type:"float",value:R(1)}),["float"],t.id,"strength").value,r=this.expectType(this.input(t,"center",{type:"vec2",value:$t(.5,.5)}),["vec2"],t.id,"center").value,u=this.expectType(this.input(t,"offset",{type:"vec2",value:$t(0,0)}),["vec2"],t.id,"offset").value;return this.output(t,{value:{type:"vec2",value:Et(e,a,r,u)}})}case"uv.radial":{const e=this.expectType(this.input(t,"uv",{type:"vec2",value:this.defaultUV()}),["vec2"],t.id,"uv").value,a=Dt(e.subtractScalar(.5));return this.output(t,{coords:{type:"vec2",value:a.coords},radius:{type:"float",value:a.radius},angle:{type:"float",value:a.angle}})}case"uv.bulge":{const e=this.expectType(this.input(t,"uv",{type:"vec2",value:this.defaultUV()}),["vec2"],t.id,"uv").value,a=this.expectType(this.input(t,"center",{type:"vec2",value:$t(.5,.5)}),["vec2"],t.id,"center").value,r=this.expectType(this.input(t,"power",{type:"float",value:R(1)}),["float"],t.id,"power").value;return this.output(t,{value:{type:"vec2",value:G(e,a,r)}})}case"uv.flipbook":{const e=this.expectType(this.input(t,"uv",{type:"vec2",value:this.defaultUV()}),["vec2"],t.id,"uv").value,a=this.floatInput(t,"columns",ze(t,"columns",1)),r=this.floatInput(t,"rows",ze(t,"rows",1)),u=this.floatInput(t,"fps",ze(t,"fps",60)),i=this.expectType(this.input(t,"time",{type:"float",value:It.elapsed}),["float"],t.id,"time").value,o=String(ke(t,"mode","clamp"));return this.output(t,{value:{type:"vec2",value:pe(e,a,r,i,u,o)}})}case"uv.pom":{const e=this.expectType(this.input(t,"uv",{type:"vec2",value:this.defaultUV()}),["vec2"],t.id,"uv").value,a=this.input(t,"heightMap"),r=this.toSampler(a,t.id).value,u=this.floatInput(t,"heightScale",.05),i=ze(t,"minLayers",8),o=ze(t,"maxLayers",24);return this.output(t,{value:{type:"vec2",value:ce(e,r,u,i,o)}})}case"shape.rectangle":{const e=this.expectType(this.input(t,"uv",{type:"vec2",value:this.defaultUV()}),["vec2"],t.id,"uv").value;return this.output(t,{value:{type:"float",value:_t(e,this.floatInput(t,"width",.5),this.floatInput(t,"height",.5))}})}case"shape.roundedRectangle":{const e=this.expectType(this.input(t,"uv",{type:"vec2",value:this.defaultUV()}),["vec2"],t.id,"uv").value;return this.output(t,{value:{type:"float",value:Wt(e,this.floatInput(t,"width",.5),this.floatInput(t,"height",.5),this.floatInput(t,"radius",.1))}})}case"noise.simplex":{const e=this.expectType(this.input(t,"uv",{type:"vec2",value:this.defaultUV()}),["vec2"],t.id,"uv").value,a=this.floatInput(t,"scale",8);return this.output(t,{value:{type:"float",value:new ne(e.multiplyScalar(a))}})}case"noise.voronoi2d":{const e=this.expectType(this.input(t,"uv",{type:"vec2",value:this.defaultUV()}),["vec2"],t.id,"uv").value,a=this.floatInput(t,"scale",8);return this.output(t,{value:{type:"float",value:new se(e.multiplyScalar(a))}})}case"effect.fresnel":return this.output(t,{value:{type:"float",value:E(Yt(this.floatInput(t,"power",1)))}});case"effect.edgeDepth":{const e=this.floatInput(t,"power",1);return this.output(t,{value:{type:"float",value:Ht(e)}})}case"effect.depthFade":{const e=this.floatInput(t,"distance",1);return this.output(t,{value:{type:"float",value:Jt(e)}})}case"util.panner":{const e=this.expectType(this.input(t,"uv",{type:"vec2",value:this.defaultUV()}),["vec2"],t.id,"coord"),a=this.expectType(this.input(t,"speed",{type:"vec2",value:$t(0,0)}),["vec2"],t.id,"speed"),r=this.expectType(this.input(t,"tile",{type:"vec2",value:$t(1,1)}),["vec2"],t.id,"tile"),u=this.expectType(this.input(t,"time",{type:"float",value:It.elapsed}),["float"],t.id,"time");return this.output(t,{value:{type:"vec2",value:e.value.multiply(r.value).add(a.value.multiplyScalar(u.value))}})}}}compileParameterNode(t){const e=String(ke(t,"parameter","")),a=this.graph.parameters?.find(t=>t.name===e||t.id===e);if(null==a)throw new ShaderGraphCompileError(`Parameter "${e}" does not exist`,t.id);const r=de(a.type),u={type:r,value:Pe(this.options.parameters?.[a.name]??a.defaultValue,r,a.name)};if("sampler2d"!==r)return{value:u};const i=this.expectType(this.input(t,"uv",{type:"vec2",value:this.defaultUV()}),["vec2"],t.id,"uv"),o=u.value.sample(i.value);return{value:{type:"rgba",value:o},rgb:{type:"rgb",value:o.rgb},r:{type:"float",value:o.r},g:{type:"float",value:o.g},b:{type:"float",value:o.b},a:{type:"float",value:o.a},sampler:u}}compileBinaryMath(t){const e=this.expectBinaryMathValue(this.input(t,"a")),a=this.expectBinaryMathValue(this.input(t,"b")),r=t.kind.split(".")[1];if("mat4"===e.type||"mat4"===a.type)return function(t,e,a,r){if("mat4"===t.type&&"mat4"===e.type&&"divide"!==a)return{type:"mat4",value:Ae(t.value,e.value,a,r)};if("mat4"===t.type&&"float"===e.type&&("multiply"===a||"divide"===a))return{type:"mat4",value:Ae(t.value,e.value,a,r)};if("float"===t.type&&"mat4"===e.type&&"multiply"===a)return{type:"mat4",value:Ae(e.value,t.value,a,r)};if("mat4"===t.type&&"vec4"===e.type&&"multiply"===a)return{type:"vec4",value:t.value.multiplyVec(e.value)};throw new ShaderGraphCompileError(`Cannot ${a} ${t.type} and ${e.type}`,r)}(e,a,r,t.id);if(e.type===a.type)return{type:e.type,value:Ae(e.value,a.value,r,t.id)};if("float"===e.type&&"float"!==a.type&&"multiply"===r)return{type:a.type,value:Ae(a.value,e.value,r,t.id)};if("float"!==e.type&&"float"===a.type)return{type:e.type,value:Ae(e.value,a.value,r,t.id)};throw new ShaderGraphCompileError(`Cannot ${r} ${e.type} and ${a.type}`,t.id)}expectBinaryMathValue(t){if(!Re(t.type)&&"mat4"!==t.type)throw new ShaderGraphCompileError(`Expected a numeric value but got ${t.type}`);return t}compileUnaryMath(t,e){const a=this.expectNumeric(this.input(t,"value"));return{type:a.type,value:e(a.value)}}compileAtan2(t){const e=this.expectNumeric(this.input(t,"y")),a=this.expectNumeric(this.input(t,"x"));return{type:function(t,e,a){if(t===e)return t;if("rgb"===t&&"vec3"===e||"vec3"===t&&"rgb"===e)return"vec3";if("rgba"===t&&"vec4"===e||"vec4"===t&&"rgba"===e)return"vec4";throw new ShaderGraphCompileError(`Cannot mix ${t} and ${e}`,a)}(e.type,a.type,t.id),value:C(e.value,a.value)}}compileBinaryFunction(t,e,a=!1){const r=this.expectNumeric(this.input(t,"a")),u=this.expectNumeric(this.input(t,"b"));return{type:a&&"float"===u.type?r.type:je(r.type,u.type,t.id),value:e(r.value,u.value)}}compilePaintedLayerMix(t){const e=this.expectNumeric(this.input(t,"base")),a=[e.value],r=[Vt(0)];let u=e.type;for(let e=1;e<=8;e++){const i=this.optionalInput(t,`layer${e}`);if(null==i)continue;const o=this.expectNumeric(i);u=je(u,o.type,t.id),a.push(o.value),r.push(Vt(e))}return 1===a.length?e:{type:u,value:it({layerColors:a,layerWeights:r,enableNoise:Boolean(ke(t,"enableNoise",!0)),noiseScale:this.floatInput(t,"noiseScale",ze(t,"noiseScale",.1)),noiseAmount:this.floatInput(t,"noiseAmount",ze(t,"noiseAmount",.5)),decay:this.floatInput(t,"softness",ze(t,"softness",.3)),mode:"hard"===String(ke(t,"mode","soft"))?X.hard:X.soft})}}defaultUV(){return"trail"===this.graph.target?oe:"decal"===this.graph.target?ue:E(M.uv)}namedRerouteDeclaration(t){const e=String(t.params?.declarationId??"");if(0===e.length)throw new ShaderGraphCompileError("Named reroute usage is missing a declaration",t.id);const a=this.nodesById.get(e);if(null==a)throw new ShaderGraphCompileError(`Named reroute declaration "${e}" does not exist`,t.id);if("utility.namedRerouteDeclaration"!==a.kind)throw new ShaderGraphCompileError(`Node "${e}" is not a named reroute declaration`,t.id);return a}input(t,e,a){const r=t.inputs?.[e];if(null!=r)return this.resolveInput(r,t.id,e);const u=(this.graph.edges??[]).find(a=>a.to.nodeId===t.id&&a.to.port===e);if(null!=u)return this.resolveSocket(u.from,t.id,e);if(null!=a)return a;const i=this.inlineLiteralFallback(t,e);if(null!=i)return i;throw new ShaderGraphCompileError(`Missing input "${e}"`,t.id,e)}optionalInput(t,e){const a=t.inputs?.[e];if(null!=a)return this.resolveInput(a,t.id,e);const r=(this.graph.edges??[]).find(a=>a.to.nodeId===t.id&&a.to.port===e);return null!=r?this.resolveSocket(r.from,t.id,e):void 0}floatInput(t,e,a){return this.expectType(this.input(t,e,{type:"float",value:R(a)}),["float"],t.id,e).value}boolInput(t,e,a){return this.expectType(this.input(t,e,{type:"boolean",value:U(a)}),["boolean"],t.id,e).value}compileComparison(t){const e=this.expectType(this.input(t,"a"),["float"],t.id,"a").value,a=this.expectType(this.input(t,"b"),["float"],t.id,"b").value;switch(t.kind){case"compare.greaterThan":return e.gt(a);case"compare.greaterThanOrEqual":return e.gte(a);case"compare.lessThan":return e.lt(a);case"compare.lessThanOrEqual":return e.lte(a);case"compare.equal":return e.equals(a);case"compare.notEqual":return e.notEquals(a)}throw new ShaderGraphCompileError(`Unsupported comparison node "${t.kind}"`,t.id)}compileBooleanBinary(t,e){const a=this.boolInput(t,"a",!1),r=this.boolInput(t,"b",!1);return"and"===e?a.and(r):a.or(r)}resolveOutputBinding(t,e){if(null!=t.input)return this.resolveSocket(t.input,void 0,e);if(void 0!==t.value)return Ve(t.value);throw new ShaderGraphCompileError(`Output "${e}" is missing an input or value`)}floatOutput(t,e){return this.expectType(this.resolveOutputBinding(t,e),["float"],e).value}resolveInput(t,e,a){return function(t){return null!=t.nodeId}(t)?this.resolveSocket(t,e,a):Ve(t.value,t.valueType)}inlineLiteralFallback(t,e){const a=ye[t.kind]?.inputs.find(t=>t.name===e);if(null==a)return;const r=ve(t.kind,a),u=he(t.kind,a);return null!=r&&void 0!==u?Ve(u,r):void 0}resolveSocket(t,e,a){const r=this.compileNode(t.nodeId).outputs[t.port];if(null==r)throw new ShaderGraphCompileError(`Node "${t.nodeId}" does not have output "${t.port}"`,e??t.nodeId,a??t.port);return r}expectType(t,e,a,r){if(!e.includes(t.type))throw new ShaderGraphCompileError(`Expected ${e.join(" or ")} but got ${t.type}`,a,r);return t}expectNumeric(t){if(!Re(t.type))throw new ShaderGraphCompileError(`Expected a numeric value but got ${t.type}`);return t}toSampler(t,e){if("sampler2d"===t.type)return t;if("texture2d"===t.type){const a=t.value;if(null==a)throw new ShaderGraphCompileError("Texture input is empty",e);return{type:"sampler2d",value:Tt(a)}}throw new ShaderGraphCompileError(`Expected texture2d or sampler2d but got ${t.type}`,e)}toColorOutput(t){switch(t.type){case"float":case"vec2":case"vec3":case"vec4":case"rgb":case"rgba":return t.value;case"boolean":{const e=yt(t.value,R(1),R(0));return ct(kt(e,e,e),1)}case"mat4":throw new ShaderGraphCompileError("mat4 values cannot be used as color");case"texture2d":case"sampler2d":throw new ShaderGraphCompileError(`${t.type} must be sampled before it can be used as color`)}}toVec3Output(t,e){if("vec3"===t.type||"rgb"===t.type)return t.value;if("vec4"===t.type||"rgba"===t.type)return t.value.xyz;throw new ShaderGraphCompileError(`Output "${e}" expects vec3/rgb but got ${t.type}`)}toMat4Output(t,e){if("mat4"===t.type)return t.value;throw new ShaderGraphCompileError(`Output "${e}" expects mat4 but got ${t.type}`)}output(t,e){return{node:t,outputs:e}}}export function compileShaderGraph(t,e={}){return new ShaderGraphCompiler(t,e).compile()}export function compileShaderGraphPreview(t,e,a={}){if(null==e)return compileShaderGraph(t,a).output;const r=new ShaderGraphCompiler(t,a).compileNode(e),u=Object.values(r.outputs)[0];if(null==u)throw new ShaderGraphCompileError(`Node "${e}" has no previewable output`,e);return{color:Ge(u),transparent:"rgba"===u.type||"vec4"===u.type}}export function buildShaderGraphMaterial(t,a={}){if("postProcess"===t.target)return buildShaderGraphPostProcessMaterial(t,a);const r=function(t,e,a){const r=a.alphaTest??t.alphaTest??e.materialOptions?.alphaTest;return null!=r?{...t,alphaTest:r}:t}(null!=a.previewNodeId?compileShaderGraphPreview(t,a.previewNodeId,a):compileShaderGraph(t,a).output,t,a);if("sprite"===t.target)return new Pt({color:r.color,discard:r.discard,alphaTest:r.alphaTest,transparent:r.transparent??!0,position:At(new b("rotation",0),new b("screenSpaceSize",0)),uniforms:{color:{value:new e(16777215)}}});if("trail"===t.target){const{position:e}=ie.getTrailShaderNodes(a.trailBillboard??!1),u=new y({...r,position:e});return ie.applyTrailShaderParameters(u),Ee(u,t),u}if("decal"===t.target){const e=null!=r.discard?re.or(r.discard):re,a=new y({...r,transparent:!1,discard:e});return a.userData.isDecal=!0,Ee(a,t),a}const u=new y({transparent:!1,...r});return Ee(u,t),u}export function buildShaderGraphPostProcessMaterial(t,e={}){const a=null!=e.previewNodeId?Me(compileShaderGraphPreview(t,e.previewNodeId,e).color):compileShaderGraph(t,e).output,r=new y({color:a.color,transparent:!1,fog:!1,lights:!1,outputEncoding:!1});return r.depthWrite=!1,r.depthTest=!1,r.toneMapped=!1,r.userData.isPostProcessShaderGraph=!0,r}export function buildShaderGraphInlinePreviewMaterial(t,e,a={}){const r=compileShaderGraphPreview({...t,target:"surface"},e,a),u=new y({color:r.color,transparent:r.transparent??!1,fog:!1,lights:!1});return u.depthWrite=!1,u.depthTest=!1,u.toneMapped=!1,u}function Me(t,e){const a=ae(Zt),r=Gt("hology_post_process_weight",1),u=vt(null!=e?r.multiply(e):r);return{color:ut(a.rgb,Ne(t),u),transparent:!1,...null!=e?{opacity:e}:{}}}function Ne(t){return t instanceof h?kt(t,t,t):t instanceof x?kt(t.x,t.y,0):t instanceof S?t.xyz:t}export function shaderGraphMaterialSideToThree(e){switch(e){case"back":return t;case"double":return r;default:return u}}function Ce(t){return"decal"!==t.target&&(t.materialOptions?.transparent??"sprite"===t.target)}function Ee(t,e){"surface"===e.target&&(t.side=shaderGraphMaterialSideToThree(e.materialOptions?.side)),null!=e.materialOptions?.transparent&&(t.transparent=e.materialOptions.transparent),null!=e.materialOptions?.bloom&&(t.userData.hasBloom=e.materialOptions.bloom)}function Ge(t){switch(t.type){case"float":return Ue(t.value);case"vec2":{const e=t.value;return ct(kt(e.x,e.y,0),1)}case"vec3":case"rgb":return ct(t.value,1);case"vec4":case"rgba":return t.value;case"boolean":return Ue(yt(t.value,R(1),R(0)));case"mat4":throw new ShaderGraphCompileError("mat4 values cannot be previewed as color");case"texture2d":case"sampler2d":throw new ShaderGraphCompileError(`${t.type} nodes require a sampled output to preview`)}}function Ue(t){return ct(kt(t,t,t),1)}function Be(t){return t instanceof h?kt(t,t,t).rgb:t instanceof x?kt(t.x,t.y,0).rgb:t}function $e(t){return t.rgb}function ke(t,e,a){const r=t.params?.[e];return void 0===r?a:r}function ze(t,e,a){const r=ke(t,e,a);return"number"==typeof r?r:Number(r??a)}function Ve(t,e){if(null!=(a=t)&&"object"==typeof a&&!Array.isArray(a)&&"value"in a)return Ve(t.value,t.valueType??e);var a;const r=e??function(t){if("boolean"==typeof t)return"boolean";if("number"==typeof t)return"float";if(Array.isArray(t))return 2===t.length?"vec2":3===t.length?"vec3":16===t.length?"mat4":"vec4";return"string"==typeof t?"rgb":"float"}(t);return{type:r,value:Pe(t,r,"literal")}}function Pe(t,a,r){switch(a){case"boolean":return t instanceof c?t:U(Boolean(t));case"float":return t instanceof h?t:R(Fe(t,0));case"vec2":return t instanceof x?t:We(t);case"vec3":return t instanceof w?t:He(t);case"vec4":return t instanceof S?t:function(t){const[e,a,r,u]=_e(t,4,1);return zt(e,a,r,u)}(t);case"mat4":return t instanceof v?t:function(t){if(t instanceof i)return et(t);const e=new i;Array.isArray(t)&&16===t.length&&e.fromArray(t.map(t=>Fe(t,0)));return et(e)}(t);case"rgb":return t instanceof d?t:function(t){if("string"==typeof t||"number"==typeof t||t instanceof e)return ht(t);const[a,r,u]=_e(t,3);return kt(a,r,u).rgb}(t);case"rgba":return t instanceof m||t instanceof S?t:Ye(t);case"texture2d":return t;case"sampler2d":return function(t){const e=t;return t instanceof f||t instanceof g||"function"==typeof e?.isSampler2D||!0===e?.isSampler2D}(t)?t:t instanceof n||!0===t?.isTexture?Tt(t):(Oe.has(r)||(Oe.add(r),console.warn(`Shader graph parameter "${r}" is missing a texture value; using a 1x1 white fallback texture.`)),Tt(Ie))}}function Ae(t,e,a,r){const u=`${a}Scalar`;if(e instanceof h&&"function"==typeof t[u])return t[u](e);if("function"==typeof t[a])return t[a](e);throw new ShaderGraphCompileError(`Node does not support ${a}`,r)}function je(t,e,a){if(t===e)return t;if("rgb"===t&&"vec3"===e||"vec3"===t&&"rgb"===e)return"vec3";if("rgba"===t&&"vec4"===e||"vec4"===t&&"rgba"===e)return"vec4";if("float"===t&&Re(e))return e;if("float"===e&&Re(t))return t;throw new ShaderGraphCompileError(`Cannot mix ${t} and ${e}`,a)}function De(t,e,a){const r=t.subtract(a),u=mt(e),i=z(e);return $t(i.multiply(r.x).subtract(u.multiply(r.y)).add(a.x),u.multiply(r.x).add(i.multiply(r.y)).add(a.y))}function Le(t,e,a){const r=t.value["u"===e?"x":"v"===e?"y":e];if(r instanceof h)return r;throw new ShaderGraphCompileError(`Component "${e}" is not available on ${t.type}`,a)}function Re(t){return"float"===t||"vec2"===t||"vec3"===t||"vec4"===t||"rgb"===t||"rgba"===t}function qe(t){return Number.isFinite(t)?Math.min(1,Math.max(0,t)):0}function Fe(t,e){if("number"==typeof t)return t;if("string"==typeof t){const a=parseFloat(t);return Number.isFinite(a)?a:e}return"boolean"==typeof t?t?1:0:e}function _e(t,a,r=0){if(Array.isArray(t))return Array.from({length:a},(e,a)=>Fe(t[a],r));if(t instanceof s||t instanceof l||t instanceof p)return Array.from({length:a},(e,a)=>t.toArray()[a]??r);if(t instanceof e){const e=t.toArray();return Array.from({length:a},(t,a)=>e[a]??(3===a?1:r))}if(null!=t&&"object"==typeof t){const e=t,u=["x","y","z","w"];if(u.some(t=>null!=e[t]))return Array.from({length:a},(t,a)=>Fe(e[u[a]],r));const i=["r","g","b","a"];if(i.some(t=>null!=e[t]))return Array.from({length:a},(t,a)=>Fe(e[i[a]],3===a?1:r))}return Array.from({length:a},()=>Fe(t,r))}function We(t){const[e,a]=_e(t,2);return $t(e,a)}function He(t){const[e,a,r]=_e(t,3);return kt(e,a,r)}function Ye(t,a=1){if("string"==typeof t||"number"==typeof t||t instanceof e)return ct(t,a);if(t instanceof w)return ct(t,a);const[r,u,i,o]=_e(t,4,a);return zt(r,u,i,o)}function Je(t,e){return e<=1?0:t/(e-1)}/*
|
|
1
|
+
import{BackSide as t,Color as e,DataTexture as a,DoubleSide as r,FrontSide as u,Matrix4 as i,RGBAFormat as o,Texture as n,Vector2 as s,Vector3 as l,Vector4 as p}from"three";import{BooleanNode as c,FloatNode as h,Mat4Node as v,NodeShaderMaterial as y,RgbNode as d,RgbaNode as m,Sampler2DNode as f,Texture2dLookupNode as g,UniformFloatNode as b,Vec2Node as x,Vec3Node as w,Vec4Node as S,abs as T,acos as I,asin as O,attributes as M,atan as N,atan2 as C,autoVarying as E,bulge as G,bool as U,ceil as B,clamp as $,colorToNormal as k,cos as z,cross as A,degrees as P,distance as V,dot as j,exp as D,exp2 as L,float as R,floor as q,fract as F,inversesqrt as _,batchingMatrix as W,colorGradientSampler as H,curveSampler as Y,ifDefApply as J,sampleNamedEasingCurve as K,length as Q,LayerMixMode as X,log as Z,log2 as tt,mat4 as et,max as at,min as rt,mix as ut,mixColorsByLayer as it,particleUniforms as ot,mod as nt,normalize as st,pow as lt,radians as pt,rgba as ct,rgb as ht,saturate as vt,select as yt,sign as dt,sin as mt,smoothstep as ft,sqrt as gt,standardMaterial as bt,scaleTransform as xt,step as wt,tan as St,textureSampler2d as Tt,timeUniforms as It,toonMaterial as Ot,translate as Mt,transformed as Nt,triplanarMapping as Ct,twirl as Et,uniformFloat as Gt,uniforms as Ut,varyingAttributes as Bt,vec2 as $t,vec3 as kt,vec4 as zt,getPaintedMaterialLayerWeight as At}from"../../shader-nodes/index.js";import{SpriteNodeShaderMaterial as Pt,getSpritePosition as Vt}from"../sprite-shader.js";import{oneMinus as jt}from"../../shader-nodes/index.js";import{vectorToRadial as Dt,remap as Lt,hueShift as Rt,blendColor as qt,desaturateColor as Ft}from"../../shader-nodes/math.js";import{rectangleFloat as _t,roundedRectangleFloat as Wt}from"../../shader-nodes/shapes.js";import{edgeDepthEffect as Ht,fresnelEffect as Yt,depthFadeEffect as Jt}from"../../shader-nodes/effects.js";import{fragmentLinearEyeDepth as Kt,sampleSceneDepth as Qt,sampleSceneLinearEyeDepth as Xt,screenUV as Zt,nearUniformName as te,farUniformName as ee}from"../../shader-nodes/depth.js";import{sampleSceneColor as ae}from"../../shader-nodes/scene-sample.js";import{decalDiscard as re,decalUV as ue}from"../../shader-nodes/decal.js";import{Trail as ie,trailUV as oe}from"../../effects/vfx/trail-renderer.js";import{SimplexNoiseNode as ne,Voronoi2d as se,rotateAxis as le}from"../../shader-nodes/index.js";import{flipbookUv as pe}from"../../shader-nodes/texture-sequence.js";import{parallaxOcclusionMapping as ce}from"../../shader-nodes/pom.js";import{defaultInlineLiteralValueForPort as he,inlineLiteralTypeForPort as ve,SHADER_GRAPH_NODE_DEFINITIONS as ye}from"./registry.js";import{shaderGraphParameterTypeToValueType as de}from"./parameters.js";import{attributeVec2 as me,varying as fe,reflect as ge,refract as be,dFdx as xe,dFdy as we,fwidth as Se}from"three-shader-graph";import{decalNormal as Te}from"../../shader-nodes/decal.js";import{Curve2 as Ie}from"../../utils/curve.js";const Oe=(()=>{const t=new a(new Uint8Array([255,255,255,255]),1,1,o);return t.name="Missing shader graph texture parameter fallback",t.needsUpdate=!0,t})(),Me=new Set;export class ShaderGraphCompileError extends Error{constructor(t,e,a){super(t),this.nodeId=e,this.port=a,this.name="ShaderGraphCompileError"}}export const shaderGraphPostProcessWeightUniformName="hology_post_process_weight";export class ShaderGraphCompiler{constructor(t,e={}){this.graph=t,this.options=e,this.compiled=new Map,this.compiling=new Set,this.nodesById=new Map((t.nodes??[]).map(t=>[t.id,t]))}validate(){this.compile()}compile(){if(1!==this.graph.version)throw new ShaderGraphCompileError(`Unsupported shader graph version ${this.graph.version}`);return this.validateEdges(),this.validateNamedReroutes(),{output:this.compileOutput(),nodes:this.compiled}}validateEdges(){for(const t of this.graph.edges??[]){if(!this.nodesById.has(t.from.nodeId))throw new ShaderGraphCompileError(`Edge references missing source node "${t.from.nodeId}"`,t.from.nodeId,t.from.port);if(!this.nodesById.has(t.to.nodeId))throw new ShaderGraphCompileError(`Edge references missing target node "${t.to.nodeId}"`,t.to.nodeId,t.to.port)}}validateNamedReroutes(){for(const t of this.graph.nodes??[])"utility.namedRerouteUsage"===t.kind&&this.namedRerouteDeclaration(t)}compileOutput(){if("postProcess"===this.graph.target)return this.compilePostProcessOutput();const t=this.graph.outputs?.color,e={color:null!=t?this.toColorOutput(this.resolveOutputBinding(t,"color")):ct("#ffffff",1),transparent:Ee(this.graph)},a=this.graph.outputs?.opacity;null!=a&&(e.opacity=this.expectType(this.resolveOutputBinding(a,"opacity"),["float"],"opacity").value);const r=this.graph.outputs?.roughness;null!=r&&(e.roughness=this.expectType(this.resolveOutputBinding(r,"roughness"),["float"],"roughness").value);const u=this.graph.outputs?.metalness;null!=u&&(e.metalness=this.floatOutput(u,"metalness"));const i=this.graph.outputs?.normal;null!=i&&(e.normal=this.toVec3Output(this.resolveOutputBinding(i,"normal"),"normal"));const o=this.graph.outputs?.emissive;null!=o&&(e.emissive=this.toVec3Output(this.resolveOutputBinding(o,"emissive"),"emissive"));const n=this.graph.outputs?.ambientOcclusion;null!=n&&(e.ambientOcclusion=this.floatOutput(n,"ambientOcclusion"));const s=this.graph.outputs?.ambientOcclusionIntensity;null!=s&&(e.ambientOcclusionIntensity=this.floatOutput(s,"ambientOcclusionIntensity"));const l=this.graph.outputs?.sheenColor;null!=l&&(e.sheenColor=this.toVec3Output(this.resolveOutputBinding(l,"sheenColor"),"sheenColor"));const p=this.graph.outputs?.sheenRoughness;null!=p&&(e.sheenRoughness=this.floatOutput(p,"sheenRoughness"));const c=this.graph.outputs?.anisotropy;null!=c&&(e.anisotropy=this.floatOutput(c,"anisotropy"));const h=this.graph.outputs?.anisotropyDirection;null!=h&&(e.anisotropyDirection=this.expectType(this.resolveOutputBinding(h,"anisotropyDirection"),["vec2"],"anisotropyDirection").value);const v=this.graph.outputs?.bakedLight;null!=v&&(e.bakedLight=this.toVec3Output(this.resolveOutputBinding(v,"bakedLight"),"bakedLight"));const y=this.graph.outputs?.transform;null!=y&&"decal"!==this.graph.target&&(e.transform=this.toMat4Output(this.resolveOutputBinding(y,"transform"),"transform"));const d=this.graph.outputs?.discard;null!=d&&(e.discard=this.expectType(this.resolveOutputBinding(d,"discard"),["boolean"],"discard").value);const m=this.graph.outputs?.transparent;null!=m&&null!=m.value&&(e.transparent=Boolean(m.value));const f=this.graph.outputs?.alphaTest;return null!=f&&null!=f.value?e.alphaTest=Number(f.value):null!=this.graph.materialOptions?.alphaTest&&(e.alphaTest=this.graph.materialOptions.alphaTest),this.applyShadingModel(e)}compilePostProcessOutput(){const t=this.graph.outputs?.color,e=null!=t?this.toColorOutput(this.resolveOutputBinding(t,"color")):ae(Zt),a=this.graph.outputs?.opacity;return Ne(e,null!=a?this.expectType(this.resolveOutputBinding(a,"opacity"),["float"],"opacity").value:void 0)}applyShadingModel(t){const e=null!=t.opacity?function(t,e){if(t instanceof h)return ct(kt(t,t,t),e);if(t instanceof x)return ct(kt(t.x,t.y,0),e);if(t instanceof S)return ct(t.rgb,t.w.multiply(e));return ct(t,e)}(t.color,t.opacity):t.color;switch(this.graph.shadingModel??"standard"){case"standard":{const a={color:$e(e),roughness:t.roughness??R(.5),...null!=t.metalness?{metalness:t.metalness}:{},..."decal"===this.graph.target?{normal:st(fe(Ut.normalMatrix).multiplyVec(Te))}:{},...null!=t.normal?{normal:t.normal}:{},...null!=t.emissive?{emissive:ke(t.emissive)}:{},...null!=t.ambientOcclusion?{ambientOcclusion:t.ambientOcclusion}:{},...null!=t.ambientOcclusionIntensity?{ambientOcclusionIntensity:t.ambientOcclusionIntensity}:{},...null!=t.sheenColor?{sheenColor:t.sheenColor.rgb}:{},...null!=t.sheenRoughness?{sheenRoughness:t.sheenRoughness}:{},...null!=t.anisotropy?{anisotropy:t.anisotropy}:{},...null!=t.anisotropyDirection?{anisotropyDirection:t.anisotropyDirection}:{},...null!=t.bakedLight?{bakedLight:t.bakedLight}:{}};return{...t,color:bt(a)}}case"toon":{const a={color:$e(e),...null!=t.normal?{normal:t.normal}:{},...null!=t.emissive?{emissive:ke(t.emissive)}:{},...null!=t.ambientOcclusion?{ambientOcclusion:t.ambientOcclusion}:{},...null!=t.ambientOcclusionIntensity?{ambientOcclusionIntensity:t.ambientOcclusionIntensity}:{},...null!=t.bakedLight?{bakedLight:t.bakedLight}:{}};return{...t,color:Ot(a)}}case"unlit":return{...t,color:e}}}compileNode(t){const e=this.compiled.get(t);if(null!=e)return e;const a=this.nodesById.get(t);if(null==a)throw new ShaderGraphCompileError(`Missing shader graph node "${t}"`,t);if(null==ye[a.kind])throw new ShaderGraphCompileError(`Unsupported shader graph node "${a.kind}"`,a.id);if(this.compiling.has(t))throw new ShaderGraphCompileError(`Shader graph contains a cycle involving node "${t}"`,t);this.compiling.add(t);try{const e=this.compileNodeInternal(a);return this.compiled.set(t,e),e}finally{this.compiling.delete(t)}}compileNodeInternal(t){switch(t.kind){case"input.parameter":return this.output(t,this.compileParameterNode(t));case"input.slider":return this.output(t,{value:{type:"float",value:R(Fe(Ae(t,"value",.5)))}});case"constant.boolean":return this.output(t,{value:{type:"boolean",value:U(Boolean(ze(t,"value",!1)))}});case"constant.float":return this.output(t,{value:{type:"float",value:R(Ae(t,"value",0))}});case"constant.vec2":return this.output(t,{value:{type:"vec2",value:He(ze(t,"value",[0,0]))}});case"constant.vec3":return this.output(t,{value:{type:"vec3",value:Ye(ze(t,"value",[0,0,0]))}});case"constant.rgba":return this.output(t,{value:{type:"rgba",value:Je(ze(t,"value","#ffffff"),Ae(t,"alpha",1))}});case"builtin.uv":{const e=this.uvAttribute(t);return this.output(t,{value:{type:"vec2",value:!0===ze(t,"flipY",!1)?$t(e.x,jt(e.y)):e},u:{type:"float",value:e.x},v:{type:"float",value:e.y}})}case"builtin.screenUv":return this.output(t,{value:{type:"vec2",value:Zt}});case"builtin.color":{const e=E(M.color);return this.output(t,{value:{type:"rgba",value:e},rgb:{type:"rgb",value:e.rgb},r:{type:"float",value:e.r},g:{type:"float",value:e.g},b:{type:"float",value:e.b},a:{type:"float",value:e.a}})}case"builtin.position":{const e=E(M.position);return this.output(t,{value:{type:"vec3",value:e},x:{type:"float",value:e.x},y:{type:"float",value:e.y},z:{type:"float",value:e.z}})}case"builtin.worldPosition":{const e=E(Nt.worldPosition);return this.output(t,{value:{type:"vec3",value:e},x:{type:"float",value:e.x},y:{type:"float",value:e.y},z:{type:"float",value:e.z}})}case"builtin.objectPosition":{let e=Ut.instanceMatrix.multiplyVec(zt(0,0,0,1));e=J("USE_BATCHING",e,t=>W.multiplyVec(t));const a=E(Ut.modelMatrix.multiplyVec(e).xyz);return this.output(t,{value:{type:"vec3",value:a},x:{type:"float",value:a.x},y:{type:"float",value:a.y},z:{type:"float",value:a.z}})}case"builtin.viewDir":{const e=E(Nt.viewDir);return this.output(t,{value:{type:"vec3",value:e},x:{type:"float",value:e.x},y:{type:"float",value:e.y},z:{type:"float",value:e.z}})}case"builtin.normal":{const e=E(M.normal);return this.output(t,{value:{type:"vec3",value:e},x:{type:"float",value:e.x},y:{type:"float",value:e.y},z:{type:"float",value:e.z}})}case"builtin.time":return this.output(t,{value:{type:"float",value:It.elapsed}});case"builtin.camera":{const e=Gt(te),a=Gt(ee),r=Ut.cameraPosition;return this.output(t,{position:{type:"vec3",value:r},x:{type:"float",value:r.x},y:{type:"float",value:r.y},z:{type:"float",value:r.z},near:{type:"float",value:e},far:{type:"float",value:a}})}case"builtin.viewMatrix":return this.output(t,{value:{type:"mat4",value:Ut.viewMatrix}});case"builtin.projectionMatrix":return this.output(t,{value:{type:"mat4",value:Ut.projectionMatrix}});case"builtin.modelViewMatrix":return this.output(t,{value:{type:"mat4",value:Ut.modelViewMatrix}});case"builtin.particle":return this.output(t,{energy:{type:"float",value:ot.energy},color:{type:"rgb",value:ot.color},opacity:{type:"float",value:ot.opacity},time:{type:"float",value:ot.time}});case"math.add":case"math.subtract":case"math.multiply":case"math.divide":return this.output(t,{value:this.compileBinaryMath(t)});case"math.oneMinus":{const e=this.expectNumeric(this.input(t,"value"));return this.output(t,{value:{type:e.type,value:jt(e.value)}})}case"math.abs":return this.output(t,{value:this.compileUnaryMath(t,T)});case"math.sign":return this.output(t,{value:this.compileUnaryMath(t,dt)});case"math.floor":return this.output(t,{value:this.compileUnaryMath(t,q)});case"math.ceil":return this.output(t,{value:this.compileUnaryMath(t,B)});case"math.fract":return this.output(t,{value:this.compileUnaryMath(t,F)});case"math.sin":return this.output(t,{value:this.compileUnaryMath(t,mt)});case"math.cos":return this.output(t,{value:this.compileUnaryMath(t,z)});case"math.tan":return this.output(t,{value:this.compileUnaryMath(t,St)});case"math.asin":return this.output(t,{value:this.compileUnaryMath(t,O)});case"math.acos":return this.output(t,{value:this.compileUnaryMath(t,I)});case"math.atan":return this.output(t,{value:this.compileUnaryMath(t,N)});case"math.atan2":return this.output(t,{value:this.compileAtan2(t)});case"math.radians":return this.output(t,{value:this.compileUnaryMath(t,pt)});case"math.degrees":return this.output(t,{value:this.compileUnaryMath(t,P)});case"math.sqrt":return this.output(t,{value:this.compileUnaryMath(t,gt)});case"math.inverseSqrt":return this.output(t,{value:this.compileUnaryMath(t,_)});case"math.exp":return this.output(t,{value:this.compileUnaryMath(t,D)});case"math.exp2":return this.output(t,{value:this.compileUnaryMath(t,L)});case"math.log":return this.output(t,{value:this.compileUnaryMath(t,Z)});case"math.log2":return this.output(t,{value:this.compileUnaryMath(t,tt)});case"math.saturate":return this.output(t,{value:this.compileUnaryMath(t,vt)});case"math.length":{const e=this.expectNumeric(this.input(t,"value"));return this.output(t,{value:{type:"float",value:Q(e.value)}})}case"math.normalize":return this.output(t,{value:this.compileUnaryMath(t,st)});case"math.clamp":{const e=this.expectNumeric(this.input(t,"value")),a=this.expectNumeric(this.input(t,"min",{type:"float",value:R(0)})),r=this.expectNumeric(this.input(t,"max",{type:"float",value:R(1)}));return this.output(t,{value:{type:e.type,value:$(e.value,a.value,r.value)}})}case"math.min":return this.output(t,{value:this.compileBinaryFunction(t,rt)});case"math.max":return this.output(t,{value:this.compileBinaryFunction(t,at)});case"math.pow":return this.output(t,{value:this.compileBinaryFunction(t,lt)});case"math.mod":return this.output(t,{value:this.compileBinaryFunction(t,nt)});case"math.step":return this.output(t,{value:this.compileBinaryFunction(t,wt,!0)});case"math.smoothstep":{const e=this.expectNumeric(this.input(t,"edge0",{type:"float",value:R(0)})),a=this.expectNumeric(this.input(t,"edge1",{type:"float",value:R(1)})),r=this.expectNumeric(this.input(t,"value"));return this.output(t,{value:{type:r.type,value:ft(e.value,a.value,r.value)}})}case"math.mix":{const e=this.expectNumeric(this.input(t,"a")),a=this.expectNumeric(this.input(t,"b")),r=this.expectNumeric(this.input(t,"t",{type:"float",value:R(.5)})),u=De(e.type,a.type,t.id);return this.output(t,{value:{type:u,value:ut(e.value,a.value,r.value)}})}case"math.remap":{const e=this.expectType(this.input(t,"value"),["float"],t.id,"value").value,a=this.floatInput(t,"inMin",-1),r=this.floatInput(t,"inMax",1),u=this.floatInput(t,"outMin",0),i=this.floatInput(t,"outMax",1);return this.output(t,{value:{type:"float",value:Lt(e,a,r,u,i)}})}case"math.select":{const e=this.boolInput(t,"condition",!1),a=this.expectNumeric(this.input(t,"a")),r=this.expectNumeric(this.input(t,"b")),u=De(a.type,r.type,t.id);return this.output(t,{value:{type:u,value:yt(e,a.value,r.value)}})}case"math.reflect":{const e=this.expectType(this.input(t,"incident"),["vec2","vec3","vec4","rgb","rgba"],t.id,"incident"),a=this.expectType(this.input(t,"normal"),["vec2","vec3","vec4","rgb","rgba"],t.id,"normal");return this.output(t,{value:{type:e.type,value:ge(e.value,a.value)}})}case"math.refract":{const e=this.expectType(this.input(t,"incident"),["vec2","vec3","vec4","rgb","rgba"],t.id,"incident"),a=this.expectType(this.input(t,"normal"),["vec2","vec3","vec4","rgb","rgba"],t.id,"normal"),r=this.floatInput(t,"ior",1.5);return this.output(t,{value:{type:e.type,value:be(e.value,a.value,r)}})}case"math.derivative":{const e=this.expectNumeric(this.input(t,"value")),a=String(ze(t,"mode","fwidth"));let r;return r="dFdx"===a?xe(e.value):"dFdy"===a?we(e.value):Se(e.value),this.output(t,{value:{type:e.type,value:r}})}case"layer.mixPainted":return this.output(t,{value:this.compilePaintedLayerMix(t)});case"math.dot":{const e=this.expectNumeric(this.input(t,"a")),a=this.expectNumeric(this.input(t,"b"));return De(e.type,a.type,t.id),this.output(t,{value:{type:"float",value:j(e.value,a.value)}})}case"math.distance":{const e=this.expectNumeric(this.input(t,"a")),a=this.expectNumeric(this.input(t,"b"));return De(e.type,a.type,t.id),this.output(t,{value:{type:"float",value:V(e.value,a.value)}})}case"math.cross":{const e=this.expectType(this.input(t,"a"),["vec3","rgb"],t.id,"a"),a=this.expectType(this.input(t,"b"),["vec3","rgb"],t.id,"b");return this.output(t,{value:{type:"vec3",value:A(e.value,a.value)}})}case"compare.greaterThan":case"compare.greaterThanOrEqual":case"compare.lessThan":case"compare.lessThanOrEqual":case"compare.equal":case"compare.notEqual":return this.output(t,{value:{type:"boolean",value:this.compileComparison(t)}});case"boolean.not":{const e=this.boolInput(t,"value",!1);return this.output(t,{value:{type:"boolean",value:yt(e,U(!1),U(!0))}})}case"boolean.and":return this.output(t,{value:{type:"boolean",value:this.compileBooleanBinary(t,"and")}});case"boolean.or":return this.output(t,{value:{type:"boolean",value:this.compileBooleanBinary(t,"or")}});case"compose.vec2":return this.output(t,{value:{type:"vec2",value:$t(this.floatInput(t,"x",0),this.floatInput(t,"y",0))}});case"compose.vec3":return this.output(t,{value:{type:"vec3",value:kt(this.floatInput(t,"x",0),this.floatInput(t,"y",0),this.floatInput(t,"z",0))}});case"compose.vec4":return this.output(t,{value:{type:"vec4",value:zt(this.floatInput(t,"x",0),this.floatInput(t,"y",0),this.floatInput(t,"z",0),this.floatInput(t,"w",0))}});case"compose.rgba":return this.output(t,{value:{type:"rgba",value:zt(this.floatInput(t,"r",1),this.floatInput(t,"g",1),this.floatInput(t,"b",1),this.floatInput(t,"a",1))}});case"decompose.component":{const e=this.expectNumeric(this.input(t,"value")),a=String(ze(t,"component","x"));return this.output(t,{value:{type:"float",value:Re(e,a,t.id)}})}case"decompose.split":{const e=this.expectNumeric(this.input(t,"value"));return this.output(t,function(t,e){const a={},r=(r,u)=>{a[r]={type:"float",value:Re(t,u,e)}};switch(t.type){case"vec2":return r("x","x"),r("y","y"),r("u","x"),r("v","y"),a;case"vec3":return r("x","x"),r("y","y"),r("z","z"),a;case"rgb":return r("r","r"),r("g","g"),r("b","b"),r("x","x"),r("y","y"),r("z","z"),a;case"vec4":return r("x","x"),r("y","y"),r("z","z"),r("w","w"),a;case"rgba":return r("r","r"),r("g","g"),r("b","b"),r("a","a"),r("x","x"),r("y","y"),r("z","z"),r("w","w"),a;case"float":return a.x={type:"float",value:t.value},a}}(e,t.id))}case"color.gradient":{const e=this.expectType(this.input(t,"t",{type:"float",value:R(.5)}),["float"],t.id,"t"),a=H(function(t){const e=ze(t,"stops",[]);if(!Array.isArray(e))return[];const a=e.map((t,a)=>{if(null==t||"object"!=typeof t||Array.isArray(t))return;const r=t,u="string"==typeof r.color?r.color:"#ffffff";return{position:_e(r.position,Ke(a,e.length)),color:u,alpha:_e(r.alpha,1)}}).filter(t=>null!=t);return a.length>0?a:[]}(t)).sample(e.value);return this.output(t,{value:{type:"rgba",value:a}})}case"utility.curve":{const e=this.expectType(this.input(t,"t",{type:"float",value:R(.5)}),["float"],t.id,"t"),a=ze(t,"curve","Linear"),r=K(a,e.value)??Y(Ie.decode(a)).sample(e.value);return this.output(t,{value:{type:"float",value:r}})}case"utility.namedRerouteDeclaration":return this.output(t,{value:this.input(t,"value")});case"utility.namedRerouteUsage":{const e=this.compileNode(this.namedRerouteDeclaration(t).id);return this.output(t,{value:e.outputs.value})}case"color.hueShift":{const e=this.expectType(this.input(t,"color"),["rgba","rgb","vec4","vec3"],t.id,"color"),a=e.value,r="rgba"===e.type||"vec4"===e.type?a.rgb??a.xyz:a,u=this.floatInput(t,"shift",0),i=Rt(r,u);return"rgba"===e.type||"vec4"===e.type?this.output(t,{value:{type:e.type,value:zt(i,a.a??a.w)}}):this.output(t,{value:{type:e.type,value:i}})}case"color.blend":{const e=this.expectType(this.input(t,"base"),["rgba","rgb","vec4","vec3"],t.id,"base"),a=this.expectType(this.input(t,"blend"),["rgba","rgb","vec4","vec3"],t.id,"blend"),r=String(ze(t,"mode","multiply")),u=e.value,i=a.value,o="rgba"===e.type||"vec4"===e.type?u.rgb??u.xyz:u,n="rgba"===a.type||"vec4"===a.type?i.rgb??i.xyz:i;let s=qt(o,n,r);if("rgba"===a.type||"vec4"===a.type){const t=i.a??i.w;s=ut(o,s,t)}if("rgba"===e.type||"vec4"===e.type){const a=u.a??u.w;return this.output(t,{value:{type:e.type,value:zt(s,a)}})}return this.output(t,{value:{type:e.type,value:s}})}case"color.desaturate":{const e=this.expectType(this.input(t,"color"),["rgba","rgb","vec4","vec3"],t.id,"color"),a=e.value,r="rgba"===e.type||"vec4"===e.type?a.rgb??a.xyz:a,u=this.floatInput(t,"fraction",1),i=Ft(r,u);return"rgba"===e.type||"vec4"===e.type?this.output(t,{value:{type:e.type,value:zt(i,a.a??a.w)}}):this.output(t,{value:{type:e.type,value:i}})}case"color.unpackNormal":{const e=this.expectType(this.input(t,"color"),["vec3","vec4","rgb","rgba"],t.id,"color"),a=this.floatInput(t,"scale",1),r=this.optionalInput(t,"normal");return this.output(t,{value:{type:"vec3",value:k(e.value,a,r?this.expectType(r,["vec3"],t.id,"normal").value:void 0)}})}case"texture.sampler2d":{const e=this.input(t,"texture");return this.output(t,{value:this.toSampler(e,t.id)})}case"texture.sample2d":{const e=this.toSampler(this.input(t,"sampler"),t.id),a=this.expectType(this.input(t,"uv",{type:"vec2",value:this.defaultUV()}),["vec2"],t.id,"uv"),r=e.value.sample(a.value);return this.output(t,{rgba:{type:"rgba",value:r},rgb:{type:"rgb",value:r.rgb},r:{type:"float",value:r.r},g:{type:"float",value:r.g},b:{type:"float",value:r.b},a:{type:"float",value:r.a}})}case"texture.triplanarMapping":{const e=this.input(t,"sampler"),a=this.toSampler(e,t.id),r=this.expectType(this.input(t,"scale",{type:"float",value:R(1)}),["float"],t.id,"scale"),u=this.expectType(this.input(t,"normal",{type:"vec3",value:Bt.normal}),["vec3"],t.id,"normal"),i=this.expectType(this.input(t,"position",{type:"vec3",value:Bt.position}),["vec3"],t.id,"position"),o=Ct(a.value,r.value,u.value,i.value);return this.output(t,{rgba:{type:"rgba",value:o},rgb:{type:"rgb",value:o.rgb},r:{type:"float",value:o.r},g:{type:"float",value:o.g},b:{type:"float",value:o.b},a:{type:"float",value:o.a}})}case"scene.sampleColor":{const e=this.expectType(this.input(t,"uv",{type:"vec2",value:Zt}),["vec2"],t.id,"uv"),a=ae(e.value);return this.output(t,{rgba:{type:"rgba",value:a},rgb:{type:"rgb",value:a.rgb},r:{type:"float",value:a.r},g:{type:"float",value:a.g},b:{type:"float",value:a.b},a:{type:"float",value:a.a}})}case"scene.sampleDepth":{const e=this.expectType(this.input(t,"uv",{type:"vec2",value:Zt}),["vec2"],t.id,"uv");return this.output(t,{depth:{type:"float",value:Qt(e.value)},linearEyeDepth:{type:"float",value:Xt(e.value)}})}case"scene.fragmentDepth":return this.output(t,{linearEyeDepth:{type:"float",value:Kt}});case"transform.translate":{const e=this.expectType(this.input(t,"offset",{type:"vec3",value:kt(0,0,0)}),["vec3","rgb"],t.id,"offset");return this.output(t,{value:{type:"mat4",value:Mt(e.value)}})}case"transform.scale":{const e=this.expectType(this.input(t,"scale",{type:"vec3",value:kt(1,1,1)}),["vec3","rgb"],t.id,"scale").value;return this.output(t,{value:{type:"mat4",value:xt(e.x,e.y,e.z)}})}case"transform.rotateAxis":{const e=this.expectType(this.input(t,"axis",{type:"vec3",value:kt(0,1,0)}),["vec3","rgb"],t.id,"axis"),a=this.expectType(this.input(t,"angle",{type:"float",value:R(0)}),["float"],t.id,"angle");return this.output(t,{value:{type:"mat4",value:le(e.value,a.value)}})}case"uv.rotate":{const e=this.expectType(this.input(t,"uv",{type:"vec2",value:this.defaultUV()}),["vec2"],t.id,"uv").value,a=this.expectType(this.input(t,"angle",{type:"float",value:R(0)}),["float"],t.id,"angle").value,r=this.expectType(this.input(t,"center",{type:"vec2",value:$t(.5,.5)}),["vec2"],t.id,"center").value;return this.output(t,{value:{type:"vec2",value:Le(e,a,r)}})}case"uv.twirl":{const e=this.expectType(this.input(t,"uv",{type:"vec2",value:this.defaultUV()}),["vec2"],t.id,"uv").value,a=this.expectType(this.input(t,"strength",{type:"float",value:R(1)}),["float"],t.id,"strength").value,r=this.expectType(this.input(t,"center",{type:"vec2",value:$t(.5,.5)}),["vec2"],t.id,"center").value,u=this.expectType(this.input(t,"offset",{type:"vec2",value:$t(0,0)}),["vec2"],t.id,"offset").value;return this.output(t,{value:{type:"vec2",value:Et(e,a,r,u)}})}case"uv.radial":{const e=this.expectType(this.input(t,"uv",{type:"vec2",value:this.defaultUV()}),["vec2"],t.id,"uv").value,a=Dt(e.subtractScalar(.5));return this.output(t,{coords:{type:"vec2",value:a.coords},radius:{type:"float",value:a.radius},angle:{type:"float",value:a.angle}})}case"uv.bulge":{const e=this.expectType(this.input(t,"uv",{type:"vec2",value:this.defaultUV()}),["vec2"],t.id,"uv").value,a=this.expectType(this.input(t,"center",{type:"vec2",value:$t(.5,.5)}),["vec2"],t.id,"center").value,r=this.expectType(this.input(t,"power",{type:"float",value:R(1)}),["float"],t.id,"power").value;return this.output(t,{value:{type:"vec2",value:G(e,a,r)}})}case"uv.flipbook":{const e=this.expectType(this.input(t,"uv",{type:"vec2",value:this.defaultUV()}),["vec2"],t.id,"uv").value,a=this.floatInput(t,"columns",Ae(t,"columns",1)),r=this.floatInput(t,"rows",Ae(t,"rows",1)),u=this.floatInput(t,"fps",Ae(t,"fps",60)),i=this.expectType(this.input(t,"time",{type:"float",value:It.elapsed}),["float"],t.id,"time").value,o=String(ze(t,"mode","clamp"));return this.output(t,{value:{type:"vec2",value:pe(e,a,r,i,u,o)}})}case"uv.pom":{const e=this.expectType(this.input(t,"uv",{type:"vec2",value:this.defaultUV()}),["vec2"],t.id,"uv").value,a=this.input(t,"heightMap"),r=this.toSampler(a,t.id).value,u=this.floatInput(t,"heightScale",.05),i=Ae(t,"minLayers",8),o=Ae(t,"maxLayers",24);return this.output(t,{value:{type:"vec2",value:ce(e,r,u,i,o)}})}case"shape.rectangle":{const e=this.expectType(this.input(t,"uv",{type:"vec2",value:this.defaultUV()}),["vec2"],t.id,"uv").value;return this.output(t,{value:{type:"float",value:_t(e,this.floatInput(t,"width",.5),this.floatInput(t,"height",.5))}})}case"shape.roundedRectangle":{const e=this.expectType(this.input(t,"uv",{type:"vec2",value:this.defaultUV()}),["vec2"],t.id,"uv").value;return this.output(t,{value:{type:"float",value:Wt(e,this.floatInput(t,"width",.5),this.floatInput(t,"height",.5),this.floatInput(t,"radius",.1))}})}case"noise.simplex":{const e=this.expectType(this.input(t,"uv",{type:"vec2",value:this.defaultUV()}),["vec2"],t.id,"uv").value,a=this.floatInput(t,"scale",8);return this.output(t,{value:{type:"float",value:new ne(e.multiplyScalar(a))}})}case"noise.voronoi2d":{const e=this.expectType(this.input(t,"uv",{type:"vec2",value:this.defaultUV()}),["vec2"],t.id,"uv").value,a=this.floatInput(t,"scale",8);return this.output(t,{value:{type:"float",value:new se(e.multiplyScalar(a))}})}case"effect.fresnel":return this.output(t,{value:{type:"float",value:E(Yt(this.floatInput(t,"power",1)))}});case"effect.edgeDepth":{const e=this.floatInput(t,"power",1);return this.output(t,{value:{type:"float",value:Ht(e)}})}case"effect.depthFade":{const e=this.floatInput(t,"distance",1);return this.output(t,{value:{type:"float",value:Jt(e)}})}case"util.panner":{const e=this.expectType(this.input(t,"uv",{type:"vec2",value:this.defaultUV()}),["vec2"],t.id,"coord"),a=this.expectType(this.input(t,"speed",{type:"vec2",value:$t(0,0)}),["vec2"],t.id,"speed"),r=this.expectType(this.input(t,"tile",{type:"vec2",value:$t(1,1)}),["vec2"],t.id,"tile"),u=this.expectType(this.input(t,"time",{type:"float",value:It.elapsed}),["float"],t.id,"time");return this.output(t,{value:{type:"vec2",value:e.value.multiply(r.value).add(a.value.multiplyScalar(u.value))}})}}}compileParameterNode(t){const e=String(ze(t,"parameter","")),a=this.graph.parameters?.find(t=>t.name===e||t.id===e);if(null==a)throw new ShaderGraphCompileError(`Parameter "${e}" does not exist`,t.id);const r=de(a.type),u={type:r,value:Ve(this.options.parameters?.[a.name]??a.defaultValue,r,a.name)};if("sampler2d"!==r)return{value:u};const i=this.expectType(this.input(t,"uv",{type:"vec2",value:this.defaultUV()}),["vec2"],t.id,"uv"),o=u.value.sample(i.value);return{value:{type:"rgba",value:o},rgb:{type:"rgb",value:o.rgb},r:{type:"float",value:o.r},g:{type:"float",value:o.g},b:{type:"float",value:o.b},a:{type:"float",value:o.a},sampler:u}}compileBinaryMath(t){const e=this.expectBinaryMathValue(this.input(t,"a")),a=this.expectBinaryMathValue(this.input(t,"b")),r=t.kind.split(".")[1];if("mat4"===e.type||"mat4"===a.type)return function(t,e,a,r){if("mat4"===t.type&&"mat4"===e.type&&"divide"!==a)return{type:"mat4",value:je(t.value,e.value,a,r)};if("mat4"===t.type&&"float"===e.type&&("multiply"===a||"divide"===a))return{type:"mat4",value:je(t.value,e.value,a,r)};if("float"===t.type&&"mat4"===e.type&&"multiply"===a)return{type:"mat4",value:je(e.value,t.value,a,r)};if("mat4"===t.type&&"vec4"===e.type&&"multiply"===a)return{type:"vec4",value:t.value.multiplyVec(e.value)};throw new ShaderGraphCompileError(`Cannot ${a} ${t.type} and ${e.type}`,r)}(e,a,r,t.id);if(e.type===a.type)return{type:e.type,value:je(e.value,a.value,r,t.id)};if("float"===e.type&&"float"!==a.type&&"multiply"===r)return{type:a.type,value:je(a.value,e.value,r,t.id)};if("float"!==e.type&&"float"===a.type)return{type:e.type,value:je(e.value,a.value,r,t.id)};throw new ShaderGraphCompileError(`Cannot ${r} ${e.type} and ${a.type}`,t.id)}expectBinaryMathValue(t){if(!qe(t.type)&&"mat4"!==t.type)throw new ShaderGraphCompileError(`Expected a numeric value but got ${t.type}`);return t}compileUnaryMath(t,e){const a=this.expectNumeric(this.input(t,"value"));return{type:a.type,value:e(a.value)}}compileAtan2(t){const e=this.expectNumeric(this.input(t,"y")),a=this.expectNumeric(this.input(t,"x"));return{type:function(t,e,a){if(t===e)return t;if("rgb"===t&&"vec3"===e||"vec3"===t&&"rgb"===e)return"vec3";if("rgba"===t&&"vec4"===e||"vec4"===t&&"rgba"===e)return"vec4";throw new ShaderGraphCompileError(`Cannot mix ${t} and ${e}`,a)}(e.type,a.type,t.id),value:C(e.value,a.value)}}compileBinaryFunction(t,e,a=!1){const r=this.expectNumeric(this.input(t,"a")),u=this.expectNumeric(this.input(t,"b"));return{type:a&&"float"===u.type?r.type:De(r.type,u.type,t.id),value:e(r.value,u.value)}}compilePaintedLayerMix(t){const e=this.expectNumeric(this.input(t,"base")),a=[e.value],r=[At(0)];let u=e.type;for(let e=1;e<=8;e++){const i=this.optionalInput(t,`layer${e}`);if(null==i)continue;const o=this.expectNumeric(i);u=De(u,o.type,t.id),a.push(o.value),r.push(At(e))}return 1===a.length?e:{type:u,value:it({layerColors:a,layerWeights:r,enableNoise:Boolean(ze(t,"enableNoise",!0)),noiseScale:this.floatInput(t,"noiseScale",Ae(t,"noiseScale",.1)),noiseAmount:this.floatInput(t,"noiseAmount",Ae(t,"noiseAmount",.5)),decay:this.floatInput(t,"softness",Ae(t,"softness",.3)),mode:"hard"===String(ze(t,"mode","soft"))?X.hard:X.soft})}}defaultUV(){return"trail"===this.graph.target?oe:"decal"===this.graph.target?ue:E(M.uv)}uvAttribute(t){const e=Math.max(0,Math.floor(Ae(t,"channel",0)));return 0===e?Bt.uv:fe(me(`uv${e}`))}namedRerouteDeclaration(t){const e=String(t.params?.declarationId??"");if(0===e.length)throw new ShaderGraphCompileError("Named reroute usage is missing a declaration",t.id);const a=this.nodesById.get(e);if(null==a)throw new ShaderGraphCompileError(`Named reroute declaration "${e}" does not exist`,t.id);if("utility.namedRerouteDeclaration"!==a.kind)throw new ShaderGraphCompileError(`Node "${e}" is not a named reroute declaration`,t.id);return a}input(t,e,a){const r=t.inputs?.[e];if(null!=r)return this.resolveInput(r,t.id,e);const u=(this.graph.edges??[]).find(a=>a.to.nodeId===t.id&&a.to.port===e);if(null!=u)return this.resolveSocket(u.from,t.id,e);if(null!=a)return a;const i=this.inlineLiteralFallback(t,e);if(null!=i)return i;throw new ShaderGraphCompileError(`Missing input "${e}"`,t.id,e)}optionalInput(t,e){const a=t.inputs?.[e];if(null!=a)return this.resolveInput(a,t.id,e);const r=(this.graph.edges??[]).find(a=>a.to.nodeId===t.id&&a.to.port===e);return null!=r?this.resolveSocket(r.from,t.id,e):void 0}floatInput(t,e,a){return this.expectType(this.input(t,e,{type:"float",value:R(a)}),["float"],t.id,e).value}boolInput(t,e,a){return this.expectType(this.input(t,e,{type:"boolean",value:U(a)}),["boolean"],t.id,e).value}compileComparison(t){const e=this.expectType(this.input(t,"a"),["float"],t.id,"a").value,a=this.expectType(this.input(t,"b"),["float"],t.id,"b").value;switch(t.kind){case"compare.greaterThan":return e.gt(a);case"compare.greaterThanOrEqual":return e.gte(a);case"compare.lessThan":return e.lt(a);case"compare.lessThanOrEqual":return e.lte(a);case"compare.equal":return e.equals(a);case"compare.notEqual":return e.notEquals(a)}throw new ShaderGraphCompileError(`Unsupported comparison node "${t.kind}"`,t.id)}compileBooleanBinary(t,e){const a=this.boolInput(t,"a",!1),r=this.boolInput(t,"b",!1);return"and"===e?a.and(r):a.or(r)}resolveOutputBinding(t,e){if(null!=t.input)return this.resolveSocket(t.input,void 0,e);if(void 0!==t.value)return Pe(t.value);throw new ShaderGraphCompileError(`Output "${e}" is missing an input or value`)}floatOutput(t,e){return this.expectType(this.resolveOutputBinding(t,e),["float"],e).value}resolveInput(t,e,a){return function(t){return null!=t.nodeId}(t)?this.resolveSocket(t,e,a):Pe(t.value,t.valueType)}inlineLiteralFallback(t,e){const a=ye[t.kind]?.inputs.find(t=>t.name===e);if(null==a)return;const r=ve(t.kind,a),u=he(t.kind,a);return null!=r&&void 0!==u?Pe(u,r):void 0}resolveSocket(t,e,a){const r=this.compileNode(t.nodeId).outputs[t.port];if(null==r)throw new ShaderGraphCompileError(`Node "${t.nodeId}" does not have output "${t.port}"`,e??t.nodeId,a??t.port);return r}expectType(t,e,a,r){if(!e.includes(t.type))throw new ShaderGraphCompileError(`Expected ${e.join(" or ")} but got ${t.type}`,a,r);return t}expectNumeric(t){if(!qe(t.type))throw new ShaderGraphCompileError(`Expected a numeric value but got ${t.type}`);return t}toSampler(t,e){if("sampler2d"===t.type)return t;if("texture2d"===t.type){const a=t.value;if(null==a)throw new ShaderGraphCompileError("Texture input is empty",e);return{type:"sampler2d",value:Tt(a)}}throw new ShaderGraphCompileError(`Expected texture2d or sampler2d but got ${t.type}`,e)}toColorOutput(t){switch(t.type){case"float":case"vec2":case"vec3":case"vec4":case"rgb":case"rgba":return t.value;case"boolean":{const e=yt(t.value,R(1),R(0));return ct(kt(e,e,e),1)}case"mat4":throw new ShaderGraphCompileError("mat4 values cannot be used as color");case"texture2d":case"sampler2d":throw new ShaderGraphCompileError(`${t.type} must be sampled before it can be used as color`)}}toVec3Output(t,e){if("vec3"===t.type||"rgb"===t.type)return t.value;if("vec4"===t.type||"rgba"===t.type)return t.value.xyz;throw new ShaderGraphCompileError(`Output "${e}" expects vec3/rgb but got ${t.type}`)}toMat4Output(t,e){if("mat4"===t.type)return t.value;throw new ShaderGraphCompileError(`Output "${e}" expects mat4 but got ${t.type}`)}output(t,e){return{node:t,outputs:e}}}export function compileShaderGraph(t,e={}){return new ShaderGraphCompiler(t,e).compile()}export function compileShaderGraphPreview(t,e,a={}){if(null==e)return compileShaderGraph(t,a).output;const r=new ShaderGraphCompiler(t,a).compileNode(e),u=Object.values(r.outputs)[0];if(null==u)throw new ShaderGraphCompileError(`Node "${e}" has no previewable output`,e);return{color:Ue(u),transparent:"rgba"===u.type||"vec4"===u.type}}export function buildShaderGraphMaterial(t,a={}){if("postProcess"===t.target)return buildShaderGraphPostProcessMaterial(t,a);const r=function(t,e,a){const r=a.alphaTest??t.alphaTest??e.materialOptions?.alphaTest;return null!=r?{...t,alphaTest:r}:t}(null!=a.previewNodeId?compileShaderGraphPreview(t,a.previewNodeId,a):compileShaderGraph(t,a).output,t,a);if("sprite"===t.target)return new Pt({color:r.color,discard:r.discard,alphaTest:r.alphaTest,transparent:r.transparent??!0,position:Vt(new b("rotation",0),new b("screenSpaceSize",0)),uniforms:{color:{value:new e(16777215)}}});if("trail"===t.target){const{position:e}=ie.getTrailShaderNodes(a.trailBillboard??!1),u=new y({...r,position:e});return ie.applyTrailShaderParameters(u),Ge(u,t),u}if("decal"===t.target){const e=null!=r.discard?re.or(r.discard):re,a=new y({...r,transparent:!1,discard:e});return a.userData.isDecal=!0,Ge(a,t),a}const u=new y({transparent:!1,...r});return Ge(u,t),u}export function buildShaderGraphPostProcessMaterial(t,e={}){const a=null!=e.previewNodeId?Ne(compileShaderGraphPreview(t,e.previewNodeId,e).color):compileShaderGraph(t,e).output,r=new y({color:a.color,transparent:!1,fog:!1,lights:!1,outputEncoding:!1});return r.depthWrite=!1,r.depthTest=!1,r.toneMapped=!1,r.userData.isPostProcessShaderGraph=!0,r}export function buildShaderGraphInlinePreviewMaterial(t,e,a={}){const r=compileShaderGraphPreview({...t,target:"surface"},e,a),u=new y({color:r.color,transparent:r.transparent??!1,fog:!1,lights:!1});return u.depthWrite=!1,u.depthTest=!1,u.toneMapped=!1,u}function Ne(t,e){const a=ae(Zt),r=Gt("hology_post_process_weight",1),u=vt(null!=e?r.multiply(e):r);return{color:ut(a.rgb,Ce(t),u),transparent:!1,...null!=e?{opacity:e}:{}}}function Ce(t){return t instanceof h?kt(t,t,t):t instanceof x?kt(t.x,t.y,0):t instanceof S?t.xyz:t}export function shaderGraphMaterialSideToThree(e){switch(e){case"back":return t;case"double":return r;default:return u}}function Ee(t){return"decal"!==t.target&&(t.materialOptions?.transparent??"sprite"===t.target)}function Ge(t,e){"surface"===e.target&&(t.side=shaderGraphMaterialSideToThree(e.materialOptions?.side)),null!=e.materialOptions?.transparent&&(t.transparent=e.materialOptions.transparent),null!=e.materialOptions?.bloom&&(t.userData.hasBloom=e.materialOptions.bloom)}function Ue(t){switch(t.type){case"float":return Be(t.value);case"vec2":{const e=t.value;return ct(kt(e.x,e.y,0),1)}case"vec3":case"rgb":return ct(t.value,1);case"vec4":case"rgba":return t.value;case"boolean":return Be(yt(t.value,R(1),R(0)));case"mat4":throw new ShaderGraphCompileError("mat4 values cannot be previewed as color");case"texture2d":case"sampler2d":throw new ShaderGraphCompileError(`${t.type} nodes require a sampled output to preview`)}}function Be(t){return ct(kt(t,t,t),1)}function $e(t){return t instanceof h?kt(t,t,t).rgb:t instanceof x?kt(t.x,t.y,0).rgb:t}function ke(t){return t.rgb}function ze(t,e,a){const r=t.params?.[e];return void 0===r?a:r}function Ae(t,e,a){const r=ze(t,e,a);return"number"==typeof r?r:Number(r??a)}function Pe(t,e){if(null!=(a=t)&&"object"==typeof a&&!Array.isArray(a)&&"value"in a)return Pe(t.value,t.valueType??e);var a;const r=e??function(t){if("boolean"==typeof t)return"boolean";if("number"==typeof t)return"float";if(Array.isArray(t))return 2===t.length?"vec2":3===t.length?"vec3":16===t.length?"mat4":"vec4";return"string"==typeof t?"rgb":"float"}(t);return{type:r,value:Ve(t,r,"literal")}}function Ve(t,a,r){switch(a){case"boolean":return t instanceof c?t:U(Boolean(t));case"float":return t instanceof h?t:R(_e(t,0));case"vec2":return t instanceof x?t:He(t);case"vec3":return t instanceof w?t:Ye(t);case"vec4":return t instanceof S?t:function(t){const[e,a,r,u]=We(t,4,1);return zt(e,a,r,u)}(t);case"mat4":return t instanceof v?t:function(t){if(t instanceof i)return et(t);const e=new i;Array.isArray(t)&&16===t.length&&e.fromArray(t.map(t=>_e(t,0)));return et(e)}(t);case"rgb":return t instanceof d?t:function(t){if("string"==typeof t||"number"==typeof t||t instanceof e)return ht(t);const[a,r,u]=We(t,3);return kt(a,r,u).rgb}(t);case"rgba":return t instanceof m||t instanceof S?t:Je(t);case"texture2d":return t;case"sampler2d":return function(t){const e=t;return t instanceof f||t instanceof g||"function"==typeof e?.isSampler2D||!0===e?.isSampler2D}(t)?t:t instanceof n||!0===t?.isTexture?Tt(t):(Me.has(r)||(Me.add(r),console.warn(`Shader graph parameter "${r}" is missing a texture value; using a 1x1 white fallback texture.`)),Tt(Oe))}}function je(t,e,a,r){const u=`${a}Scalar`;if(e instanceof h&&"function"==typeof t[u])return t[u](e);if("function"==typeof t[a])return t[a](e);throw new ShaderGraphCompileError(`Node does not support ${a}`,r)}function De(t,e,a){if(t===e)return t;if("rgb"===t&&"vec3"===e||"vec3"===t&&"rgb"===e)return"vec3";if("rgba"===t&&"vec4"===e||"vec4"===t&&"rgba"===e)return"vec4";if("float"===t&&qe(e))return e;if("float"===e&&qe(t))return t;throw new ShaderGraphCompileError(`Cannot mix ${t} and ${e}`,a)}function Le(t,e,a){const r=t.subtract(a),u=mt(e),i=z(e);return $t(i.multiply(r.x).subtract(u.multiply(r.y)).add(a.x),u.multiply(r.x).add(i.multiply(r.y)).add(a.y))}function Re(t,e,a){const r=t.value["u"===e?"x":"v"===e?"y":e];if(r instanceof h)return r;throw new ShaderGraphCompileError(`Component "${e}" is not available on ${t.type}`,a)}function qe(t){return"float"===t||"vec2"===t||"vec3"===t||"vec4"===t||"rgb"===t||"rgba"===t}function Fe(t){return Number.isFinite(t)?Math.min(1,Math.max(0,t)):0}function _e(t,e){if("number"==typeof t)return t;if("string"==typeof t){const a=parseFloat(t);return Number.isFinite(a)?a:e}return"boolean"==typeof t?t?1:0:e}function We(t,a,r=0){if(Array.isArray(t))return Array.from({length:a},(e,a)=>_e(t[a],r));if(t instanceof s||t instanceof l||t instanceof p)return Array.from({length:a},(e,a)=>t.toArray()[a]??r);if(t instanceof e){const e=t.toArray();return Array.from({length:a},(t,a)=>e[a]??(3===a?1:r))}if(null!=t&&"object"==typeof t){const e=t,u=["x","y","z","w"];if(u.some(t=>null!=e[t]))return Array.from({length:a},(t,a)=>_e(e[u[a]],r));const i=["r","g","b","a"];if(i.some(t=>null!=e[t]))return Array.from({length:a},(t,a)=>_e(e[i[a]],3===a?1:r))}return Array.from({length:a},()=>_e(t,r))}function He(t){const[e,a]=We(t,2);return $t(e,a)}function Ye(t){const[e,a,r]=We(t,3);return kt(e,a,r)}function Je(t,a=1){if("string"==typeof t||"number"==typeof t||t instanceof e)return ct(t,a);if(t instanceof w)return ct(t,a);const[r,u,i,o]=We(t,4,a);return zt(r,u,i,o)}function Ke(t,e){return e<=1?0:t/(e-1)}/*
|
|
2
2
|
* Copyright (©) 2026 Hology Interactive AB. All rights reserved.
|
|
3
3
|
* See the LICENSE.md file for details.
|
|
4
4
|
*/
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import"reflect-metadata";import{afterEach as e,expect as t,test as o,vi as n}from"vitest";n.hoisted(()=>{if("undefined"==typeof HTMLCanvasElement)return;const e=new Proxy({},{get:(e,t)=>(t in e||(e[t]=("string"!=typeof t||!t.startsWith("is"))&&n.fn()),e[t]),set:(e,t,o)=>(e[t]=o,!0)});Object.defineProperty(HTMLCanvasElement.prototype,"getContext",{configurable:!0,value:()=>e})}),n.mock("../gameplay/services/render.js",()=>({ViewController:class{}})),n.mock("../gameplay/services/physics/physics-system.js",()=>({PhysicsSystem:class{},RayTestResult:class{},ShapeCastResult:class{}})),n.mock("../gameplay/services/world.js",()=>({World:class{}})),n.mock("@hology/nebula",()=>({ease:{easeInOutCubic:e=>e}}));import{Subject as a}from"rxjs";import{Container as
|
|
1
|
+
import"reflect-metadata";import{afterEach as e,expect as t,test as o,vi as n}from"vitest";n.hoisted(()=>{if("undefined"==typeof HTMLCanvasElement)return;const e=new Proxy({},{get:(e,t)=>(t in e||(e[t]=("string"!=typeof t||!t.startsWith("is"))&&n.fn()),e[t]),set:(e,t,o)=>(e[t]=o,!0)});Object.defineProperty(HTMLCanvasElement.prototype,"getContext",{configurable:!0,value:()=>e})}),n.mock("../gameplay/services/render.js",()=>({ViewController:class{}})),n.mock("../gameplay/services/physics/physics-system.js",()=>({PhysicsSystem:class{},RayTestResult:class{},ShapeCastResult:class{}})),n.mock("../gameplay/services/world.js",()=>({World:class{}})),n.mock("@hology/nebula",()=>({ease:{easeInOutCubic:e=>e}}));import{Subject as a}from"rxjs";import{Container as s}from"typedi";import{Object3D as r,PerspectiveCamera as i,Ray as c,Scene as l,Vector3 as p}from"three";import{BaseActor as m}from"../gameplay/actors/actor.js";import{FirstPersonCameraComponent as d}from"../gameplay/actors/camera/first-person-camera-component.js";import{ThirdPersonCameraComponent as u}from"../gameplay/actors/camera/third-person-camera-component.js";import{PhysicsSystem as h}from"../gameplay/services/physics/physics-system.js";import{ViewController as y}from"../gameplay/services/render.js";import{World as f}from"../gameplay/services/world.js";function w(e=new C){const t=v(),o=new d;return o.actor=e,{camera:o,actor:e,element:t}}function v(){const e=document.createElement("div"),t=document.createElement("canvas");return e.appendChild(t),Object.defineProperty(e,"clientWidth",{configurable:!0,value:1280}),Object.defineProperty(e,"clientHeight",{configurable:!0,value:720}),s.set(y,{htmlElement:e,setCamera:n.fn(),onUpdate:()=>new a,onLateUpdate:()=>new a}),s.set(f,{scene:new l}),s.set(h,{sphereCast:n.fn()}),e}function B(){Object.defineProperty(document.body,"requestPointerLock",{configurable:!0,value:n.fn()}),Object.defineProperty(document,"pointerLockElement",{configurable:!0,value:null})}function g(e,t){return e.mock.calls.filter(e=>e[0]===t).length}e(()=>{n.restoreAllMocks(),s.reset()}),o("first-person camera clamps pitch input",()=>{const{camera:e}=w();e.rotationInput.rotateX(Math.PI),t(e.rotationInput.rotation.x).toBeCloseTo(Math.PI/2-.01),e.rotationInput.rotateX(2*-Math.PI),t(e.rotationInput.rotation.x).toBeCloseTo(-Math.PI/2+.01)}),o("first-person camera follows actor eye offset",async()=>{const e=new C;e.position.set(1,2,3),e.rotation.y=Math.PI/2,e.object.updateMatrixWorld(!0);const{camera:o}=w(e);o.eyeHeight=1.5,o.offsetZ=.25,o.autoActivate=!1,await o.onInit(),o.updateCameraTransform(),t(o.camera.position.x).toBeCloseTo(1.25),t(o.camera.position.y).toBeCloseTo(3.5),t(o.camera.position.z).toBeCloseTo(3)}),o("first-person aim helpers write to caller-provided objects",async()=>{const e=new C;e.position.set(0,1,0);const{camera:o}=w(e);o.autoActivate=!1,await o.onInit();const n=new p,a=new p,s=new c;t(o.getAimOrigin(n)).toBe(n),t(o.getAimDirection(a)).toBe(a),t(o.getAimRay(s)).toBe(s),t(s.origin).toEqual(n),t(s.direction).toEqual(a),t(a.x).toBeCloseTo(0),t(a.y).toBeCloseTo(0),t(a.z).toBeCloseTo(1),t(a.length()).toBeCloseTo(1)}),o("first-person camera pitch input tilts aim downward when positive",async()=>{const{camera:e}=w();e.autoActivate=!1,await e.onInit(),e.rotationInput.rotateX(.25),e.updateCameraTransform();const o=e.getAimDirection(new p);t(o.y).toBeLessThan(0),t(o.z).toBeGreaterThan(0),t(o.length()).toBeCloseTo(1)}),o("first-person hidden objects restore their previous visibility",()=>{const{camera:e}=w(),o=new r,n=new r;n.visible=!1,e.hideObjects(o,n),t(o.visible).toBe(!1),t(n.visible).toBe(!1),e.restoreHiddenObjects(),t(o.visible).toBe(!0),t(n.visible).toBe(!1)}),o("first-person activation is idempotent",async()=>{const{camera:e,element:o}=w();e.autoActivate=!1,await e.onInit(),B();const a=n.spyOn(o,"addEventListener"),s=n.spyOn(o,"removeEventListener"),r=n.spyOn(document,"addEventListener"),i=n.spyOn(document,"removeEventListener");e.activate(),e.activate(),t(g(a,"pointerdown")).toBe(1),t(g(a,"keydown")).toBe(1),t(g(r,"pointerlockchange")).toBe(1),e.deactivate(),e.deactivate(),t(g(s,"pointerdown")).toBe(1),t(g(s,"keydown")).toBe(1),t(g(i,"pointerlockchange")).toBe(1)}),o("third-person activation remains idempotent",async()=>{const e=new C,o=v(),a=new u;a.actor=e,a.autoActivate=!1,await a.onInit(),B();const s=n.spyOn(o,"addEventListener"),r=n.spyOn(o,"removeEventListener"),i=n.spyOn(document,"addEventListener"),c=n.spyOn(document,"removeEventListener");a.activate(),a.activate(),t(g(s,"pointerdown")).toBe(1),t(g(s,"keydown")).toBe(1),t(g(i,"pointerlockchange")).toBe(1),a.deactivate(),a.deactivate(),t(g(r,"pointerdown")).toBe(1),t(g(r,"keydown")).toBe(1),t(g(c,"pointerlockchange")).toBe(1)}),o("third-person camera hides the cursor only after pointer lock is active",async()=>{const e=new C,o=v(),a=o.querySelector("canvas"),s=new u;s.actor=e,s.autoActivate=!1,await s.onInit(),B();const r=n.fn(()=>Promise.resolve());Object.defineProperty(a,"requestPointerLock",{configurable:!0,value:r});let i=null;Object.defineProperty(document,"pointerLockElement",{configurable:!0,get:()=>i}),s.activate(),s.hideCursor({ignoreRecentUnlock:!0}),t(r).toHaveBeenCalledWith(),t(s.isMouseLocked).toBe(!1),t(o.style.cursor).not.toBe("none"),i=a,document.dispatchEvent(new Event("pointerlockchange")),t(s.isMouseLocked).toBe(!0),t(o.style.cursor).toBe("none")}),o("third-person camera does not treat sustained fast movement as a teleport",async()=>{const e=new C;v();const o=new u;o.actor=e,o.autoActivate=!0,o.collision=!1,o.fixedBehind=!1,o.smoothCamera=!0,o.smoothSpeed=10,await o.onInit(),o.onLateUpdate(1/60);for(let t=0;t<12;t++)e.position.x+=.2,o.onLateUpdate(1/60);t(e.position.x-o.camera.position.x).toBeGreaterThan(.7)}),o("third-person camera still snaps after a target teleport",async()=>{const e=new C;v();const o=new u;o.actor=e,o.autoActivate=!0,o.collision=!1,o.fixedBehind=!1,o.smoothCamera=!0,o.teleportSnapDistance=5,await o.onInit(),o.onLateUpdate(1/60),e.position.x=10,o.onLateUpdate(1/60),t(o.camera.position.x).toBeCloseTo(10)}),o("third-person collision casts along the current frame camera arm",async()=>{const e=new C;v();const o=s.get(h);n.mocked(o.sphereCast).mockReturnValue({hasHit:!1});const a=new u;a.actor=e,a.autoActivate=!0,a.fixedBehind=!1,await a.onInit(),a.rotationInput.rotation.y=Math.PI/2,a.onLateUpdate(1/60);const[,,r,i]=n.mocked(o.sphereCast).mock.calls[0],c=Math.sqrt(a.distance**2+(a.offsetY-a.height)**2);t(r.x).toBeCloseTo(-a.distance/c),t(r.y).toBeCloseTo((a.offsetY-a.height)/c),t(i).toBeCloseTo(c)}),o("third-person collision preserves the safe camera arm and recovers smoothly",async()=>{const e=new C;v();const o=s.get(h);n.mocked(o.sphereCast).mockReturnValueOnce({hasHit:!0,distance:4}).mockReturnValue({hasHit:!1});const a=new u;a.actor=e,a.autoActivate=!0,a.fixedBehind=!1,await a.onInit();const r=new p(0,a.height,0);a.onLateUpdate(1/60),t(a.camera.position.distanceTo(r)).toBeCloseTo(3.95),a.onLateUpdate(.1);const i=a.camera.position.distanceTo(r);t(i).toBeGreaterThan(3.95),t(i).toBeLessThan(Math.sqrt(a.distance**2+(a.offsetY-a.height)**2))}),o("third-person camera blends temporary control over the gameplay pose",async()=>{const e=new C;v();const o=new u;o.actor=e,o.autoActivate=!0,o.collision=!1,o.fixedBehind=!1,o.smoothCamera=!1,await o.onInit(),o.onLateUpdate(1/60);const n=o.camera.position.clone(),a=new p(10,4,2),s=new p(1,2,3);o.setTemporaryControl(a,s,{blendInDuration:1}),o.onLateUpdate(.5),t(o.camera.position.x).toBeCloseTo((n.x+a.x)/2),t(o.camera.position.y).toBeCloseTo((n.y+a.y)/2),o.onLateUpdate(.5),t(o.camera.position).toEqual(a)}),o("third-person camera blends temporary control back to the current gameplay pose",async()=>{const e=new C;v();const o=new u;o.actor=e,o.autoActivate=!0,o.collision=!1,o.fixedBehind=!1,o.smoothCamera=!1,await o.onInit();const n=new p(10,4,2);o.setTemporaryControl(n,new p(1,2,3),{blendInDuration:0}),o.onLateUpdate(1/60),e.position.x=2,o.clearTemporaryControl(1),o.onLateUpdate(.5);const a=new p(2,o.offsetY,-o.distance);t(o.camera.position.x).toBeCloseTo((n.x+a.x)/2),t(o.camera.position.y).toBeCloseTo((n.y+a.y)/2)}),o("third-person camera applies collision to temporary control",async()=>{const e=new C;v();const o=s.get(h);n.mocked(o.sphereCast).mockReturnValue({hasHit:!0,distance:2});const a=new u;a.actor=e,a.autoActivate=!0,a.collision=!0,a.fixedBehind=!1,a.smoothCamera=!1,await a.onInit(),a.setTemporaryControl(new p(0,0,10),new p,{blendInDuration:0}),a.onLateUpdate(1/60),t(a.camera.position.z).toBeCloseTo(1.95)}),o("third-person camera pans toward a temporary look-at target without repositioning",async()=>{const e=new C;v();const o=new u;o.actor=e,o.autoActivate=!0,o.collision=!1,o.fixedBehind=!1,o.smoothCamera=!1,await o.onInit(),o.onLateUpdate(1/60);const n=o.camera.position.clone(),a=o.camera.quaternion.clone(),s=new p(0,2,-20),r=new i;r.position.copy(n),r.lookAt(s);const c=a.angleTo(r.quaternion);o.setTemporaryLookAt(s,{blendInDuration:1}),o.onLateUpdate(.5),t(o.camera.position).toEqual(n),t(a.angleTo(o.camera.quaternion)).toBeCloseTo(c/2)}),o("third-person camera blends a temporary field of view and restores it",async()=>{const e=new C;v();const o=new u;o.actor=e,o.autoActivate=!0,o.collision=!1,await o.onInit();const n=o.camera.fov;o.setTemporaryLookAt(new p(0,2,10),{blendInDuration:1,blendOutDuration:1,fieldOfView:35}),o.onLateUpdate(1),t(o.camera.fov).toBe(35),o.clearTemporaryControl(),o.onLateUpdate(.5),t(o.camera.fov).toBeCloseTo((35+n)/2),o.onLateUpdate(.5),t(o.camera.fov).toBe(n)});class C extends m{constructor(){super(),this.__isInitialised=!0}}/*
|
|
2
2
|
* Copyright (©) 2026 Hology Interactive AB. All rights reserved.
|
|
3
3
|
* See the LICENSE.md file for details.
|
|
4
4
|
*/
|
package/dist/test/input.test.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{expect as e,test as t,vi as a}from"vitest";import{ActionInput as
|
|
1
|
+
import{expect as e,test as t,vi as a}from"vitest";import o from"typedi";import{ActionInput as n,ButtonInput as s,ToggleInput as p}from"../gameplay/input/input.js";a.mock("../gameplay/services/render.js",()=>({ViewController:class{}}));import{InputService as i}from"../gameplay/input/input-service.js";import{GamepadButtonBind as l,Keybind as d}from"../gameplay/input/keybind.js";import{ViewController as u}from"../gameplay/services/render.js";t("button input uses hold mode by default",()=>{const t=new s,a=[];t.onStart(()=>a.push(t.activated)),t.onEnd(()=>a.push(t.activated)),t.toggle(!0),e(t.activated).toBe(!0),t.toggle(!0),e(a).toEqual([!0]),t.toggle(!1),e(t.activated).toBe(!1),e(a).toEqual([!0,!1])}),t("button input can switch to toggle mode at runtime",()=>{const t=new s,a=[];t.onStart(()=>a.push(t.activated)),t.onEnd(()=>a.push(t.activated)),t.mode="toggle",t.toggle(!0),e(t.activated).toBe(!1),t.toggle(!1),e(t.activated).toBe(!0),t.toggle(!1),e(t.activated).toBe(!1),e(a).toEqual([!0,!1])}),t("action and toggle inputs remain compatibility presets",()=>{const t=new n;t.toggle(!0),e(t.activated).toBe(!0),t.toggle(!1),e(t.activated).toBe(!1);const a=new p;a.toggle(!0),e(a.activated).toBe(!1),a.toggle(!1),e(a.activated).toBe(!0)}),t("held gamepad buttons can be suppressed until released",()=>{const t={pressed:!0,value:1},a={connected:!0,mapping:"standard",buttons:[t],axes:[]},o=function(){const e=Object.create(i.prototype);return Object.assign(e,{view:{paused:!1},inputs:new Map,deltaCallbacks:new Map,toggleCallbacks:new Map,keybinds:new Map,gamepadButtonBinds:new Map,gamepadAxisBinds:new Map,activeToggleCallbacks:[],prevGamepadButtonStates:new Map,suppressedGamepadButtons:new Map,lastActiveDevice:"kbm",onDeviceChanged:{next:()=>{}}}),e}(),n=[];o.gamepadButtonBinds.set("jump",[new l(0)]),o.toggleCallbacks.set("jump",e=>n.push(e)),Object.defineProperty(navigator,"getGamepads",{configurable:!0,value:()=>[a]}),o.suppressHeldGamepadButtonsUntilRelease(),o.update(0),t.pressed=!1,t.value=0,o.update(0),t.pressed=!0,t.value=1,o.update(0),e(n).toEqual([!0])}),t("mouse button gameplay input requires pointer lock",()=>{let t=null;Object.defineProperty(document,"pointerLockElement",{configurable:!0,get:()=>t}),o.set(u,{paused:!1,htmlElement:document.body});const a=new i,n=[];a.keybinds.set("attack",[new d("MouseLeft")]),a.toggleCallbacks.set("attack",e=>n.push(e)),a.onKeyDown({button:0}),t=document.body,a.onKeyDown({button:0}),e(n).toEqual([!0])});/*
|
|
2
2
|
* Copyright (©) 2026 Hology Interactive AB. All rights reserved.
|
|
3
3
|
* See the LICENSE.md file for details.
|
|
4
4
|
*/
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{expect as e,test as t,vi as o}from"vitest";import{DoubleSide as a,FrontSide as r,Texture as n}from"three";o.hoisted(()=>{if("undefined"==typeof HTMLCanvasElement)return;const e=new Proxy({},{get:(e,t)=>(t in e||(e[t]=("string"!=typeof t||!t.startsWith("is"))&&o.fn()),e[t]),set:(e,t,o)=>(e[t]=o,!0)});Object.defineProperty(HTMLCanvasElement.prototype,"getContext",{configurable:!0,value:()=>e}),"undefined"==typeof AudioBuffer&&Object.defineProperty(globalThis,"AudioBuffer",{configurable:!0,value:class{}})}),o.mock("three/examples/jsm/Addons.js",async e=>({...await e(),ConvexHull:class{},SkeletonUtils:{clone:e=>e},RectAreaLightHelper:class{}})),o.mock("three-shader-graph",async e=>{const t=await e();return{...t,rgb:(e,o,a)=>null==o&&null==a&&!0===e?.isColor&&"function"==typeof e.getHexString?t.rgb("#"+e.getHexString()):t.rgb(e,o,a)}}),o.mock("../rendering.js",()=>({RenderingView:class{constructor(){this._id="test",this.csm={setupMaterial:o.fn()}}getEnvTexture(e){return e}},setRenderingPaused:o.fn()}));import{materialFromAsset as s,prepareCustomParamsFromShaderGraph as d,prepareShaderGraphParameters as u}from"../scene/materializer.js";import{SerializedParamType as p}from"../scene/model.js";import{AssetsProvider as i}from"../scene/assets-provider.js";import{AssetResourceLoader as l}from"../scene/asset-resource-loader.js";import{buildShaderGraphMaterial as c,buildShaderGraphPostProcessMaterial as m,compileShaderGraph as v,compileShaderGraphPreview as f,createDefaultPostProcessShaderGraphDocument as h,createDefaultShaderGraphDocument as y,createDefaultVfxShaderGraphDocument as g,shaderGraphPostProcessWeightUniformName as I,shaderGraphParametersToPropertyParameters as b}from"../shader/graph/index.js";import{NodeShaderMaterial as x,textureSampler2d as k,UniformFloatNode as B,UniformSampler2dArraySlice as T}from"../shader-nodes/index.js";import{StandardShader as w}from"../shader/builtin/standard-shader.js";import{sceneMapUniformName as C}from"../shader-nodes/scene-sample.js";function j(e,t,o){return{version:1,target:"surface",parameters:[],nodes:e,edges:t,outputs:o}}function M(e,t){return{id:e,name:e,type:"material",material:{type:"shaderGraph",side:r,params:{},shaderParams:{},shaderGraph:t}}}t("graph validation rejects missing nodes, invalid ports, and incompatible edges",()=>{const t=y();t.outputs.color={input:{nodeId:"missing",port:"value"}},e(()=>v(t)).toThrow(/Missing shader graph node/);const o=y();o.outputs.color={input:{nodeId:"base-color",port:"missing"}},e(()=>v(o)).toThrow(/does not have output/);const a=j([{id:"a",kind:"constant.vec2",params:{value:[1,1]}},{id:"b",kind:"constant.vec3",params:{value:[1,1,1]}},{id:"add",kind:"math.add"}],[{from:{nodeId:"a",port:"value"},to:{nodeId:"add",port:"a"}},{from:{nodeId:"b",port:"value"},to:{nodeId:"add",port:"b"}}],{color:{input:{nodeId:"add",port:"value"}}});e(()=>v(a)).toThrow(/Cannot add vec2 and vec3/)}),t("named reroutes pass values through declarations to multiple usages",()=>{const t=j([{id:"source",kind:"constant.float",params:{value:.75}},{id:"declaration",kind:"utility.namedRerouteDeclaration",params:{name:"Mask"}},{id:"usage-a",kind:"utility.namedRerouteUsage",params:{declarationId:"declaration"}},{id:"usage-b",kind:"utility.namedRerouteUsage",params:{declarationId:"declaration"}},{id:"add",kind:"math.add"}],[{from:{nodeId:"source",port:"value"},to:{nodeId:"declaration",port:"value"}},{from:{nodeId:"usage-a",port:"value"},to:{nodeId:"add",port:"a"}},{from:{nodeId:"usage-b",port:"value"},to:{nodeId:"add",port:"b"}}],{color:{input:{nodeId:"add",port:"value"}}}),o=v(t),a=o.nodes.get("declaration")?.outputs.value;e(a?.type).toBe("float"),e(o.nodes.get("usage-a")?.outputs.value).toBe(a),e(o.nodes.get("usage-b")?.outputs.value).toBe(a),e(o.output.color).toBeTruthy()}),t("named reroutes preserve dynamic vector types",()=>{const t=j([{id:"source",kind:"constant.vec3",params:{value:[1,2,3]}},{id:"declaration",kind:"utility.namedRerouteDeclaration",params:{name:"Direction"}},{id:"usage",kind:"utility.namedRerouteUsage",params:{declarationId:"declaration"}}],[{from:{nodeId:"source",port:"value"},to:{nodeId:"declaration",port:"value"}}],{color:{input:{nodeId:"usage",port:"value"}}}),o=v(t);e(o.nodes.get("usage")?.outputs.value.type).toBe("vec3")}),t("named reroute validation rejects missing and invalid declarations",()=>{const t=j([{id:"usage",kind:"utility.namedRerouteUsage",params:{declarationId:"missing"}}],[],{});e(()=>v(t)).toThrow(/declaration "missing" does not exist/);const o=j([{id:"not-declaration",kind:"constant.float",params:{value:1}},{id:"usage",kind:"utility.namedRerouteUsage",params:{declarationId:"not-declaration"}}],[],{});e(()=>v(o)).toThrow(/not a named reroute declaration/)}),t("named reroute cycles report a compile error",()=>{const t=j([{id:"declaration",kind:"utility.namedRerouteDeclaration",params:{name:"Cycle"}},{id:"usage",kind:"utility.namedRerouteUsage",params:{declarationId:"declaration"}}],[{from:{nodeId:"usage",port:"value"},to:{nodeId:"declaration",port:"value"}}],{color:{input:{nodeId:"usage",port:"value"}}});e(()=>v(t)).toThrow(/contains a cycle/)}),t("surface graph compiles to NodeShaderOutput",()=>{const t=v(y("surface"));e(t.output.color).toBeTruthy(),e(t.output.transparent).toBe(!1)}),t("inline literal inputs compile without separate constant nodes",()=>{const t=j([{id:"uv",kind:"builtin.uv"},{id:"scale-uv",kind:"math.multiply",inputs:{b:{value:2,valueType:"float"}}},{id:"rotate",kind:"uv.rotate",inputs:{angle:{value:.5,valueType:"float"},center:{value:[.25,.75],valueType:"vec2"}}},{id:"translate",kind:"transform.translate",inputs:{offset:{value:[0,1,0],valueType:"vec3"}}}],[{from:{nodeId:"uv",port:"value"},to:{nodeId:"scale-uv",port:"a"}},{from:{nodeId:"scale-uv",port:"value"},to:{nodeId:"rotate",port:"uv"}}],{color:{input:{nodeId:"rotate",port:"value"}},transform:{input:{nodeId:"translate",port:"value"}}}),o=v(t);e(o.nodes.get("scale-uv")?.outputs.value.type).toBe("vec2"),e(o.nodes.get("rotate")?.outputs.value.type).toBe("vec2"),e(o.nodes.get("translate")?.outputs.value.type).toBe("mat4"),e(o.output.color).toBeTruthy(),e(o.output.transform).toBeTruthy()}),t("sprite graph compiles with sprite-compatible material output",()=>{const t=y("sprite"),o=v(t);e(o.output.color).toBeTruthy(),e(o.output.transparent).toBe(!0)}),t("default VFX shader graphs compile mapped targets and build non-sprite materials",()=>{for(const t of["sprite","surface","decal","trail"]){const o=g(t);if(e(v(o).output.color).toBeTruthy(),"sprite"===t)continue;const a=c(o,{trailBillboard:"trail"===t});e(a.isMaterial).toBe(!0)}}),t("graph material options apply to supported material targets",()=>{const t=y("surface");t.materialOptions={side:"double",transparent:!0};const o=c(t);e(o.side).toBe(a),e(o.transparent).toBe(!0);const r=y("decal");r.materialOptions={transparent:!0};const n=c(r);e(n.transparent).toBe(!0)}),t("standard graph compiles extended material outputs and vertex transform",()=>{const t=j([{id:"metalness",kind:"constant.float",params:{value:.8}},{id:"ao",kind:"constant.float",params:{value:.5}},{id:"sheen",kind:"constant.vec3",params:{value:[.1,.2,.3]}},{id:"offset",kind:"constant.vec3",params:{value:[0,1,0]}},{id:"translate",kind:"transform.translate"}],[{from:{nodeId:"offset",port:"value"},to:{nodeId:"translate",port:"offset"}}],{metalness:{input:{nodeId:"metalness",port:"value"}},ambientOcclusion:{input:{nodeId:"ao",port:"value"}},sheenColor:{input:{nodeId:"sheen",port:"value"}},transform:{input:{nodeId:"translate",port:"value"}}}),o=v(t);e(o.output.metalness).toBeTruthy(),e(o.output.ambientOcclusion).toBeTruthy(),e(o.output.sheenColor).toBeTruthy(),e(o.output.transform).toBeTruthy();const a=c(t);e(a.outputMetalness).toBeTruthy(),e(a.outputAmbientOcclusion).toBeTruthy(),e(a.outputTransform).toBeTruthy()}),t("math multiply composes transform matrices",()=>{const t=j([{id:"translate",kind:"transform.translate",inputs:{offset:{value:[0,1,0],valueType:"vec3"}}},{id:"scale",kind:"transform.scale",inputs:{scale:{value:[2,2,2],valueType:"vec3"}}},{id:"compose",kind:"math.multiply"}],[{from:{nodeId:"translate",port:"value"},to:{nodeId:"compose",port:"a"}},{from:{nodeId:"scale",port:"value"},to:{nodeId:"compose",port:"b"}}],{transform:{input:{nodeId:"compose",port:"value"}}}),o=v(t);e(o.nodes.get("compose")?.outputs.value.type).toBe("mat4"),e(o.output.transform).toBeTruthy(),e(c(t).outputTransform).toBeTruthy()}),t("matrix input nodes compile as mat4 uniforms",()=>{for(const t of["builtin.viewMatrix","builtin.projectionMatrix","builtin.modelViewMatrix"]){const o=j([{id:"matrix",kind:t}],[],{transform:{input:{nodeId:"matrix",port:"value"}}}),a=v(o);e(a.nodes.get("matrix")?.outputs.value.type).toBe("mat4"),e(a.output.transform).toBeTruthy()}}),t("compose vec4 node compiles from float components",()=>{const t=j([{id:"compose",kind:"compose.vec4",inputs:{x:{value:.1},y:{value:.2},z:{value:.3},w:{value:1}}}],[],{color:{input:{nodeId:"compose",port:"value"}}}),o=v(t);e(o.nodes.get("compose")?.outputs.value.type).toBe("vec4"),e(o.output.color).toBeTruthy()}),t("atan2 node compiles from y and x inputs",()=>{const t=j([{id:"atan2",kind:"math.atan2",inputs:{y:{value:1},x:{value:0}}}],[],{color:{input:{nodeId:"atan2",port:"value"}}}),o=v(t);e(o.nodes.get("atan2")?.outputs.value.type).toBe("float"),e(o.output.color).toBeTruthy()}),t("built-in attribute nodes compile for fragment and transform outputs",()=>{const t=j([{id:"color",kind:"builtin.color"},{id:"normal",kind:"builtin.normal"},{id:"position",kind:"builtin.position"},{id:"worldPosition",kind:"builtin.worldPosition"},{id:"objectPosition",kind:"builtin.objectPosition"},{id:"translate",kind:"transform.translate"}],[{from:{nodeId:"objectPosition",port:"value"},to:{nodeId:"translate",port:"offset"}}],{color:{input:{nodeId:"color",port:"value"}},normal:{input:{nodeId:"normal",port:"value"}},roughness:{input:{nodeId:"position",port:"x"}},metalness:{input:{nodeId:"worldPosition",port:"x"}},transform:{input:{nodeId:"translate",port:"value"}}}),o=v(t);e(o.nodes.get("color")?.outputs.value.type).toBe("rgba"),e(o.nodes.get("normal")?.outputs.value.type).toBe("vec3"),e(o.nodes.get("position")?.outputs.value.type).toBe("vec3"),e(o.nodes.get("worldPosition")?.outputs.value.type).toBe("vec3"),e(o.nodes.get("objectPosition")?.outputs.value.type).toBe("vec3"),e(o.output.color).toBeTruthy(),e(o.output.normal).toBeTruthy(),e(o.output.transform).toBeTruthy(),e(c(t).outputTransform).toBeTruthy()}),t("painted layer mix node compiles color layers and builds a material",()=>{const t=j([{id:"base",kind:"constant.rgba",params:{value:"#224422",alpha:1}},{id:"grass",kind:"constant.rgba",params:{value:"#55aa33",alpha:1}},{id:"stone",kind:"constant.rgba",params:{value:"#777777",alpha:1}},{id:"mix",kind:"layer.mixPainted",params:{mode:"soft",enableNoise:!0,softness:.25,noiseScale:.2,noiseAmount:.4}}],[{from:{nodeId:"base",port:"value"},to:{nodeId:"mix",port:"base"}},{from:{nodeId:"grass",port:"value"},to:{nodeId:"mix",port:"layer1"}},{from:{nodeId:"stone",port:"value"},to:{nodeId:"mix",port:"layer3"}}],{color:{input:{nodeId:"mix",port:"value"}}}),o=v(t);e(o.nodes.get("mix")?.outputs.value.type).toBe("rgba"),e(o.output.color).toBeTruthy(),e(c(t).isMaterial).toBe(!0)}),t("painted layer mix node compiles sparse float layers in hard and soft modes",()=>{for(const t of["hard","soft"]){const o=j([{id:"base",kind:"constant.float",params:{value:.8}},{id:"mud",kind:"constant.float",params:{value:1}},{id:"wet",kind:"constant.float",params:{value:.25}},{id:"mix",kind:"layer.mixPainted",params:{mode:t,enableNoise:!1,softness:.3,noiseScale:.1,noiseAmount:.5}}],[{from:{nodeId:"base",port:"value"},to:{nodeId:"mix",port:"base"}},{from:{nodeId:"mud",port:"value"},to:{nodeId:"mix",port:"layer2"}},{from:{nodeId:"wet",port:"value"},to:{nodeId:"mix",port:"layer8"}}],{roughness:{input:{nodeId:"mix",port:"value"}}}),a=v(o);e(a.nodes.get("mix")?.outputs.value.type).toBe("float"),e(a.output.roughness).toBeTruthy()}}),t("painted layer mix node rejects incompatible layer types",()=>{const t=j([{id:"base",kind:"constant.vec2",params:{value:[0,1]}},{id:"layer",kind:"constant.vec3",params:{value:[1,0,0]}},{id:"mix",kind:"layer.mixPainted"}],[{from:{nodeId:"base",port:"value"},to:{nodeId:"mix",port:"base"}},{from:{nodeId:"layer",port:"value"},to:{nodeId:"mix",port:"layer1"}}],{color:{input:{nodeId:"mix",port:"value"}}});e(()=>v(t)).toThrow(/Cannot mix vec2 and vec3/)}),t("additional math, uv, shape, noise, and effect nodes compile",()=>{const t=j([{id:"uv",kind:"builtin.uv"},{id:"angle",kind:"constant.float",params:{value:.5}},{id:"scale",kind:"constant.float",params:{value:4}},{id:"half",kind:"constant.float",params:{value:.5}},{id:"axis",kind:"constant.vec3",params:{value:[0,1,0]}},{id:"rotatedUv",kind:"uv.rotate"},{id:"twirl",kind:"uv.twirl"},{id:"bulge",kind:"uv.bulge"},{id:"radial",kind:"uv.radial"},{id:"flipbook",kind:"uv.flipbook"},{id:"shape",kind:"shape.roundedRectangle"},{id:"noise",kind:"noise.simplex"},{id:"voronoi",kind:"noise.voronoi2d"},{id:"fresnel",kind:"effect.fresnel"},{id:"sin",kind:"math.sin"},{id:"pow",kind:"math.pow"},{id:"mix",kind:"math.mix"},{id:"rotateAxis",kind:"transform.rotateAxis"}],[{from:{nodeId:"uv",port:"value"},to:{nodeId:"rotatedUv",port:"uv"}},{from:{nodeId:"angle",port:"value"},to:{nodeId:"rotatedUv",port:"angle"}},{from:{nodeId:"rotatedUv",port:"value"},to:{nodeId:"twirl",port:"uv"}},{from:{nodeId:"scale",port:"value"},to:{nodeId:"twirl",port:"strength"}},{from:{nodeId:"twirl",port:"value"},to:{nodeId:"bulge",port:"uv"}},{from:{nodeId:"half",port:"value"},to:{nodeId:"bulge",port:"power"}},{from:{nodeId:"bulge",port:"value"},to:{nodeId:"radial",port:"uv"}},{from:{nodeId:"twirl",port:"value"},to:{nodeId:"flipbook",port:"uv"}},{from:{nodeId:"twirl",port:"value"},to:{nodeId:"shape",port:"uv"}},{from:{nodeId:"half",port:"value"},to:{nodeId:"shape",port:"width"}},{from:{nodeId:"half",port:"value"},to:{nodeId:"shape",port:"height"}},{from:{nodeId:"half",port:"value"},to:{nodeId:"shape",port:"radius"}},{from:{nodeId:"twirl",port:"value"},to:{nodeId:"noise",port:"uv"}},{from:{nodeId:"scale",port:"value"},to:{nodeId:"noise",port:"scale"}},{from:{nodeId:"twirl",port:"value"},to:{nodeId:"voronoi",port:"uv"}},{from:{nodeId:"scale",port:"value"},to:{nodeId:"voronoi",port:"scale"}},{from:{nodeId:"scale",port:"value"},to:{nodeId:"fresnel",port:"power"}},{from:{nodeId:"radial",port:"angle"},to:{nodeId:"sin",port:"value"}},{from:{nodeId:"noise",port:"value"},to:{nodeId:"pow",port:"a"}},{from:{nodeId:"half",port:"value"},to:{nodeId:"pow",port:"b"}},{from:{nodeId:"shape",port:"value"},to:{nodeId:"mix",port:"a"}},{from:{nodeId:"voronoi",port:"value"},to:{nodeId:"mix",port:"b"}},{from:{nodeId:"fresnel",port:"value"},to:{nodeId:"mix",port:"t"}},{from:{nodeId:"axis",port:"value"},to:{nodeId:"rotateAxis",port:"axis"}},{from:{nodeId:"angle",port:"value"},to:{nodeId:"rotateAxis",port:"angle"}}],{color:{input:{nodeId:"mix",port:"value"}},opacity:{input:{nodeId:"sin",port:"value"}},anisotropyDirection:{input:{nodeId:"flipbook",port:"value"}},transform:{input:{nodeId:"rotateAxis",port:"value"}}}),o=v(t);e(o.nodes.get("mix")?.outputs.value.type).toBe("float"),e(o.nodes.get("twirl")?.outputs.value.type).toBe("vec2"),e(o.nodes.get("bulge")?.outputs.value.type).toBe("vec2"),e(o.nodes.get("radial")?.outputs.coords.type).toBe("vec2"),e(o.nodes.get("flipbook")?.outputs.value.type).toBe("vec2"),e(o.nodes.get("rotateAxis")?.outputs.value.type).toBe("mat4"),e(o.output.color).toBeTruthy(),e(o.output.opacity).toBeTruthy(),e(o.output.transform).toBeTruthy()}),t("slider input node outputs a clamped float value",()=>{const t=j([{id:"slider",kind:"input.slider",params:{value:1.25}}],[],{roughness:{input:{nodeId:"slider",port:"value"}}}),o=v(t);e(o.nodes.get("slider")?.outputs.value.type).toBe("float"),e(o.output.roughness).toBeTruthy()}),t("comparison and boolean graph nodes compile to boolean outputs",()=>{const t=j([{id:"a",kind:"constant.float",params:{value:.75}},{id:"b",kind:"constant.float",params:{value:.5}},{id:"greater",kind:"compare.greaterThan"},{id:"lessEqual",kind:"compare.lessThanOrEqual"},{id:"not",kind:"boolean.not"},{id:"and",kind:"boolean.and"},{id:"or",kind:"boolean.or"}],[{from:{nodeId:"a",port:"value"},to:{nodeId:"greater",port:"a"}},{from:{nodeId:"b",port:"value"},to:{nodeId:"greater",port:"b"}},{from:{nodeId:"a",port:"value"},to:{nodeId:"lessEqual",port:"a"}},{from:{nodeId:"b",port:"value"},to:{nodeId:"lessEqual",port:"b"}},{from:{nodeId:"lessEqual",port:"value"},to:{nodeId:"not",port:"value"}},{from:{nodeId:"greater",port:"value"},to:{nodeId:"and",port:"a"}},{from:{nodeId:"not",port:"value"},to:{nodeId:"and",port:"b"}},{from:{nodeId:"and",port:"value"},to:{nodeId:"or",port:"a"}},{from:{nodeId:"lessEqual",port:"value"},to:{nodeId:"or",port:"b"}}],{discard:{input:{nodeId:"or",port:"value"}}}),o=v(t);e(o.nodes.get("greater")?.outputs.value.type).toBe("boolean"),e(o.nodes.get("lessEqual")?.outputs.value.type).toBe("boolean"),e(o.nodes.get("not")?.outputs.value.type).toBe("boolean"),e(o.nodes.get("and")?.outputs.value.type).toBe("boolean"),e(o.nodes.get("or")?.outputs.value.type).toBe("boolean"),e(o.output.discard).toBeTruthy()}),t("split component node exposes vector and color channels as separate outputs",()=>{const t=j([{id:"vec",kind:"constant.vec3",params:{value:[.2,.4,.6]}},{id:"color",kind:"constant.rgba",params:{value:"#336699",alpha:.75}},{id:"splitVec",kind:"decompose.split"},{id:"splitColor",kind:"decompose.split"},{id:"sum",kind:"math.add"}],[{from:{nodeId:"vec",port:"value"},to:{nodeId:"splitVec",port:"value"}},{from:{nodeId:"color",port:"value"},to:{nodeId:"splitColor",port:"value"}},{from:{nodeId:"splitVec",port:"z"},to:{nodeId:"sum",port:"a"}},{from:{nodeId:"splitColor",port:"a"},to:{nodeId:"sum",port:"b"}}],{roughness:{input:{nodeId:"sum",port:"value"}}}),o=v(t);e(Object.keys(o.nodes.get("splitVec")?.outputs??{})).toEqual(["x","y","z"]),e(Object.keys(o.nodes.get("splitColor")?.outputs??{})).toEqual(["r","g","b","a","x","y","z","w"]),e(o.nodes.get("splitVec")?.outputs.z.type).toBe("float"),e(o.nodes.get("splitColor")?.outputs.a.type).toBe("float"),e(o.output.roughness).toBeTruthy()}),t("color gradient node compiles editable stops to a sampled color",()=>{const t=j([{id:"threshold",kind:"constant.float",params:{value:.75}},{id:"gradient",kind:"color.gradient",params:{stops:[{position:0,color:"#000000",alpha:1},{position:.5,color:"#ff0000",alpha:1},{position:1,color:"#ffffff",alpha:1}]}}],[{from:{nodeId:"threshold",port:"value"},to:{nodeId:"gradient",port:"t"}}],{color:{input:{nodeId:"gradient",port:"value"}}}),o=v(t);e(o.nodes.get("gradient")?.outputs.value.type).toBe("rgba"),e(o.output.color).toBeTruthy(),e(f(t,"gradient").color).toBeTruthy()}),t("curve node compiles editable curve to a sampled float",()=>{const t=j([{id:"t",kind:"constant.float",params:{value:.75}},{id:"curve",kind:"utility.curve",params:{curve:"InOutQuad"}}],[{from:{nodeId:"t",port:"value"},to:{nodeId:"curve",port:"t"}}],{roughness:{input:{nodeId:"curve",port:"value"}}}),o=v(t);e(o.nodes.get("curve")?.outputs.value.type).toBe("float"),e(o.output.roughness).toBeTruthy(),e(f(t,"curve").color).toBeTruthy()}),t("graph parameters convert to property params and custom defaults",()=>{const t=y();t.parameters.push({name:"opacity",type:"float",group:"Surface",defaultValue:.5});const o=b(t);e(o.map(e=>e.name)).toEqual(["baseColor","opacity"]),e(o.every(e=>!0===e.options.optional)).toBe(!0),e(o.map(e=>e.options.group??null)).toEqual([null,"Surface"]);const a=d(t);e(a.baseColor).toMatchObject({type:p.RgbNode,value:"#ffffff",override:!1}),e(a.opacity).toMatchObject({type:p.FloatNode,value:.5,override:!1})}),t("existing shader graph param values keep overriding unless explicitly disabled",()=>{const t=y(),o=d(t,{baseColor:{type:p.RgbNode,value:"#ff0000"}});e(o.baseColor).toMatchObject({type:p.RgbNode,value:"#ff0000",override:!0});const a=d(t,{baseColor:{type:p.RgbNode,value:"#00ff00",override:!1}});e(a.baseColor).toMatchObject({type:p.RgbNode,value:"#ffffff",override:!1})}),t("texture graph parameter defaults serialize as sampler parameters",()=>{const t=j([],[],{});t.parameters.push({name:"map",type:"texture",defaultValue:"texture-asset-id"});const o=d(t);e(o.map).toMatchObject({type:p.Sampler2DNode,value:"texture-asset-id"})}),t("shader graph params keep graph-declared serialized types over previous string params",()=>{const t=y();t.parameters.push({name:"map",type:"texture",defaultValue:null});const o=d(t,{baseColor:{type:p.String,value:"#ff0000"},map:{type:p.String,value:"texture-asset-id"}});e(o.baseColor).toMatchObject({type:p.RgbNode,value:"#ff0000"}),e(o.map).toMatchObject({type:p.Sampler2DNode,value:"texture-asset-id"})}),t("shader graph texture params resolve asset ids before compile",async()=>{const t=j([{id:"texture",kind:"input.parameter",params:{parameter:"map"}}],[],{color:{input:{nodeId:"texture",port:"value"}}});t.parameters.push({name:"map",type:"texture",defaultValue:"texture-asset-id"});const o=await u({},t,new D([{id:"texture-asset-id",name:"Map",type:"texture",fileKey:"map.png",fileFormat:"png"}]),new A);e(o.map).toBeTruthy(),e(typeof o.map).not.toBe("string"),e(v(t,{parameters:o}).output.color).toBeTruthy()}),t("shader graph texture array params expose named slice uniforms",async()=>{const t=j([{id:"texture",kind:"input.parameter",params:{parameter:"map"}}],[],{color:{input:{nodeId:"texture",port:"value"}}});t.parameters.push({name:"map",type:"texture",defaultValue:"texture-array-layer"});const o=await u({},t,new D([{id:"texture-array-layer",name:"Map Layer",type:"texture",fileKey:"map.png",fileFormat:"png",texture:{textureArrayFileKey:"textures/packed.ktx2",textureArrayLayer:5}}]),new O(new n,5)),a=c(t,{parameters:o});e(a.uniforms.pu_map_i?.value).toBe(5),e(a.fragmentShader).toContain("uniform float pu_map_i")}),t("standard shader height maps support texture array slices",()=>{const t=new w;t.heightMap=new T("heightMap",new n,new B("heightMap_i",3,void 0,!1)),t.map=k(new n),t.heightScale=.05;const o=t.build();e(o.fragmentShader).toContain("uniform sampler2DArray heightMap"),e(o.fragmentShader).toContain("uniform float heightMap_i"),e(o.fragmentShader).toContain("texture(heightMap, vec3(uv + vCurrOffset, heightMapLayer)).r")}),t("standard shader supports particle opacity for instanced VFX materials",()=>{const t=(new w).build();e(t.fragmentShader).toContain("IS_PARTICLE"),e(t.fragmentShader).toContain("particleData")}),t("material graph asset reference and local embedded graph both build materials",async()=>{const t={id:"graph-asset",name:"Reusable Graph",type:"shaderGraph",shaderGraph:y()},o=M("referenced",{source:"asset",assetId:t.id}),a=M("local",{source:"local",graph:y()}),r=new D([t,o,a]),n=new l;e((await s(o,null,r,n,[],!1)).isMaterial).toBe(!0),e((await s(a,null,r,n,[],!1)).isMaterial).toBe(!0)}),t("selected-node preview compiles scalar, vector, color, rgba, and boolean outputs",()=>{const t=[{id:"float",kind:"constant.float",params:{value:.25}},{id:"slider",kind:"input.slider",params:{value:.5}},{id:"vec2",kind:"constant.vec2",params:{value:[1,0]}},{id:"rgb",kind:"constant.vec3",params:{value:[1,.5,0]}},{id:"rgba",kind:"constant.rgba",params:{value:"#ff8800",alpha:.75}},{id:"boolean",kind:"constant.boolean",params:{value:!0}}];for(const o of t){const t=j([o],[],{color:{input:{nodeId:o.id,port:"value"}}});e(f(t,o.id).color).toBeTruthy()}}),t("post-process shader graph builds a fullscreen-safe material",()=>{const t=h(),o=m(t);e(o).toBeInstanceOf(x),e(o.depthWrite).toBe(!1),e(o.depthTest).toBe(!1),e(o.toneMapped).toBe(!1),e(o.transparent).toBe(!1),e(o.userData.isPostProcessShaderGraph).toBe(!0),e(o.uniforms[C]).toBeTruthy(),e(o.uniforms[I]).toBeTruthy()}),t("empty post-process shader graph defaults to identity scene color",()=>{const t=h();t.nodes=[],t.edges=[],t.outputs={};const o=v(t),a=m(t);e(o.output.color).toBeTruthy(),e(o.output.transparent).toBe(!1),e(a.uniforms[C]).toBeTruthy(),e(a.uniforms[I]).toBeTruthy()}),t("post-process color and opacity outputs expose blend weight uniform",()=>{const t=j([{id:"color",kind:"constant.rgba",params:{value:"#ff0044",alpha:1}},{id:"opacity",kind:"constant.float",params:{value:.35}}],[],{color:{input:{nodeId:"color",port:"value"}},opacity:{input:{nodeId:"opacity",port:"value"}},roughness:{value:.8}});t.target="postProcess";const o=v(t),a=m(t);e(o.output.color).toBeTruthy(),e(o.output.opacity).toBeTruthy(),e(o.output.roughness).toBeUndefined(),e(a.uniforms[I]).toBeTruthy()}),t("selected-node preview builds through post-process fullscreen path",()=>{const t=j([{id:"float",kind:"constant.float",params:{value:.25}}],[],{color:{input:{nodeId:"float",port:"value"}}});t.target="postProcess";const o=m(t,{previewNodeId:"float"});e(o).toBeInstanceOf(x),e(o.userData.isPostProcessShaderGraph).toBe(!0),e(o.uniforms[C]).toBeTruthy(),e(o.uniforms[I]).toBeTruthy()}),t("texture parameter nodes expose sampled value and channel outputs",()=>{const t=j([{id:"texture",kind:"input.parameter",params:{parameter:"albedo"}}],[],{color:{input:{nodeId:"texture",port:"value"}}});t.parameters.push({name:"albedo",type:"texture",defaultValue:null});const o=v(t,{parameters:{albedo:new n}}),a=o.nodes.get("texture")?.outputs??{};e(Object.keys(a)).toEqual(["value","rgb","r","g","b","a","sampler"]),e(a.value.type).toBe("rgba"),e(a.rgb.type).toBe("rgb"),e(a.r.type).toBe("float"),e(a.g.type).toBe("float"),e(a.b.type).toBe("float"),e(a.a.type).toBe("float"),e(a.sampler.type).toBe("sampler2d"),e(o.output.color).toBeTruthy()}),t("texture parameter nodes accept resolved sampler values",()=>{const t=j([{id:"texture",kind:"input.parameter",params:{parameter:"map"}}],[],{color:{input:{nodeId:"texture",port:"value"}}});t.parameters.push({name:"map",type:"texture",defaultValue:"texture-asset-id"});const o=v(t,{parameters:{map:k(new n)}});e(o.output.color).toBeTruthy()}),t("missing texture parameter values warn and use fallback sampler",()=>{const t=j([{id:"texture",kind:"input.parameter",params:{parameter:"missingTexture"}}],[],{color:{input:{nodeId:"texture",port:"value"}}});t.parameters.push({name:"missingTexture",type:"texture",defaultValue:null});const a=o.spyOn(console,"warn").mockImplementation(()=>{});try{const o=v(t),r=o.nodes.get("texture")?.outputs??{};e(r.sampler.type).toBe("sampler2d"),e(o.output.color).toBeTruthy(),e(a).toHaveBeenCalledWith(e.stringContaining("missingTexture"))}finally{a.mockRestore()}}),t("scene depth graph nodes expose sampled and fragment linear eye depth",()=>{const t=j([{id:"uv",kind:"builtin.screenUv"},{id:"sceneDepth",kind:"scene.sampleDepth"},{id:"fragmentDepth",kind:"scene.fragmentDepth"},{id:"depthFade",kind:"math.subtract"}],[{from:{nodeId:"uv",port:"value"},to:{nodeId:"sceneDepth",port:"uv"}},{from:{nodeId:"sceneDepth",port:"linearEyeDepth"},to:{nodeId:"depthFade",port:"a"}},{from:{nodeId:"fragmentDepth",port:"linearEyeDepth"},to:{nodeId:"depthFade",port:"b"}}],{opacity:{input:{nodeId:"depthFade",port:"value"}}}),o=v(t);e(o.nodes.get("sceneDepth")?.outputs.depth.type).toBe("float"),e(o.nodes.get("sceneDepth")?.outputs.linearEyeDepth.type).toBe("float"),e(o.nodes.get("fragmentDepth")?.outputs.linearEyeDepth.type).toBe("float"),e(o.output.opacity).toBeTruthy()}),t("unpack normal node compiles with various input combinations",()=>{const t=j([{id:"sample",kind:"constant.vec3",params:{value:[.5,.5,1]}},{id:"scale",kind:"constant.float",params:{value:1}},{id:"normal",kind:"builtin.normal"},{id:"toNormal",kind:"color.unpackNormal"}],[{from:{nodeId:"sample",port:"value"},to:{nodeId:"toNormal",port:"color"}},{from:{nodeId:"scale",port:"value"},to:{nodeId:"toNormal",port:"scale"}},{from:{nodeId:"normal",port:"value"},to:{nodeId:"toNormal",port:"normal"}}],{normal:{input:{nodeId:"toNormal",port:"value"}}}),o=v(t);e(o.nodes.get("toNormal")?.outputs.value.type).toBe("vec3"),e(o.output.normal).toBeTruthy()});class D extends i{constructor(e){super(),this.onCreate=null,this.onDelete=null,this.onUpdate=null,this.assets=new Map(e.map(e=>[e.id,e]))}async getAsset(e){return this.assets.get(e)}async getAssets(){return Array.from(this.assets.values())}}class A extends l{async getTexture(){return new n}async getTextureArray(){return{texture:null,layerIndex:null}}}class O extends A{constructor(e,t){super(),this.textureArray=e,this.layerIndex=t}async getTextureArray(){return{texture:this.textureArray,layerIndex:this.layerIndex}}}/*
|
|
1
|
+
import{expect as e,test as t,vi as o}from"vitest";import{DoubleSide as a,FrontSide as r,Texture as n}from"three";o.hoisted(()=>{if("undefined"==typeof HTMLCanvasElement)return;const e=new Proxy({},{get:(e,t)=>(t in e||(e[t]=("string"!=typeof t||!t.startsWith("is"))&&o.fn()),e[t]),set:(e,t,o)=>(e[t]=o,!0)});Object.defineProperty(HTMLCanvasElement.prototype,"getContext",{configurable:!0,value:()=>e}),"undefined"==typeof AudioBuffer&&Object.defineProperty(globalThis,"AudioBuffer",{configurable:!0,value:class{}})}),o.mock("three/examples/jsm/Addons.js",async e=>({...await e(),ConvexHull:class{},SkeletonUtils:{clone:e=>e},RectAreaLightHelper:class{}})),o.mock("three-shader-graph",async e=>{const t=await e();return{...t,rgb:(e,o,a)=>null==o&&null==a&&!0===e?.isColor&&"function"==typeof e.getHexString?t.rgb("#"+e.getHexString()):t.rgb(e,o,a)}}),o.mock("../rendering.js",()=>({RenderingView:class{constructor(){this._id="test",this.csm={setupMaterial:o.fn()}}getEnvTexture(e){return e}},setRenderingPaused:o.fn()}));import{materialFromAsset as s,prepareCustomParamsFromShaderGraph as d,prepareShaderGraphParameters as u}from"../scene/materializer.js";import{SerializedParamType as p}from"../scene/model.js";import{AssetsProvider as i}from"../scene/assets-provider.js";import{AssetResourceLoader as l}from"../scene/asset-resource-loader.js";import{buildShaderGraphMaterial as c,buildShaderGraphPostProcessMaterial as m,compileShaderGraph as v,compileShaderGraphPreview as f,createDefaultPostProcessShaderGraphDocument as h,createDefaultShaderGraphDocument as y,createDefaultVfxShaderGraphDocument as g,shaderGraphPostProcessWeightUniformName as I,shaderGraphParametersToPropertyParameters as b}from"../shader/graph/index.js";import{NodeShaderMaterial as x,textureSampler2d as k,UniformFloatNode as B,UniformSampler2dArraySlice as T}from"../shader-nodes/index.js";import{StandardShader as w}from"../shader/builtin/standard-shader.js";import{sceneMapUniformName as C}from"../shader-nodes/scene-sample.js";function j(e,t,o){return{version:1,target:"surface",parameters:[],nodes:e,edges:t,outputs:o}}function M(e,t){return{id:e,name:e,type:"material",material:{type:"shaderGraph",side:r,params:{},shaderParams:{},shaderGraph:t}}}t("graph validation rejects missing nodes, invalid ports, and incompatible edges",()=>{const t=y();t.outputs.color={input:{nodeId:"missing",port:"value"}},e(()=>v(t)).toThrow(/Missing shader graph node/);const o=y();o.outputs.color={input:{nodeId:"base-color",port:"missing"}},e(()=>v(o)).toThrow(/does not have output/);const a=j([{id:"a",kind:"constant.vec2",params:{value:[1,1]}},{id:"b",kind:"constant.vec3",params:{value:[1,1,1]}},{id:"add",kind:"math.add"}],[{from:{nodeId:"a",port:"value"},to:{nodeId:"add",port:"a"}},{from:{nodeId:"b",port:"value"},to:{nodeId:"add",port:"b"}}],{color:{input:{nodeId:"add",port:"value"}}});e(()=>v(a)).toThrow(/Cannot add vec2 and vec3/)}),t("named reroutes pass values through declarations to multiple usages",()=>{const t=j([{id:"source",kind:"constant.float",params:{value:.75}},{id:"declaration",kind:"utility.namedRerouteDeclaration",params:{name:"Mask"}},{id:"usage-a",kind:"utility.namedRerouteUsage",params:{declarationId:"declaration"}},{id:"usage-b",kind:"utility.namedRerouteUsage",params:{declarationId:"declaration"}},{id:"add",kind:"math.add"}],[{from:{nodeId:"source",port:"value"},to:{nodeId:"declaration",port:"value"}},{from:{nodeId:"usage-a",port:"value"},to:{nodeId:"add",port:"a"}},{from:{nodeId:"usage-b",port:"value"},to:{nodeId:"add",port:"b"}}],{color:{input:{nodeId:"add",port:"value"}}}),o=v(t),a=o.nodes.get("declaration")?.outputs.value;e(a?.type).toBe("float"),e(o.nodes.get("usage-a")?.outputs.value).toBe(a),e(o.nodes.get("usage-b")?.outputs.value).toBe(a),e(o.output.color).toBeTruthy()}),t("named reroutes preserve dynamic vector types",()=>{const t=j([{id:"source",kind:"constant.vec3",params:{value:[1,2,3]}},{id:"declaration",kind:"utility.namedRerouteDeclaration",params:{name:"Direction"}},{id:"usage",kind:"utility.namedRerouteUsage",params:{declarationId:"declaration"}}],[{from:{nodeId:"source",port:"value"},to:{nodeId:"declaration",port:"value"}}],{color:{input:{nodeId:"usage",port:"value"}}}),o=v(t);e(o.nodes.get("usage")?.outputs.value.type).toBe("vec3")}),t("named reroute validation rejects missing and invalid declarations",()=>{const t=j([{id:"usage",kind:"utility.namedRerouteUsage",params:{declarationId:"missing"}}],[],{});e(()=>v(t)).toThrow(/declaration "missing" does not exist/);const o=j([{id:"not-declaration",kind:"constant.float",params:{value:1}},{id:"usage",kind:"utility.namedRerouteUsage",params:{declarationId:"not-declaration"}}],[],{});e(()=>v(o)).toThrow(/not a named reroute declaration/)}),t("named reroute cycles report a compile error",()=>{const t=j([{id:"declaration",kind:"utility.namedRerouteDeclaration",params:{name:"Cycle"}},{id:"usage",kind:"utility.namedRerouteUsage",params:{declarationId:"declaration"}}],[{from:{nodeId:"usage",port:"value"},to:{nodeId:"declaration",port:"value"}}],{color:{input:{nodeId:"usage",port:"value"}}});e(()=>v(t)).toThrow(/contains a cycle/)}),t("surface graph compiles to NodeShaderOutput",()=>{const t=v(y("surface"));e(t.output.color).toBeTruthy(),e(t.output.transparent).toBe(!1)}),t("inline literal inputs compile without separate constant nodes",()=>{const t=j([{id:"uv",kind:"builtin.uv"},{id:"scale-uv",kind:"math.multiply",inputs:{b:{value:2,valueType:"float"}}},{id:"rotate",kind:"uv.rotate",inputs:{angle:{value:.5,valueType:"float"},center:{value:[.25,.75],valueType:"vec2"}}},{id:"translate",kind:"transform.translate",inputs:{offset:{value:[0,1,0],valueType:"vec3"}}}],[{from:{nodeId:"uv",port:"value"},to:{nodeId:"scale-uv",port:"a"}},{from:{nodeId:"scale-uv",port:"value"},to:{nodeId:"rotate",port:"uv"}}],{color:{input:{nodeId:"rotate",port:"value"}},transform:{input:{nodeId:"translate",port:"value"}}}),o=v(t);e(o.nodes.get("scale-uv")?.outputs.value.type).toBe("vec2"),e(o.nodes.get("rotate")?.outputs.value.type).toBe("vec2"),e(o.nodes.get("translate")?.outputs.value.type).toBe("mat4"),e(o.output.color).toBeTruthy(),e(o.output.transform).toBeTruthy()}),t("uv attribute node supports selecting non-default uv channels",()=>{const t=j([{id:"uv",kind:"builtin.uv",params:{channel:2}}],[],{color:{input:{nodeId:"uv",port:"value"}}}),o=v(t);e(o.nodes.get("uv")?.outputs.value.type).toBe("vec2");const a=c(t);e(a.fragmentShader).toContain("uv2")}),t("sprite graph compiles with sprite-compatible material output",()=>{const t=y("sprite"),o=v(t);e(o.output.color).toBeTruthy(),e(o.output.transparent).toBe(!0)}),t("default VFX shader graphs compile mapped targets and build non-sprite materials",()=>{for(const t of["sprite","surface","decal","trail"]){const o=g(t);if(e(v(o).output.color).toBeTruthy(),"sprite"===t)continue;const a=c(o,{trailBillboard:"trail"===t});e(a.isMaterial).toBe(!0)}}),t("graph material options apply to supported material targets",()=>{const t=y("surface");t.materialOptions={side:"double",transparent:!0};const o=c(t);e(o.side).toBe(a),e(o.transparent).toBe(!0);const r=y("decal");r.materialOptions={transparent:!0};const n=c(r);e(n.transparent).toBe(!0)}),t("standard graph compiles extended material outputs and vertex transform",()=>{const t=j([{id:"metalness",kind:"constant.float",params:{value:.8}},{id:"ao",kind:"constant.float",params:{value:.5}},{id:"sheen",kind:"constant.vec3",params:{value:[.1,.2,.3]}},{id:"offset",kind:"constant.vec3",params:{value:[0,1,0]}},{id:"translate",kind:"transform.translate"}],[{from:{nodeId:"offset",port:"value"},to:{nodeId:"translate",port:"offset"}}],{metalness:{input:{nodeId:"metalness",port:"value"}},ambientOcclusion:{input:{nodeId:"ao",port:"value"}},sheenColor:{input:{nodeId:"sheen",port:"value"}},transform:{input:{nodeId:"translate",port:"value"}}}),o=v(t);e(o.output.metalness).toBeTruthy(),e(o.output.ambientOcclusion).toBeTruthy(),e(o.output.sheenColor).toBeTruthy(),e(o.output.transform).toBeTruthy();const a=c(t);e(a.outputMetalness).toBeTruthy(),e(a.outputAmbientOcclusion).toBeTruthy(),e(a.outputTransform).toBeTruthy()}),t("math multiply composes transform matrices",()=>{const t=j([{id:"translate",kind:"transform.translate",inputs:{offset:{value:[0,1,0],valueType:"vec3"}}},{id:"scale",kind:"transform.scale",inputs:{scale:{value:[2,2,2],valueType:"vec3"}}},{id:"compose",kind:"math.multiply"}],[{from:{nodeId:"translate",port:"value"},to:{nodeId:"compose",port:"a"}},{from:{nodeId:"scale",port:"value"},to:{nodeId:"compose",port:"b"}}],{transform:{input:{nodeId:"compose",port:"value"}}}),o=v(t);e(o.nodes.get("compose")?.outputs.value.type).toBe("mat4"),e(o.output.transform).toBeTruthy(),e(c(t).outputTransform).toBeTruthy()}),t("matrix input nodes compile as mat4 uniforms",()=>{for(const t of["builtin.viewMatrix","builtin.projectionMatrix","builtin.modelViewMatrix"]){const o=j([{id:"matrix",kind:t}],[],{transform:{input:{nodeId:"matrix",port:"value"}}}),a=v(o);e(a.nodes.get("matrix")?.outputs.value.type).toBe("mat4"),e(a.output.transform).toBeTruthy()}}),t("compose vec4 node compiles from float components",()=>{const t=j([{id:"compose",kind:"compose.vec4",inputs:{x:{value:.1},y:{value:.2},z:{value:.3},w:{value:1}}}],[],{color:{input:{nodeId:"compose",port:"value"}}}),o=v(t);e(o.nodes.get("compose")?.outputs.value.type).toBe("vec4"),e(o.output.color).toBeTruthy()}),t("atan2 node compiles from y and x inputs",()=>{const t=j([{id:"atan2",kind:"math.atan2",inputs:{y:{value:1},x:{value:0}}}],[],{color:{input:{nodeId:"atan2",port:"value"}}}),o=v(t);e(o.nodes.get("atan2")?.outputs.value.type).toBe("float"),e(o.output.color).toBeTruthy()}),t("built-in attribute nodes compile for fragment and transform outputs",()=>{const t=j([{id:"color",kind:"builtin.color"},{id:"normal",kind:"builtin.normal"},{id:"position",kind:"builtin.position"},{id:"worldPosition",kind:"builtin.worldPosition"},{id:"objectPosition",kind:"builtin.objectPosition"},{id:"translate",kind:"transform.translate"}],[{from:{nodeId:"objectPosition",port:"value"},to:{nodeId:"translate",port:"offset"}}],{color:{input:{nodeId:"color",port:"value"}},normal:{input:{nodeId:"normal",port:"value"}},roughness:{input:{nodeId:"position",port:"x"}},metalness:{input:{nodeId:"worldPosition",port:"x"}},transform:{input:{nodeId:"translate",port:"value"}}}),o=v(t);e(o.nodes.get("color")?.outputs.value.type).toBe("rgba"),e(o.nodes.get("normal")?.outputs.value.type).toBe("vec3"),e(o.nodes.get("position")?.outputs.value.type).toBe("vec3"),e(o.nodes.get("worldPosition")?.outputs.value.type).toBe("vec3"),e(o.nodes.get("objectPosition")?.outputs.value.type).toBe("vec3"),e(o.output.color).toBeTruthy(),e(o.output.normal).toBeTruthy(),e(o.output.transform).toBeTruthy(),e(c(t).outputTransform).toBeTruthy()}),t("painted layer mix node compiles color layers and builds a material",()=>{const t=j([{id:"base",kind:"constant.rgba",params:{value:"#224422",alpha:1}},{id:"grass",kind:"constant.rgba",params:{value:"#55aa33",alpha:1}},{id:"stone",kind:"constant.rgba",params:{value:"#777777",alpha:1}},{id:"mix",kind:"layer.mixPainted",params:{mode:"soft",enableNoise:!0,softness:.25,noiseScale:.2,noiseAmount:.4}}],[{from:{nodeId:"base",port:"value"},to:{nodeId:"mix",port:"base"}},{from:{nodeId:"grass",port:"value"},to:{nodeId:"mix",port:"layer1"}},{from:{nodeId:"stone",port:"value"},to:{nodeId:"mix",port:"layer3"}}],{color:{input:{nodeId:"mix",port:"value"}}}),o=v(t);e(o.nodes.get("mix")?.outputs.value.type).toBe("rgba"),e(o.output.color).toBeTruthy(),e(c(t).isMaterial).toBe(!0)}),t("painted layer mix node compiles sparse float layers in hard and soft modes",()=>{for(const t of["hard","soft"]){const o=j([{id:"base",kind:"constant.float",params:{value:.8}},{id:"mud",kind:"constant.float",params:{value:1}},{id:"wet",kind:"constant.float",params:{value:.25}},{id:"mix",kind:"layer.mixPainted",params:{mode:t,enableNoise:!1,softness:.3,noiseScale:.1,noiseAmount:.5}}],[{from:{nodeId:"base",port:"value"},to:{nodeId:"mix",port:"base"}},{from:{nodeId:"mud",port:"value"},to:{nodeId:"mix",port:"layer2"}},{from:{nodeId:"wet",port:"value"},to:{nodeId:"mix",port:"layer8"}}],{roughness:{input:{nodeId:"mix",port:"value"}}}),a=v(o);e(a.nodes.get("mix")?.outputs.value.type).toBe("float"),e(a.output.roughness).toBeTruthy()}}),t("painted layer mix node rejects incompatible layer types",()=>{const t=j([{id:"base",kind:"constant.vec2",params:{value:[0,1]}},{id:"layer",kind:"constant.vec3",params:{value:[1,0,0]}},{id:"mix",kind:"layer.mixPainted"}],[{from:{nodeId:"base",port:"value"},to:{nodeId:"mix",port:"base"}},{from:{nodeId:"layer",port:"value"},to:{nodeId:"mix",port:"layer1"}}],{color:{input:{nodeId:"mix",port:"value"}}});e(()=>v(t)).toThrow(/Cannot mix vec2 and vec3/)}),t("additional math, uv, shape, noise, and effect nodes compile",()=>{const t=j([{id:"uv",kind:"builtin.uv"},{id:"angle",kind:"constant.float",params:{value:.5}},{id:"scale",kind:"constant.float",params:{value:4}},{id:"half",kind:"constant.float",params:{value:.5}},{id:"axis",kind:"constant.vec3",params:{value:[0,1,0]}},{id:"rotatedUv",kind:"uv.rotate"},{id:"twirl",kind:"uv.twirl"},{id:"bulge",kind:"uv.bulge"},{id:"radial",kind:"uv.radial"},{id:"flipbook",kind:"uv.flipbook"},{id:"shape",kind:"shape.roundedRectangle"},{id:"noise",kind:"noise.simplex"},{id:"voronoi",kind:"noise.voronoi2d"},{id:"fresnel",kind:"effect.fresnel"},{id:"sin",kind:"math.sin"},{id:"pow",kind:"math.pow"},{id:"mix",kind:"math.mix"},{id:"rotateAxis",kind:"transform.rotateAxis"}],[{from:{nodeId:"uv",port:"value"},to:{nodeId:"rotatedUv",port:"uv"}},{from:{nodeId:"angle",port:"value"},to:{nodeId:"rotatedUv",port:"angle"}},{from:{nodeId:"rotatedUv",port:"value"},to:{nodeId:"twirl",port:"uv"}},{from:{nodeId:"scale",port:"value"},to:{nodeId:"twirl",port:"strength"}},{from:{nodeId:"twirl",port:"value"},to:{nodeId:"bulge",port:"uv"}},{from:{nodeId:"half",port:"value"},to:{nodeId:"bulge",port:"power"}},{from:{nodeId:"bulge",port:"value"},to:{nodeId:"radial",port:"uv"}},{from:{nodeId:"twirl",port:"value"},to:{nodeId:"flipbook",port:"uv"}},{from:{nodeId:"twirl",port:"value"},to:{nodeId:"shape",port:"uv"}},{from:{nodeId:"half",port:"value"},to:{nodeId:"shape",port:"width"}},{from:{nodeId:"half",port:"value"},to:{nodeId:"shape",port:"height"}},{from:{nodeId:"half",port:"value"},to:{nodeId:"shape",port:"radius"}},{from:{nodeId:"twirl",port:"value"},to:{nodeId:"noise",port:"uv"}},{from:{nodeId:"scale",port:"value"},to:{nodeId:"noise",port:"scale"}},{from:{nodeId:"twirl",port:"value"},to:{nodeId:"voronoi",port:"uv"}},{from:{nodeId:"scale",port:"value"},to:{nodeId:"voronoi",port:"scale"}},{from:{nodeId:"scale",port:"value"},to:{nodeId:"fresnel",port:"power"}},{from:{nodeId:"radial",port:"angle"},to:{nodeId:"sin",port:"value"}},{from:{nodeId:"noise",port:"value"},to:{nodeId:"pow",port:"a"}},{from:{nodeId:"half",port:"value"},to:{nodeId:"pow",port:"b"}},{from:{nodeId:"shape",port:"value"},to:{nodeId:"mix",port:"a"}},{from:{nodeId:"voronoi",port:"value"},to:{nodeId:"mix",port:"b"}},{from:{nodeId:"fresnel",port:"value"},to:{nodeId:"mix",port:"t"}},{from:{nodeId:"axis",port:"value"},to:{nodeId:"rotateAxis",port:"axis"}},{from:{nodeId:"angle",port:"value"},to:{nodeId:"rotateAxis",port:"angle"}}],{color:{input:{nodeId:"mix",port:"value"}},opacity:{input:{nodeId:"sin",port:"value"}},anisotropyDirection:{input:{nodeId:"flipbook",port:"value"}},transform:{input:{nodeId:"rotateAxis",port:"value"}}}),o=v(t);e(o.nodes.get("mix")?.outputs.value.type).toBe("float"),e(o.nodes.get("twirl")?.outputs.value.type).toBe("vec2"),e(o.nodes.get("bulge")?.outputs.value.type).toBe("vec2"),e(o.nodes.get("radial")?.outputs.coords.type).toBe("vec2"),e(o.nodes.get("flipbook")?.outputs.value.type).toBe("vec2"),e(o.nodes.get("rotateAxis")?.outputs.value.type).toBe("mat4"),e(o.output.color).toBeTruthy(),e(o.output.opacity).toBeTruthy(),e(o.output.transform).toBeTruthy()}),t("slider input node outputs a clamped float value",()=>{const t=j([{id:"slider",kind:"input.slider",params:{value:1.25}}],[],{roughness:{input:{nodeId:"slider",port:"value"}}}),o=v(t);e(o.nodes.get("slider")?.outputs.value.type).toBe("float"),e(o.output.roughness).toBeTruthy()}),t("comparison and boolean graph nodes compile to boolean outputs",()=>{const t=j([{id:"a",kind:"constant.float",params:{value:.75}},{id:"b",kind:"constant.float",params:{value:.5}},{id:"greater",kind:"compare.greaterThan"},{id:"lessEqual",kind:"compare.lessThanOrEqual"},{id:"not",kind:"boolean.not"},{id:"and",kind:"boolean.and"},{id:"or",kind:"boolean.or"}],[{from:{nodeId:"a",port:"value"},to:{nodeId:"greater",port:"a"}},{from:{nodeId:"b",port:"value"},to:{nodeId:"greater",port:"b"}},{from:{nodeId:"a",port:"value"},to:{nodeId:"lessEqual",port:"a"}},{from:{nodeId:"b",port:"value"},to:{nodeId:"lessEqual",port:"b"}},{from:{nodeId:"lessEqual",port:"value"},to:{nodeId:"not",port:"value"}},{from:{nodeId:"greater",port:"value"},to:{nodeId:"and",port:"a"}},{from:{nodeId:"not",port:"value"},to:{nodeId:"and",port:"b"}},{from:{nodeId:"and",port:"value"},to:{nodeId:"or",port:"a"}},{from:{nodeId:"lessEqual",port:"value"},to:{nodeId:"or",port:"b"}}],{discard:{input:{nodeId:"or",port:"value"}}}),o=v(t);e(o.nodes.get("greater")?.outputs.value.type).toBe("boolean"),e(o.nodes.get("lessEqual")?.outputs.value.type).toBe("boolean"),e(o.nodes.get("not")?.outputs.value.type).toBe("boolean"),e(o.nodes.get("and")?.outputs.value.type).toBe("boolean"),e(o.nodes.get("or")?.outputs.value.type).toBe("boolean"),e(o.output.discard).toBeTruthy()}),t("split component node exposes vector and color channels as separate outputs",()=>{const t=j([{id:"vec",kind:"constant.vec3",params:{value:[.2,.4,.6]}},{id:"color",kind:"constant.rgba",params:{value:"#336699",alpha:.75}},{id:"splitVec",kind:"decompose.split"},{id:"splitColor",kind:"decompose.split"},{id:"sum",kind:"math.add"}],[{from:{nodeId:"vec",port:"value"},to:{nodeId:"splitVec",port:"value"}},{from:{nodeId:"color",port:"value"},to:{nodeId:"splitColor",port:"value"}},{from:{nodeId:"splitVec",port:"z"},to:{nodeId:"sum",port:"a"}},{from:{nodeId:"splitColor",port:"a"},to:{nodeId:"sum",port:"b"}}],{roughness:{input:{nodeId:"sum",port:"value"}}}),o=v(t);e(Object.keys(o.nodes.get("splitVec")?.outputs??{})).toEqual(["x","y","z"]),e(Object.keys(o.nodes.get("splitColor")?.outputs??{})).toEqual(["r","g","b","a","x","y","z","w"]),e(o.nodes.get("splitVec")?.outputs.z.type).toBe("float"),e(o.nodes.get("splitColor")?.outputs.a.type).toBe("float"),e(o.output.roughness).toBeTruthy()}),t("color gradient node compiles editable stops to a sampled color",()=>{const t=j([{id:"threshold",kind:"constant.float",params:{value:.75}},{id:"gradient",kind:"color.gradient",params:{stops:[{position:0,color:"#000000",alpha:1},{position:.5,color:"#ff0000",alpha:1},{position:1,color:"#ffffff",alpha:1}]}}],[{from:{nodeId:"threshold",port:"value"},to:{nodeId:"gradient",port:"t"}}],{color:{input:{nodeId:"gradient",port:"value"}}}),o=v(t);e(o.nodes.get("gradient")?.outputs.value.type).toBe("rgba"),e(o.output.color).toBeTruthy(),e(f(t,"gradient").color).toBeTruthy()}),t("curve node compiles editable curve to a sampled float",()=>{const t=j([{id:"t",kind:"constant.float",params:{value:.75}},{id:"curve",kind:"utility.curve",params:{curve:"InOutQuad"}}],[{from:{nodeId:"t",port:"value"},to:{nodeId:"curve",port:"t"}}],{roughness:{input:{nodeId:"curve",port:"value"}}}),o=v(t);e(o.nodes.get("curve")?.outputs.value.type).toBe("float"),e(o.output.roughness).toBeTruthy(),e(f(t,"curve").color).toBeTruthy()}),t("graph parameters convert to property params and custom defaults",()=>{const t=y();t.parameters.push({name:"opacity",type:"float",group:"Surface",defaultValue:.5});const o=b(t);e(o.map(e=>e.name)).toEqual(["baseColor","opacity"]),e(o.every(e=>!0===e.options.optional)).toBe(!0),e(o.map(e=>e.options.group??null)).toEqual([null,"Surface"]);const a=d(t);e(a.baseColor).toMatchObject({type:p.RgbNode,value:"#ffffff",override:!1}),e(a.opacity).toMatchObject({type:p.FloatNode,value:.5,override:!1})}),t("existing shader graph param values keep overriding unless explicitly disabled",()=>{const t=y(),o=d(t,{baseColor:{type:p.RgbNode,value:"#ff0000"}});e(o.baseColor).toMatchObject({type:p.RgbNode,value:"#ff0000",override:!0});const a=d(t,{baseColor:{type:p.RgbNode,value:"#00ff00",override:!1}});e(a.baseColor).toMatchObject({type:p.RgbNode,value:"#ffffff",override:!1})}),t("texture graph parameter defaults serialize as sampler parameters",()=>{const t=j([],[],{});t.parameters.push({name:"map",type:"texture",defaultValue:"texture-asset-id"});const o=d(t);e(o.map).toMatchObject({type:p.Sampler2DNode,value:"texture-asset-id"})}),t("shader graph params keep graph-declared serialized types over previous string params",()=>{const t=y();t.parameters.push({name:"map",type:"texture",defaultValue:null});const o=d(t,{baseColor:{type:p.String,value:"#ff0000"},map:{type:p.String,value:"texture-asset-id"}});e(o.baseColor).toMatchObject({type:p.RgbNode,value:"#ff0000"}),e(o.map).toMatchObject({type:p.Sampler2DNode,value:"texture-asset-id"})}),t("shader graph texture params resolve asset ids before compile",async()=>{const t=j([{id:"texture",kind:"input.parameter",params:{parameter:"map"}}],[],{color:{input:{nodeId:"texture",port:"value"}}});t.parameters.push({name:"map",type:"texture",defaultValue:"texture-asset-id"});const o=await u({},t,new D([{id:"texture-asset-id",name:"Map",type:"texture",fileKey:"map.png",fileFormat:"png"}]),new A);e(o.map).toBeTruthy(),e(typeof o.map).not.toBe("string"),e(v(t,{parameters:o}).output.color).toBeTruthy()}),t("shader graph texture array params expose named slice uniforms",async()=>{const t=j([{id:"texture",kind:"input.parameter",params:{parameter:"map"}}],[],{color:{input:{nodeId:"texture",port:"value"}}});t.parameters.push({name:"map",type:"texture",defaultValue:"texture-array-layer"});const o=await u({},t,new D([{id:"texture-array-layer",name:"Map Layer",type:"texture",fileKey:"map.png",fileFormat:"png",texture:{textureArrayFileKey:"textures/packed.ktx2",textureArrayLayer:5}}]),new O(new n,5)),a=c(t,{parameters:o});e(a.uniforms.pu_map_i?.value).toBe(5),e(a.fragmentShader).toContain("uniform float pu_map_i")}),t("standard shader height maps support texture array slices",()=>{const t=new w;t.heightMap=new T("heightMap",new n,new B("heightMap_i",3,void 0,!1)),t.map=k(new n),t.heightScale=.05;const o=t.build();e(o.fragmentShader).toContain("uniform sampler2DArray heightMap"),e(o.fragmentShader).toContain("uniform float heightMap_i"),e(o.fragmentShader).toContain("texture(heightMap, vec3(uv + vCurrOffset, heightMapLayer)).r")}),t("standard shader supports particle opacity for instanced VFX materials",()=>{const t=(new w).build();e(t.fragmentShader).toContain("IS_PARTICLE"),e(t.fragmentShader).toContain("particleData")}),t("material graph asset reference and local embedded graph both build materials",async()=>{const t={id:"graph-asset",name:"Reusable Graph",type:"shaderGraph",shaderGraph:y()},o=M("referenced",{source:"asset",assetId:t.id}),a=M("local",{source:"local",graph:y()}),r=new D([t,o,a]),n=new l;e((await s(o,null,r,n,[],!1)).isMaterial).toBe(!0),e((await s(a,null,r,n,[],!1)).isMaterial).toBe(!0)}),t("selected-node preview compiles scalar, vector, color, rgba, and boolean outputs",()=>{const t=[{id:"float",kind:"constant.float",params:{value:.25}},{id:"slider",kind:"input.slider",params:{value:.5}},{id:"vec2",kind:"constant.vec2",params:{value:[1,0]}},{id:"rgb",kind:"constant.vec3",params:{value:[1,.5,0]}},{id:"rgba",kind:"constant.rgba",params:{value:"#ff8800",alpha:.75}},{id:"boolean",kind:"constant.boolean",params:{value:!0}}];for(const o of t){const t=j([o],[],{color:{input:{nodeId:o.id,port:"value"}}});e(f(t,o.id).color).toBeTruthy()}}),t("post-process shader graph builds a fullscreen-safe material",()=>{const t=h(),o=m(t);e(o).toBeInstanceOf(x),e(o.depthWrite).toBe(!1),e(o.depthTest).toBe(!1),e(o.toneMapped).toBe(!1),e(o.transparent).toBe(!1),e(o.userData.isPostProcessShaderGraph).toBe(!0),e(o.uniforms[C]).toBeTruthy(),e(o.uniforms[I]).toBeTruthy()}),t("empty post-process shader graph defaults to identity scene color",()=>{const t=h();t.nodes=[],t.edges=[],t.outputs={};const o=v(t),a=m(t);e(o.output.color).toBeTruthy(),e(o.output.transparent).toBe(!1),e(a.uniforms[C]).toBeTruthy(),e(a.uniforms[I]).toBeTruthy()}),t("post-process color and opacity outputs expose blend weight uniform",()=>{const t=j([{id:"color",kind:"constant.rgba",params:{value:"#ff0044",alpha:1}},{id:"opacity",kind:"constant.float",params:{value:.35}}],[],{color:{input:{nodeId:"color",port:"value"}},opacity:{input:{nodeId:"opacity",port:"value"}},roughness:{value:.8}});t.target="postProcess";const o=v(t),a=m(t);e(o.output.color).toBeTruthy(),e(o.output.opacity).toBeTruthy(),e(o.output.roughness).toBeUndefined(),e(a.uniforms[I]).toBeTruthy()}),t("selected-node preview builds through post-process fullscreen path",()=>{const t=j([{id:"float",kind:"constant.float",params:{value:.25}}],[],{color:{input:{nodeId:"float",port:"value"}}});t.target="postProcess";const o=m(t,{previewNodeId:"float"});e(o).toBeInstanceOf(x),e(o.userData.isPostProcessShaderGraph).toBe(!0),e(o.uniforms[C]).toBeTruthy(),e(o.uniforms[I]).toBeTruthy()}),t("texture parameter nodes expose sampled value and channel outputs",()=>{const t=j([{id:"texture",kind:"input.parameter",params:{parameter:"albedo"}}],[],{color:{input:{nodeId:"texture",port:"value"}}});t.parameters.push({name:"albedo",type:"texture",defaultValue:null});const o=v(t,{parameters:{albedo:new n}}),a=o.nodes.get("texture")?.outputs??{};e(Object.keys(a)).toEqual(["value","rgb","r","g","b","a","sampler"]),e(a.value.type).toBe("rgba"),e(a.rgb.type).toBe("rgb"),e(a.r.type).toBe("float"),e(a.g.type).toBe("float"),e(a.b.type).toBe("float"),e(a.a.type).toBe("float"),e(a.sampler.type).toBe("sampler2d"),e(o.output.color).toBeTruthy()}),t("texture parameter nodes accept resolved sampler values",()=>{const t=j([{id:"texture",kind:"input.parameter",params:{parameter:"map"}}],[],{color:{input:{nodeId:"texture",port:"value"}}});t.parameters.push({name:"map",type:"texture",defaultValue:"texture-asset-id"});const o=v(t,{parameters:{map:k(new n)}});e(o.output.color).toBeTruthy()}),t("missing texture parameter values warn and use fallback sampler",()=>{const t=j([{id:"texture",kind:"input.parameter",params:{parameter:"missingTexture"}}],[],{color:{input:{nodeId:"texture",port:"value"}}});t.parameters.push({name:"missingTexture",type:"texture",defaultValue:null});const a=o.spyOn(console,"warn").mockImplementation(()=>{});try{const o=v(t),r=o.nodes.get("texture")?.outputs??{};e(r.sampler.type).toBe("sampler2d"),e(o.output.color).toBeTruthy(),e(a).toHaveBeenCalledWith(e.stringContaining("missingTexture"))}finally{a.mockRestore()}}),t("scene depth graph nodes expose sampled and fragment linear eye depth",()=>{const t=j([{id:"uv",kind:"builtin.screenUv"},{id:"sceneDepth",kind:"scene.sampleDepth"},{id:"fragmentDepth",kind:"scene.fragmentDepth"},{id:"depthFade",kind:"math.subtract"}],[{from:{nodeId:"uv",port:"value"},to:{nodeId:"sceneDepth",port:"uv"}},{from:{nodeId:"sceneDepth",port:"linearEyeDepth"},to:{nodeId:"depthFade",port:"a"}},{from:{nodeId:"fragmentDepth",port:"linearEyeDepth"},to:{nodeId:"depthFade",port:"b"}}],{opacity:{input:{nodeId:"depthFade",port:"value"}}}),o=v(t);e(o.nodes.get("sceneDepth")?.outputs.depth.type).toBe("float"),e(o.nodes.get("sceneDepth")?.outputs.linearEyeDepth.type).toBe("float"),e(o.nodes.get("fragmentDepth")?.outputs.linearEyeDepth.type).toBe("float"),e(o.output.opacity).toBeTruthy()}),t("unpack normal node compiles with various input combinations",()=>{const t=j([{id:"sample",kind:"constant.vec3",params:{value:[.5,.5,1]}},{id:"scale",kind:"constant.float",params:{value:1}},{id:"normal",kind:"builtin.normal"},{id:"toNormal",kind:"color.unpackNormal"}],[{from:{nodeId:"sample",port:"value"},to:{nodeId:"toNormal",port:"color"}},{from:{nodeId:"scale",port:"value"},to:{nodeId:"toNormal",port:"scale"}},{from:{nodeId:"normal",port:"value"},to:{nodeId:"toNormal",port:"normal"}}],{normal:{input:{nodeId:"toNormal",port:"value"}}}),o=v(t);e(o.nodes.get("toNormal")?.outputs.value.type).toBe("vec3"),e(o.output.normal).toBeTruthy()});class D extends i{constructor(e){super(),this.onCreate=null,this.onDelete=null,this.onUpdate=null,this.assets=new Map(e.map(e=>[e.id,e]))}async getAsset(e){return this.assets.get(e)}async getAssets(){return Array.from(this.assets.values())}}class A extends l{async getTexture(){return new n}async getTextureArray(){return{texture:null,layerIndex:null}}}class O extends A{constructor(e,t){super(),this.textureArray=e,this.layerIndex=t}async getTextureArray(){return{texture:this.textureArray,layerIndex:this.layerIndex}}}/*
|
|
2
2
|
* Copyright (©) 2026 Hology Interactive AB. All rights reserved.
|
|
3
3
|
* See the LICENSE.md file for details.
|
|
4
4
|
*/
|