incanto 0.3.1 → 0.3.3
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/dist/3d.d.ts +3 -0
- package/dist/3d.js +3 -3
- package/dist/{create-game-BK0RAa-8.js → create-game-DKdR28SI.js} +9 -2
- package/dist/index.js +1 -1
- package/dist/{physics-3d-ueniRlc-.js → physics-3d-DXODbpY1.js} +1 -1
- package/dist/react.js +1 -1
- package/dist/{register-P5PDzb7s.js → register-CljkZG08.js} +132 -38
- package/dist/test.js +3 -3
- package/editor/assets/{agent8-CjLdyAp1.js → agent8-CXBTOrkj.js} +1 -1
- package/editor/assets/{index-C82h2nk2.js → index-DMghTAy4.js} +36 -36
- package/editor/index.html +1 -1
- package/package.json +1 -1
- package/schemas/scene.schema.json +10 -0
- package/skills/incanto-building-3d-games.md +1 -0
- package/skills/incanto-node-reference.md +2 -0
|
@@ -5298,7 +5298,7 @@ if (splatDetailAmount > 0.001) {
|
|
|
5298
5298
|
|
|
5299
5299
|
normal = perturbSplatNormal(normal, vViewPosition, vUvSplat, blendedNormalSample);
|
|
5300
5300
|
#endif
|
|
5301
|
-
`;function yy(e,t){for(let n=0;n<4;n++){let r=n+1;e.uniforms[`splatTexture${r}`]={value:t.textures[n]??t.textures[0]??null},e.uniforms[`splatNormalMap${r}`]={value:t.normalMaps[n]??null},e.uniforms[`splatTextureRepeat${r}`]={value:t.repeats[n]??1},e.uniforms[`splatRoughness${r}`]={value:t.roughness[n]??1},e.uniforms[`splatMetalness${r}`]={value:t.metalness[n]??0},e.uniforms[`uvNoiseIntensity${r}`]={value:t.uvNoiseIntensity[n]??.15}}e.uniforms.splatTextureCount={value:Math.min(4,t.layerCount)},e.uniforms.splatEnvCut={value:t.envCut},e.vertexShader=uy(e.vertexShader,`#include <common>`,dy),e.vertexShader=uy(e.vertexShader,`#include <begin_vertex>`,fy);let n=e.fragmentShader;n=uy(n,`#include <common>`,py),n=uy(n,`#include <color_fragment>`,my),n=uy(n,`#include <roughnessmap_fragment>`,hy),n=uy(n,`#include <metalnessmap_fragment>`,_y),n=uy(n,`#include <normal_fragment_maps>`,vy),n=uy(n,`#include <lights_fragment_maps>`,gy),t.hasNormalMaps&&(n=`#define USE_TERRAIN_NORMALMAP\n${n}`),e.fragmentShader=n}var by=[`island`,`alpine`,`plains`,`desert`,`grassland`,`forest`,`savanna`,`snow`,`wetland`,`volcanic`,`custom`],xy=`https://agent8-games.verse8.io/assets/3D/default/textures/terrain`,Sy=`https://agent8-games.verse8.io/assets/3D/default/textures/vegetation/ground`,Cy=1/3,wy=Math.PI/4;function Ty(e,t,n){return{texture:`${e}/${t}.png`,normalMap:`${e}/${t}_normal.png`,...n}}function Ey(e,t){let n=t.replace(/\/$/,``);switch(e){case`island`:return[Ty(n,`sand`,{heightRange:[0,.12],slopeRange:[0,Math.PI/2],heightBlendRange:.05,roughness:.9}),Ty(n,`grass`,{heightRange:[.1,.7],slopeRange:[0,wy],heightBlendRange:.1,slopeBlendRange:.1,roughness:.8}),Ty(n,`stone`,{heightRange:[0,1],slopeRange:[wy,Math.PI],heightBlendRange:.5,slopeBlendRange:.5,roughness:.85}),Ty(n,`snow`,{heightRange:[.8,1],slopeRange:[0,Math.PI/3],heightBlendRange:.1,roughness:.6})];case`alpine`:return[Ty(n,`grass`,{heightRange:[0,.45],slopeRange:[0,Math.PI/5],heightBlendRange:.1,roughness:.8}),Ty(n,`stone`,{heightRange:[.3,1],slopeRange:[0,Math.PI],heightBlendRange:.25,slopeBlendRange:.5,roughness:.85}),Ty(n,`gravel`,{heightRange:[0,1],slopeRange:[Math.PI/5,Math.PI],heightBlendRange:.5,slopeBlendRange:.15,roughness:.9}),Ty(n,`snow`,{heightRange:[.55,1],slopeRange:[0,Math.PI/3],heightBlendRange:.12,roughness:.55})];case`plains`:return[Ty(n,`grass`,{heightRange:[0,1],slopeRange:[0,wy],heightBlendRange:.1,slopeBlendRange:.1,roughness:.8}),Ty(n,`dirt`,{heightRange:[.35,.65],slopeRange:[0,Math.PI/5],heightBlendRange:.2,roughness:.9}),Ty(n,`gravel`,{heightRange:[0,1],slopeRange:[wy,Math.PI],heightBlendRange:.5,slopeBlendRange:.3,roughness:.9})];case`desert`:return[Ty(n,`sand`,{heightRange:[0,1],slopeRange:[0,wy],heightBlendRange:.1,slopeBlendRange:.15,roughness:.95}),Ty(n,`gravel`,{heightRange:[0,.15],slopeRange:[0,Math.PI/3],heightBlendRange:.08,roughness:.9}),Ty(n,`stone`,{heightRange:[0,1],slopeRange:[wy,Math.PI],heightBlendRange:.5,slopeBlendRange:.4,roughness:.85})];case`grassland`:return n===`https://agent8-games.verse8.io/assets/3D/default/textures/terrain`?[{texture:`${Sy}/grass.jpg`,heightRange:[0,1],slopeRange:[0,wy],heightBlendRange:.1,slopeBlendRange:.15,roughness:1,repeat:Cy,uvNoiseIntensity:0},{texture:`${Sy}/dirt_color.jpg`,heightRange:[0,1],slopeRange:[wy,Math.PI],heightBlendRange:.5,slopeBlendRange:.3,roughness:1,repeat:Cy,uvNoiseIntensity:0}]:[Ty(n,`grass`,{heightRange:[0,1],slopeRange:[0,wy],heightBlendRange:.1,slopeBlendRange:.15,roughness:.8}),Ty(n,`dirt`,{heightRange:[0,1],slopeRange:[wy,Math.PI],heightBlendRange:.5,slopeBlendRange:.3,roughness:.9})];case`forest`:return n===`https://agent8-games.verse8.io/assets/3D/default/textures/terrain`?[{texture:`${Sy}/dirt_color.jpg`,heightRange:[0,1],slopeRange:[0,Math.PI],heightBlendRange:.1,roughness:1,repeat:Cy,uvNoiseIntensity:0},{texture:`${Sy}/grass.jpg`,heightRange:[-2,-1],roughness:1,repeat:Cy,uvNoiseIntensity:0}]:[Ty(n,`dirt`,{heightRange:[0,1],slopeRange:[0,Math.PI],heightBlendRange:.1,roughness:.9}),Ty(n,`grass`,{heightRange:[-2,-1],roughness:.8})];case`savanna`:return[Ty(n,`grass`,{heightRange:[0,1],slopeRange:[0,wy],heightBlendRange:.1,slopeBlendRange:.15,roughness:.95}),Ty(n,`sand`,{heightRange:[0,.22],slopeRange:[0,Math.PI/4],heightBlendRange:.12,roughness:.95}),Ty(n,`dirt`,{heightRange:[0,1],slopeRange:[wy,Math.PI],heightBlendRange:.5,slopeBlendRange:.3,roughness:.95})];case`snow`:return[Ty(n,`snow`,{heightRange:[0,1],slopeRange:[0,wy],heightBlendRange:.1,slopeBlendRange:.18,roughness:.55}),Ty(n,`stone`,{heightRange:[0,1],slopeRange:[wy,Math.PI],heightBlendRange:.5,slopeBlendRange:.35,roughness:.85})];case`wetland`:return[Ty(n,`grass`,{heightRange:[0,1],slopeRange:[0,wy],heightBlendRange:.1,slopeBlendRange:.15,roughness:.95}),Ty(n,`dirt`,{heightRange:[0,1],slopeRange:[wy,Math.PI],heightBlendRange:.5,slopeBlendRange:.3,roughness:.95})];case`volcanic`:return[Ty(n,`stone`,{heightRange:[0,1],slopeRange:[0,Math.PI],heightBlendRange:.1,roughness:.95}),Ty(n,`gravel`,{heightRange:[0,.28],slopeRange:[0,Math.PI/3],heightBlendRange:.14,slopeBlendRange:.2,roughness:.95})];case`custom`:return[]}}var Dy={scale:100,threshold:.7,blend:.1},Oy={scale:55,threshold:.58,blend:.12},ky={scale:85,threshold:.62,blend:.12},Ay={scale:48,threshold:.5,blend:.14};function jy(e,t){let n=Be(e+27927);return(e,r)=>{let i=(.5+.5*n(e/t.scale,r/t.scale)-(t.threshold-t.blend))/(2*t.blend),a=Math.min(1,Math.max(0,i));return a*a*(3-2*a)}}function My(e,t){return e===`grassland`?jy(t,Dy):e===`forest`?jy(t,Oy):e===`savanna`?jy(t,ky):e===`wetland`?jy(t,Ay):null}function Ny(e){return e===`savanna`?{from:0,to:2}:e===`grassland`||e===`forest`||e===`wetland`?{from:0,to:1}:null}var Py=128,Fy=class extends Zv{static typeName=`Terrain3D`;static props={size:{default:[200,200]},maxHeight:{default:28},seed:{default:1},resolution:{default:128},theme:{default:`island`,options:by},roughness:{default:.5},detail:{default:4},flatThreshold:{default:.95},islandEdge:{default:null},layers:{default:[]},textureBase:{default:xy},basins:{default:[]}};size=[200,200];maxHeight=28;seed=1;resolution=128;theme=`island`;roughness=.5;detail=4;flatThreshold=.95;islandEdge=null;layers=[];textureBase=xy;basins=[];static validateJson(e){let t=e;if(!by.includes(t.theme))throw new y(`BAD_FORMAT`,`Terrain3D '${e.name}' theme must be one of [${by.join(`, `)}], got '${t.theme}'.`,{prop:`theme`,validOptions:by});if(!((t.size[0]??0)>0)||!((t.size[1]??0)>0))throw new y(`BAD_FORMAT`,`Terrain3D '${e.name}' size must be positive [width, depth] meters, got ${JSON.stringify(t.size)}.`,{prop:`size`});if(!(t.maxHeight>0))throw new y(`BAD_FORMAT`,`Terrain3D '${e.name}' maxHeight must be > 0, got ${t.maxHeight}.`,{prop:`maxHeight`});if(!Number.isInteger(t.resolution)||t.resolution<2||t.resolution>Py)throw new y(`BAD_FORMAT`,`Terrain3D '${e.name}' resolution must be an integer in [2, ${Py}] (grid segments per side), got ${t.resolution}.`,{prop:`resolution`});if(!(t.roughness>0)||!Number.isFinite(t.roughness))throw new y(`BAD_FORMAT`,`Terrain3D '${e.name}' roughness must be a finite number > 0, got ${t.roughness}.`,{prop:`roughness`});if(!Number.isInteger(t.detail)||t.detail<0)throw new y(`BAD_FORMAT`,`Terrain3D '${e.name}' detail must be an integer ≥ 0 (octave count), got ${t.detail}.`,{prop:`detail`});if(!(t.flatThreshold>=0)||!Number.isFinite(t.flatThreshold))throw new y(`BAD_FORMAT`,`Terrain3D '${e.name}' flatThreshold must be a finite number ≥ 0, got ${t.flatThreshold}.`,{prop:`flatThreshold`});if(t.islandEdge!==null&&typeof t.islandEdge!=`boolean`)throw new y(`BAD_FORMAT`,`Terrain3D '${e.name}' islandEdge must be true, false or null (null = theme default), got ${JSON.stringify(t.islandEdge)}.`,{prop:`islandEdge`});if(!Array.isArray(t.basins))throw new y(`BAD_FORMAT`,`Terrain3D '${e.name}' basins must be an array of { x, z, radius, depth }, got ${JSON.stringify(t.basins)}.`,{prop:`basins`});if(t.basins.forEach((t,n)=>{if(!t||typeof t!=`object`||!Number.isFinite(t.x)||!Number.isFinite(t.z)||!(t.radius>0)||!(t.depth>0))throw new y(`BAD_FORMAT`,`Terrain3D '${e.name}' basins[${n}] needs finite x, z and positive radius, depth, got ${JSON.stringify(t)}.`,{prop:`basins`})}),t.theme===`custom`){if(!Array.isArray(t.layers)||t.layers.length<1||t.layers.length>4)throw new y(`BAD_FORMAT`,`Terrain3D '${e.name}' theme 'custom' needs 1–4 layers, got ${Array.isArray(t.layers)?t.layers.length:typeof t.layers}.`,{prop:`layers`});t.layers.forEach((t,n)=>{Ry(e.name,t,n)})}else if(Array.isArray(t.layers)&&t.layers.length>0)throw new y(`BAD_FORMAT`,`Terrain3D '${e.name}' layers is only read with theme 'custom' — preset theme '${t.theme}' defines its own. Drop layers or switch to theme: 'custom'.`,{prop:`layers`})}heightmap=null;heightmapKey=``;builtKey=``;loadedTextures=[];_createObject3D(){let e=new Ha;return e.setAttribute(`position`,new q(new Float32Array,3)),new ho(e,new Bs({vertexColors:!0}))}heightAt(e,t){let n=0,r=0,i=0;for(let e=this;e;e=e.parent)e instanceof Zv&&(n+=e.position[0]??0,r+=e.position[1]??0,i+=e.position[2]??0);return this._heightmap().heightAt(e-n,t-i)+r}_heightmap(){let e=this.segments(),t=JSON.stringify([this.size,this.maxHeight,this.seed,e,this.roughness,this.detail,this.flatThreshold,this.effectiveIslandEdge(),this.basins]);return(!this.heightmap||t!==this.heightmapKey)&&(this.heightmap=sy({width:this.size[0]??1,depth:this.size[1]??1,segsX:e,segsZ:e,maxHeight:this.maxHeight,seed:this.seed,roughness:this.roughness,detail:this.detail,flatThreshold:this.flatThreshold,islandEdge:this.effectiveIslandEdge(),basins:this.basins}),this.heightmapKey=t),this.heightmap}resolvedLayers(){return this.theme===`custom`?this.layers:Ey(this.theme,this.textureBase)}_syncObject3D(){super._syncObject3D(),this.rebuildIfNeeded()}effectiveIslandEdge(){return this.islandEdge??this.theme===`island`}segments(){return Math.max(2,Math.min(Math.floor(this.resolution),Py))}rebuildIfNeeded(){let e=JSON.stringify([this.size,this.maxHeight,this.seed,this.segments(),this.theme,this.roughness,this.detail,this.flatThreshold,this.islandEdge,this.layers,this.textureBase,this.basins]);if(e===this.builtKey)return;this.builtKey=e;let t=this._ensureObject3D();t.geometry.dispose(),t.geometry=this.buildGeometry(),t.material.dispose(),this.disposeTextures(),t.material=this.buildMaterial(),t.receiveShadow=!0,t.castShadow=!0}buildGeometry(){let e=this._heightmap(),t=new Os(e.width,e.depth,e.segsX,e.segsZ);t.rotateX(-Math.PI/2);let n=t.getAttribute(`position`);for(let t=0;t<n.count;t++)n.setY(t,e.heights[t]);n.needsUpdate=!0,t.computeVertexNormals();let r=this.resolvedLayers(),i=e.maxHeight-e.minHeight||1,a=new Float32Array(n.count*3),o=this.theme===`custom`?null:Ny(this.theme),s=o?My(this.theme,this.seed):null;for(let t=0;t<n.count;t++){let c=n.getX(t),l=n.getZ(t),u=ly(Math.min(1,Math.max(0,(n.getY(t)-e.minHeight)/i)),e.slopeAt(c,l),r);if(s&&o){let e=(u[o.from]??0)*s(c,l);u[o.from]=(u[o.from]??0)-e,u[o.to]=(u[o.to]??0)+e}a[t*3]=u[0],a[t*3+1]=u[1],a[t*3+2]=u[2]}return t.setAttribute(`color`,new q(a,3)),t}buildMaterial(){let e=this.resolvedLayers(),t=new Bs({vertexColors:!0}),n=typeof document<`u`?new bc:null,r=(e,t)=>{if(!n||!e)return null;let r=n.load(e);return r.wrapS=bt,r.wrapT=bt,r.anisotropy=8,t&&(r.colorSpace=Yn),this.loadedTextures.push(r),r},i=e.map(e=>r(e.texture,!0)),a=e.map(e=>r(e.normalMap,!1)),o=a.some(e=>e!==null);if(o)for(let e=0;e<a.length;e++)a[e]||(a[e]=Ly());let s={textures:i,normalMaps:a,repeats:e.map(e=>e.repeat??2),roughness:e.map(e=>e.roughness??1),metalness:e.map(e=>e.metalness??0),uvNoiseIntensity:e.map(e=>e.uvNoiseIntensity??.15),envCut:.45,layerCount:Math.min(4,e.length),hasNormalMaps:o};return t.onBeforeCompile=e=>{yy(e,s)},t.customProgramCacheKey=()=>`incanto-terrain-splat:${+!!o}`,t}disposeTextures(){for(let e of this.loadedTextures)e.dispose();this.loadedTextures=[]}free(){let e=this._ensureObject3D();e.geometry.dispose(),e.material.dispose(),this.disposeTextures(),this.heightmap=null,super.free()}},Iy=null;function Ly(){return Iy||(Iy=new ko(new Uint8Array([128,128,255,255]),1,1),Iy.needsUpdate=!0),Iy}function Ry(e,t,n){let r=`layers[${n}]`;if(typeof t!=`object`||!t||Array.isArray(t))throw new y(`BAD_FORMAT`,`Terrain3D '${e}' ${r} must be an object ({texture, heightRange?, slopeRange?, …}).`,{prop:`layers`});let i=t;if(typeof i.texture!=`string`||i.texture.length===0)throw new y(`BAD_FORMAT`,`Terrain3D '${e}' ${r} needs "texture": a texture URL string.`,{prop:`layers`});for(let t of[`heightRange`,`slopeRange`]){let n=i[t];if(n!==void 0&&(!Array.isArray(n)||n.length!==2||n.some(e=>typeof e!=`number`||!Number.isFinite(e))))throw new y(`BAD_FORMAT`,`Terrain3D '${e}' ${r}.${t} must be [min, max] numbers, got ${JSON.stringify(n)}.`,{prop:`layers`})}}var zy=Math.PI/180;async function By(e,t){let n=await Xf(()=>import(`./rapier-DE6a0vmv.js`),[],import.meta.url);return await n.init(),new Vy(n,e,t)}var Vy=class{R;engine;debugDraw=!1;dimension=`3d`;unregisterDebug;warnedNoCollider=new WeakSet;entries=new Map;byColliderHandle=new Map;world;events;kcc;disconnect;lastDt=1/60;optsGravity;lastScene=null;constructor(e,t,n){this.R=e,this.engine=t,this.optsGravity=n?.gravity,this.lastScene=t.scene;let r=t.scene?.physics?.gravity,i=n?.gravity??r??[0,-9.81,0];this.world=new e.World({x:i[0]??0,y:i[1]??0,z:i[2]??0}),this.kcc=this.world.createCharacterController(.01),this.events=new e.EventQueue(!0),this.unregisterDebug=Gf(this),this.disconnect=t.fixedUpdated.connect(e=>this.step(e)),this.syncBodies()}step(e){if(this.lastDt=e,this.engine.scene!==this.lastScene&&(this.lastScene=this.engine.scene,!this.optsGravity)){let e=this.engine.scene?.physics?.gravity??[0,-9.81,0];this.world.gravity={x:e[0]??0,y:e[1]??0,z:e[2]??0}}this.syncBodies(),this.world.timestep=e,this.world.step(this.events);for(let[e,t]of this.entries){let n=t.body.translation();if(!t.body.isFixed()){let[t,r,i]=Ky(e),a=[n.x-t,n.y-r,n.z-i];e._interpPrev=e._interpCurr??a,e._interpCurr=a,e.position=[a[0],a[1],a[2]]}if(e instanceof ty){if(!e.fixedRotation){let n=t.body.rotation();Uy.setFromQuaternion(Hy.set(n.x,n.y,n.z,n.w),`XYZ`),e.rotation=[Uy.x/zy,Uy.y/zy,Uy.z/zy]}let n=t.body.linvel();e.linearVelocity=[n.x,n.y,n.z],t.lastV=[n.x,n.y,n.z]}t.last=Gy(e)}this.events.drainCollisionEvents((e,t,n)=>{let r=this.byColliderHandle.get(e),i=this.byColliderHandle.get(t);if(!r||!i)return;let a=n?`triggerEnter`:`triggerExit`;r.emit(a,i),i.emit(a,r)})}moveAndSlide(e){let t=this.ensureEntry(e),n=e.velocity[1]??0;e.snapToGround&&n<=0?this.kcc.enableSnapToGround(.1):this.kcc.disableSnapToGround(),this.kcc.setMaxSlopeClimbAngle(e.slopeLimitDeg*zy);let r={x:(e.velocity[0]??0)*this.lastDt,y:n*this.lastDt,z:(e.velocity[2]??0)*this.lastDt};this.kcc.computeColliderMovement(t.collider,r,this.R.QueryFilterFlags.EXCLUDE_SENSORS);let i=this.kcc.computedMovement(),a=t.body.translation();t.body.setNextKinematicTranslation({x:a.x+i.x,y:a.y+i.y,z:a.z+i.z}),e._grounded=this.kcc.computedGrounded()}debugLines(){return this.debugDraw?this.world.debugRender().vertices:null}dispose(){this.disconnect(),this.unregisterDebug(),this.world.free()}syncBodies(){let e=this.engine.scene;if(!e)return;let t=new Set;Wy(e.root,t);for(let e of t){if(e.collider.shape===void 0){let t=this.entries.get(e);t&&(this.byColliderHandle.delete(t.collider.handle),this.world.removeRigidBody(t.body),this.entries.delete(e),e._physics=null,e._interpPrev=e._interpCurr=null),this.warnedNoCollider.has(e)||(this.warnedNoCollider.add(e),console.warn(`[incanto] '${e.name}' has no collider — physics skips it.`));continue}let t=this.entries.get(e);t&&t.colliderKey!==JSON.stringify(e.collider)&&(this.byColliderHandle.delete(t.collider.handle),this.world.removeRigidBody(t.body),this.entries.delete(e),e._physics=null,e._interpPrev=e._interpCurr=null),this.ensureEntry(e)}for(let[e,n]of this.entries)t.has(e)||(this.byColliderHandle.delete(n.collider.handle),this.world.removeRigidBody(n.body),this.entries.delete(e),e._physics=null,e._interpPrev=e._interpCurr=null)}ensureEntry(e){let t=this.entries.get(e),n=this.R;if(!t){let r;r=e instanceof ty?n.RigidBodyDesc.dynamic():e instanceof ny||e instanceof ey?n.RigidBodyDesc.kinematicPositionBased():n.RigidBodyDesc.fixed();let[i,a,o]=Gy(e);r.setTranslation(i,a,o),Hy.setFromEuler(Uy.set((e.rotation[0]??0)*zy,(e.rotation[1]??0)*zy,(e.rotation[2]??0)*zy,`XYZ`)),r.setRotation({x:Hy.x,y:Hy.y,z:Hy.z,w:Hy.w}),e instanceof ty&&(r.setGravityScale(e.gravityScale),r.setLinvel(e.linearVelocity[0]??0,e.linearVelocity[1]??0,e.linearVelocity[2]??0),e.fixedRotation&&r.lockRotations());let s=this.world.createRigidBody(r),c=qy(n,e);c.setActiveEvents(n.ActiveEvents.COLLISION_EVENTS),c.setActiveCollisionTypes(n.ActiveCollisionTypes.ALL),e instanceof ey&&c.setSensor(!0),e instanceof ty&&c.setMass(e.mass).setFriction(e.friction).setRestitution(e.restitution);let l=this.world.createCollider(c,s);return t={colliderKey:JSON.stringify(e.collider),body:s,collider:l,last:[i,a,o],lastV:[0,0,0]},this.entries.set(e,t),this.byColliderHandle.set(l.handle,e),e._physics=this,e instanceof ty&&(e._physics3d=this),t}let r=Gy(e);if((r[0]!==t.last[0]||r[1]!==t.last[1]||r[2]!==t.last[2])&&(t.body.setTranslation({x:r[0],y:r[1],z:r[2]},!0),t.last=r,e._interpPrev=e._interpCurr=null),e instanceof ty){let n=[e.linearVelocity[0]??0,e.linearVelocity[1]??0,e.linearVelocity[2]??0];(n[0]!==t.lastV[0]||n[1]!==t.lastV[1]||n[2]!==t.lastV[2])&&(t.body.setLinvel({x:n[0],y:n[1],z:n[2]},!0),t.lastV=n),e.gravityScale!==t.body.gravityScale()&&t.body.setGravityScale(e.gravityScale,!0)}return t}applyImpulse(e,t){this.entries.get(e)?.body.applyImpulse({x:t[0],y:t[1],z:t[2]},!0)}massOf(e){return this.entries.get(e)?.body.mass()??1}velocityOf(e){let t=this.entries.get(e)?.body.linvel();return t?[t.x,t.y,t.z]:[0,0,0]}castRay(e,t,n,r,i){let a=new this.R.Ray({x:e[0],y:e[1],z:e[2]},{x:t[0],y:t[1],z:t[2]}),o=r?this.entries.get(r)?.collider:void 0,s=i?.staticOnly?this.R.QueryFilterFlags.EXCLUDE_DYNAMIC|this.R.QueryFilterFlags.EXCLUDE_KINEMATIC:void 0,c=this.world.castRayAndGetNormal(a,n,!0,s,void 0,o,void 0,e=>!e.isSensor());return c?{distance:c.timeOfImpact,normal:[c.normal.x,c.normal.y,c.normal.z],node:this.byColliderHandle.get(c.collider.handle)??null}:null}},Hy=new V,Uy=new _i;function Wy(e,t){e instanceof Qv&&t.add(e);for(let n of e.children)Wy(n,t)}function Gy(e){let t=0,n=0,r=0;for(let i=e;i;i=i.parent)i instanceof Zv&&(t+=i.position[0]??0,n+=i.position[1]??0,r+=i.position[2]??0);return[t,n,r]}function Ky(e){let t=0,n=0,r=0;for(let i=e.parent;i;i=i.parent)i instanceof Zv&&(t+=i.position[0]??0,n+=i.position[1]??0,r+=i.position[2]??0);return[t,n,r]}function qy(e,t){let n=t.collider;if(Xv(n,t.name),n.shape===`heightfield`)return Jy(e,t);let r=Yy(e,n,t.name),i=n.offset;return Array.isArray(i)&&r.setTranslation(i[0]??0,i[1]??0,i[2]??0),r}function Jy(e,t){let n=t.children.find(e=>e instanceof Fy);if(!n)throw new y(`BAD_FORMAT`,`Collider on '${t.name}': heightfield needs a Terrain3D CHILD of this body — add { "type": "Terrain3D" } under the StaticBody3D; the collider reads its height grid.`,{prop:`collider`});let r=n._heightmap(),i=r.segsZ,a=r.segsX,o=new Float32Array((i+1)*(a+1));for(let e=0;e<=a;e++)for(let t=0;t<=i;t++)o[e*(i+1)+t]=r.heights[t*(a+1)+e];let s=e.ColliderDesc.heightfield(i,a,o,{x:r.width,y:1,z:r.depth}),c=Array.isArray(t.collider.offset)?t.collider.offset:[];return s.setTranslation((n.position[0]??0)+(c[0]??0),(n.position[1]??0)+(c[1]??0),(n.position[2]??0)+(c[2]??0)),s}function Yy(e,t,n){let r=t.shape;if(r===`box`){let r=t.size;if(!Array.isArray(r)||r.length!==3||r.some(e=>typeof e!=`number`))throw new y(`BAD_FORMAT`,`Collider on '${n}': box needs "size": [x, y, z].`);return e.ColliderDesc.cuboid(r[0]/2,r[1]/2,r[2]/2)}if(r===`sphere`){if(typeof t.radius!=`number`)throw new y(`BAD_FORMAT`,`Collider on '${n}': sphere needs "radius".`);return e.ColliderDesc.ball(t.radius)}if(r===`capsule`){if(typeof t.radius!=`number`||typeof t.height!=`number`)throw new y(`BAD_FORMAT`,`Collider on '${n}': capsule needs "radius" and "height".`);return e.ColliderDesc.capsule(t.height/2,t.radius)}if(r===`trimesh`){let r=t.vertices,i=t.indices;if(!Array.isArray(r)||!Array.isArray(i))throw new y(`BAD_FORMAT`,`Collider on '${n}': trimesh needs "vertices" (flat xyz) and "indices" (triangles).`);return e.ColliderDesc.trimesh(new Float32Array(r),new Uint32Array(i))}throw new y(`BAD_FORMAT`,`Collider on '${n}': "shape" must be 'box', 'sphere', 'capsule', 'trimesh', or 'heightfield', got ${JSON.stringify(r)}.`)}var Xy=new Os(1,1),Zy=new H(0,0,1),Qy=new H,$y=new H,eb=new V,tb=new V,nb=class extends Zv{static typeName=`Sprite3D`;static props={texture:{default:``},size:{default:[1,1]},billboard:{default:`y`,options:[`y`,`full`,`none`]},anchor:{default:[.5,0]},tint:{default:`#ffffff`},opacity:{default:1},flipX:{default:!1},pixelArt:{default:!0},alphaTest:{default:.5},castShadow:{default:!1}};texture=``;size=[1,1];billboard=`y`;anchor=[.5,0];tint=`#ffffff`;opacity=1;flipX=!1;pixelArt=!0;alphaTest=.5;castShadow=!1;quadMesh=null;textureRef=``;loadedTexture=null;_camFaceQuat=new V;_shadowHooked=!1;_quad(){return this.quadMesh||(this.quadMesh=new ho(Xy,new no({transparent:!1,depthWrite:!0})),this._ensureObject3D().add(this.quadMesh)),this.quadMesh}resolveTexture(){if(!this.texture)return null;if(this.textureRef!==this.texture&&(this.loadedTexture?.dispose(),this.loadedTexture=null,this.textureRef=this.texture,typeof document<`u`)){let e=new bc().load(this.texture);e.colorSpace=Yn;let t=this.pixelArt?Ct:Et;e.magFilter=t,e.minFilter=t,this.loadedTexture=e}return this.loadedTexture}_syncObject3D(e=1){super._syncObject3D(e);let t=this._quad(),n=t.material,r=this.size[0]??1,i=this.size[1]??1;t.scale.set(this.flipX?-r:r,i,1);let a=this.anchor[0]??.5,o=this.anchor[1]??0;t.position.set((.5-a)*r,(.5-o)*i,0);let s=this.resolveTexture();if(n.map!==s&&(n.map=s,n.needsUpdate=!0),t.visible=s!==null,n.color.set(this.tint),n.opacity=this.opacity,n.transparent=this.opacity<1,n.alphaTest=this.alphaTest,t.renderOrder=this.renderOrder,t.castShadow=this.castShadow,this.castShadow){let e=t.customDepthMaterial;e||(e=new Hs({depthPacking:Jn}),t.customDepthMaterial=e),e.map!==s&&(e.map=s,e.needsUpdate=!0),e.alphaTest=this.alphaTest,this._shadowHooked||(this._shadowHooked=!0,t.onBeforeShadow=(e,n,r,i)=>{this.billboard!==`none`&&(this._faceToward(t,i.position),t.updateWorldMatrix(!0,!1))},t.onAfterShadow=()=>{this.billboard!==`none`&&(t.quaternion.copy(this._camFaceQuat),t.updateWorldMatrix(!0,!1))})}}_onRender3D(e){if(this.billboard===`none`)return;let t=this._quad();this._faceToward(t,e.camera.position),this._camFaceQuat.copy(t.quaternion)}_faceToward(e,t){e.getWorldPosition(Qy),$y.copy(t).sub(Qy),this.billboard===`y`&&($y.y=0),!($y.lengthSq()<1e-12)&&($y.normalize(),eb.setFromUnitVectors(Zy,$y),e.parent?.getWorldQuaternion(tb),e.quaternion.copy(tb.invert()).multiply(eb))}free(){this.loadedTexture?.dispose(),this.loadedTexture=null,(this.quadMesh?.customDepthMaterial)?.dispose(),super.free()}},rb=class extends nb{static typeName=`AnimatedSprite3D`;static signals=[`animationFinished`];static props={sheet:{default:``},frameWidth:{default:0},frameHeight:{default:0},animations:{default:{}},autoplay:{default:``}};sheet=``;frameWidth=0;frameHeight=0;animations={};autoplay=``;playing=!1;currentFrame=0;currentAnimation=``;frameList=[];frameIndex=0;frameTime=0;sheetTexture=null;loadedSheetRef=``;play(e){let t=this.animations[e];if(!t)throw new y(`UNKNOWN_ANIMATION`,`No animation '${e}' on '${this.name}'. Available: [${Object.keys(this.animations).join(`, `)}].`);this.currentAnimation=e,this.frameList=ib(t.frames,e),this.frameIndex=0,this.currentFrame=this.frameList[0]??0,this.frameTime=0,this.playing=!0}stop(){this.playing=!1}onReady(){this.autoplay&&this.play(this.autoplay)}update(e){if(!this.playing||this.currentAnimation===``)return;let t=this.animations[this.currentAnimation];if(!t||!Number.isFinite(t.fps)||t.fps<=0)return;let n=1/t.fps;for(this.frameTime+=e;this.frameTime>=n;)if(this.frameTime-=n,this.frameIndex+=1,this.frameIndex>=this.frameList.length)if(t.loop)this.frameIndex=0;else{this.frameIndex=this.frameList.length-1,this.playing=!1,this.emit(`animationFinished`,this.currentAnimation);break}this.currentFrame=this.frameList[this.frameIndex]??0}resolveTexture(){if(!this.sheet||this.frameWidth<=0||this.frameHeight<=0)return null;if(this.loadedSheetRef!==this.sheet&&(this.sheetTexture?.dispose(),this.sheetTexture=null,this.loadedSheetRef=this.sheet,typeof document<`u`)){let e=new bc().load(this.sheet);e.colorSpace=Yn;let t=this.pixelArt?Ct:Et;e.magFilter=t,e.minFilter=t,this.sheetTexture=e}let e=this.sheetTexture;if(!e)return null;let t=e.image;if(!t?.width||!t.height)return e;let n=Math.max(1,Math.floor(t.width/this.frameWidth)),r=this.currentFrame%n,i=Math.floor(this.currentFrame/n);return e.repeat.set(this.frameWidth/t.width,this.frameHeight/t.height),e.offset.set(r*this.frameWidth/t.width,1-(i+1)*this.frameHeight/t.height),e}free(){this.sheetTexture?.dispose(),this.sheetTexture=null,super.free()}};function ib(e,t){if(!Array.isArray(e)||e.length===0)throw new y(`BAD_FORMAT`,`Animation '${t}': "frames" must be a non-empty array.`);if(e.length===2){let[t,n]=e;if(n>=t){let e=[];for(let r=t;r<=n;r++)e.push(r);return e}}return[...e]}var ab=class extends Zv{static typeName=`Camera3D`;static props={fov:{default:60},near:{default:.1},far:{default:1e3},current:{default:!1}};fov=60;near=.1;far=1e3;current=!1;_createObject3D(){return new Nc(this.fov,1,this.near,this.far)}_syncObject3D(){super._syncObject3D();let e=this._ensureObject3D();(e.fov!==this.fov||e.near!==this.near||e.far!==this.far)&&(e.fov=this.fov,e.near=this.near,e.far=this.far,e.updateProjectionMatrix())}},ob=8,sb=.2,cb=.15,lb=1.2,ub=.08,db=2.5,fb=-20,pb=.002,mb=180/Math.PI,hb=.3,gb=.4,_b=.3,vb=new _i,yb=new V,bb=new G,xb=new H,Sb=new H,Cb=new H(0,1,0),wb=class extends Zv{static typeName=`CharacterController3D`;static signals=[`movementStateChanged`];static props={view:{default:`free`,options:[`free`,`firstPerson`,`quarter`,`side`]},camDistance:{default:4},eyeHeight:{default:.64},maxSpeed:{default:2.5},sprintMultiplier:{default:2},jumpVelocity:{default:4},sprintJumpMultiplier:{default:1.2},floatHeight:{default:.01},mouseLook:{default:!0},zoomMin:{default:0},zoomMax:{default:0},pitchMin:{default:-1.3},pitchMax:{default:1.5},cameraCollision:{default:!0},turnSpeed:{default:100},camLerp:{default:25},moveAction:{default:`move`},jumpAction:{default:`jump`},sprintAction:{default:`sprint`},skinPath:{default:`../Skin`},skinYawOffset:{default:0}};view=`free`;camDistance=4;eyeHeight=.64;maxSpeed=2.5;sprintMultiplier=2;jumpVelocity=4;sprintJumpMultiplier=1.2;floatHeight=.01;mouseLook=!0;zoomMin=0;zoomMax=0;pitchMin=-1.3;pitchMax=1.5;cameraCollision=!0;turnSpeed=100;camLerp=25;moveAction=`move`;jumpAction=`jump`;sprintAction=`sprint`;skinPath=`../Skin`;skinYawOffset=0;yaw=0;pitch=0;state=`idle`;grounded=!1;get body(){let e=this.parent;if(!(e instanceof ty))throw new y(`TREE_VIOLATION`,`CharacterController3D '${this.name}' must be a child of a RigidBody3D (parent is '${e?.name??`none`}').`);return e}onEnterTree(){if(this.body,this.skinPath!==``){let e=this.getNodeOrNull(this.skinPath);e&&(e.rotation=[e.rotation[0]??0,180+this.skinYawOffset,e.rotation[2]??0])}}fixedUpdate(e){let t=this.body,n=t._physics3d,r=this.tree?.engine?.input;if(!n||!r)return;let i=t.collider.radius??.32,a=(t.collider.height??1)/2,o=Eb(t),s=n.velocityOf(t),c=n.massOf(t),l=i+this.floatHeight,u=i+2,d=o[1]-a,f=null;for(let[e,r]of[[0,0],[i,0],[-i,0],[0,i],[0,-i]]){let i=n.castRay([o[0]+e,d,o[2]+r],[0,-1,0],u,t);if(i){let t=i.distance;if(f=f===null?t:Math.min(f,t),e===0&&r===0)break}}this.grounded=f!==null&&f<l*2;let p=0,m=0,h=!1;try{let e=r.getVector(this.moveAction);h=e.x!==0||e.y!==0;let t=this.view===`free`||this.view===`firstPerson`?this.yaw:0,n=this.view===`side`?[e.x,0,0]:qv(e.x,-e.y,t),i=Math.hypot(n[0],n[2])||1;p=n[0]/i,m=n[2]/i}catch{}let g=Db(r,this.sprintAction),_=Jv(h,g),v=1+(this.sprintMultiplier-1)*_,y=h?this.maxSpeed*v:0,b=this.grounded?1:sb,x=(p*y-s[0])/ob*b,S=(m*y-s[2])/ob*b;if(t.applyImpulse([x*c,0,S*c]),!h&&this.grounded&&t.applyImpulse([-s[0]*cb*c,0,-s[2]*cb*c]),f!==null&&f<l*2){let e=lb*(l-f)-s[1]*ub;t.applyImpulse([0,e*c,0])}if(this.grounded&&Db(r,this.jumpAction)){let e=this.jumpVelocity*(1+(this.sprintJumpMultiplier-1)*_);t.linearVelocity=[s[0],e,s[2]],this.grounded=!1}s[1]<fb?t.gravityScale=0:s[1]<0&&!this.grounded?t.gravityScale=db:t.gravityScale=1,this.view===`side`&&Math.abs(o[2])>.01&&(t.position=[t.position[0]??0,t.position[1]??0,0]);let C=Yv(this.grounded,h?_:0);if(C!==this.state&&(this.state=C,this.emit(`movementStateChanged`,C)),h&&this.skinPath!==``){let t=this.getNodeOrNull(this.skinPath);if(t){let n=Math.atan2(p,m)*mb+this.skinYawOffset,r=t.rotation[1]??0,i=(n-r+540)%360-180,a=this.turnSpeed*e*mb;Math.abs(i)>a&&(i=Math.sign(i)*a),t.rotation=[t.rotation[0]??0,r+i,t.rotation[2]??0]}}}update(e){let t=this.tree?.engine?.input;if(!t)return;if(this.mouseLook){let e=t.pointerDelta();this.yaw-=e.x*pb,this.pitch=Tb(this.pitch+e.y*pb,-this.pitchMax,-this.pitchMin);let n=t.wheelDelta();n!==0&&this.zoomMax>this.zoomMin&&(this.camDistance=Tb(this.camDistance+n*pb,this.zoomMin,this.zoomMax))}let n=this.findCamera();if(!n)return;let r=this.parent instanceof ty?this.parent:null;if(!r)return;let i=Eb(r),a=this.view===`free`?11:1e3,o=1-Math.exp(-a*e),s=this.pivot??[i[0],i[1],i[2]];this.pivot=s,s[0]+=(i[0]-s[0])*o,s[1]+=(i[1]-s[1])*o,s[2]+=(i[2]-s[2])*o;let c=Wv(this.view,s,this.eyeHeight,this.yaw,this.pitch,this.camDistance),l=[s[0],s[1]+this.eyeHeight,s[2]],u=[c.position[0],c.position[1],c.position[2]];if(this.cameraCollision&&this.view!==`firstPerson`){let e=r._physics3d;e&&(u=Gv(l,u,(t,n,i)=>e.castRay(t,n,i,r,{staticOnly:!0})?.distance??null,hb,gb),u=Kv(u,(t,n)=>e.castRay(t,[0,-1,0],n,r,{staticOnly:!0})?.distance??null,_b))}let d=this.view===`firstPerson`?1e3:this.camLerp,f=1-Math.exp(-d*e),p=n.position;n.position=[(p[0]??0)+(u[0]-(p[0]??0))*f,(p[1]??0)+(u[1]-(p[1]??0))*f,(p[2]??0)+(u[2]-(p[2]??0))*f],this.view===`free`?(xb.set(n.position[0]??0,n.position[1]??0,n.position[2]??0),Sb.set(l[0],l[1],l[2]),bb.lookAt(xb,Sb,Cb),yb.setFromRotationMatrix(bb),vb.setFromQuaternion(yb,`XYZ`)):(vb.set(c.rotation[0],c.rotation[1],c.rotation[2],`YXZ`),yb.setFromEuler(vb),vb.setFromQuaternion(yb,`XYZ`)),n.rotation=[vb.x*mb,vb.y*mb,vb.z*mb]}pivot=null;cameraCache=null;findCamera(){if(this.cameraCache?.tree===this.tree&&this.cameraCache.current)return this.cameraCache;this.cameraCache=null;let e=this.tree?.root;if(!e)return null;let t=e=>{if(!this.cameraCache){if(e instanceof ab&&e.current){this.cameraCache=e;return}for(let n of e.children)t(n)}};return t(e),this.cameraCache}};function Tb(e,t,n){return Math.min(n,Math.max(t,e))}function Eb(e){let t=0,n=0,r=0;for(let i=e;i&&i instanceof Zv;i=i.parent)t+=i.position[0]??0,n+=i.position[1]??0,r+=i.position[2]??0;return[t,n,r]}function Db(e,t){try{return e.isPressed(t)}catch{return!1}}var Ob=.15,kb=.7,Ab=.5,jb=.7,Mb=.5;function Nb(e,t,n=3){let r=0,i=1,a=1,o=.65;for(let s=0;s<n;s++){let n=e*a,s=t*a,c=Math.sin(n*.1+s*.2)*.5+.5,l=Math.cos(n*.3+s*.1)*.5+.5,u=Math.sin((n+s)*.15)*.5+.5,d=Math.cos(n*n*.01+s*.2)*.5+.5,f=c*l*.7+u*d*.3;r+=f*i,i*=o,a*=2}return r/((1-o**n)/(1-o))}function Pb(e,t){let n=Nb(e*Ob,t*Ob,4),r=Nb(e*Ob*.2,t*Ob*.2,2);return n>Ab-kb*.5-r*Mb}var Fb=.9;function Ib(e,t,n){let r=(Math.imul(e,2246822507)^Math.imul(t,3266489909)^Math.imul(n+1,668265263))>>>0;return r=Math.imul(r^r>>>15,739982445)>>>0,r=Math.imul(r^r>>>12,695872825)>>>0,r^=r>>>15,(r>>>0)/4294967296}function Lb(e,t,n=Fb){let r=Math.floor(e/n),i=Math.floor(t/n),a=1/0,o=r,s=i;for(let c=-1;c<=1;c++)for(let l=-1;l<=1;l++){let u=r+l,d=i+c,f=(u+Ib(u,d,0))*n,p=(d+Ib(u,d,1))*n,m=(e-f)*(e-f)+(t-p)*(t-p);m<a&&(a=m,o=u,s=d)}return{id:Math.imul(o,2654435761)^Math.imul(s,2246822519)|0,hue:Ib(o,s,2)*2-1,leanDir:Ib(o,s,3)*Math.PI*2,lean:Ib(o,s,4)}}function Rb(e,t,n,r){let i=e*r,a=t*r,o=Math.floor(i),s=Math.floor(a),c=i-o,l=a-s,u=c*c*(3-2*c),d=l*l*(3-2*l),f=Ib(o,s,n),p=Ib(o+1,s,n),m=Ib(o,s+1,n),h=Ib(o+1,s+1,n),g=f+(p-f)*u;return g+(m+(h-m)*u-g)*d}var zb=[.55,1.7],Bb=.17,Vb=.23,Hb=.28;function Ub(e,t,n){let r=Rb(e,t,n^6877377|0,Bb),i=r*r*(3-2*r);return zb[0]+(zb[1]-zb[0])*i}function Wb(e,t,n){return Rb(e,t,n^3001105|0,Vb)}function Gb(e,t,n){let r=Rb(e,t,n^7840757|0,Hb)*.72+Rb(e,t,n^1688295|0,Hb*3.1)*.28,i=Math.min(1,Math.max(0,(r-.18)/.64));return i*i*(3-2*i)}function Kb(e){let{seed:t,areaX:n,areaZ:r,count:i,density:a}=e,o=new b(t),s=n/2,c=r/2,l=[],u=Math.floor(i*jb);for(let e=0;e<u&&l.length<i;e++){let e=o.range(-s,s),t=o.range(-c,c),n=o.next()<.3,r=Pb(e,t);if(!n&&!r&&o.next()>.15)continue;let i=o.next()*Math.PI*2,a=.5+o.next()*.7,u=r?a*(1+o.next()*.2):a*(.9+o.next()*.2);l.push({x:e,z:t,rotY:i,scale:u})}let d=Math.min(3,Math.max(1,a*10)),f=Math.ceil(Math.sqrt(i*d)),p=n/f,m=r/f,h=Math.floor((i-l.length)*.8),g=0;for(let e=0;e<f&&l.length<i;e++)for(let t=0;t<f&&l.length<i;t++){let i=t/f*n-s+p/2,a=e/f*r-c+m/2,u=i+(o.next()-.5)*p*1.5,d=a+(o.next()-.5)*m*1.5;if(u<-s||u>s||d<-c||d>c)continue;let _=Pb(u,d);if(g<h){if(!_&&o.next()>.05)continue}else if(_){if(o.next()>.2)continue}else if(o.next()>.4)continue;if(o.next()<.2)continue;let v=o.next()*Math.PI*2,y=_?.5:.3;l.push({x:u,z:d,rotY:v,scale:.5+o.next()*y}),g++}for(;l.length<i;)l.push({x:o.range(-s,s),z:o.range(-c,c),rotY:o.next()*Math.PI*2,scale:.4+o.next()*.6});return l.sort((e,t)=>t.z-e.z),l}var qb=[`daisy`,`cosmos`,`bellflower`],Jb={daisy:{petals:8,blooms:3,leaves:3,petalLength:.1,petalWidth:.052,cupDeg:16,nodDeg:14,centerRadius:.024,centerColor:`#e0a72e`},cosmos:{petals:8,blooms:2,leaves:2,petalLength:.12,petalWidth:.064,cupDeg:26,nodDeg:18,centerRadius:.023,centerColor:`#d2941f`},bellflower:{petals:5,blooms:3,leaves:2,petalLength:.088,petalWidth:.058,cupDeg:46,nodDeg:52,centerRadius:.015,centerColor:`#cfe06a`}},Yb=[1,.78,.62],Xb=[0,.07,.11],Zb=[.6,2.7,4.9],Qb=.07,$b=.011,ex=.17,tx=.05,nx=[.12,.2,.28],rx=new K(`#3e6a26`),ix=new K(`#548331`);function ax(e){let t=Jb[e],n=new ox(!0),r=new ox(!1),i=new K(t.centerColor);for(let e=0;e<t.blooms;e++){let a=Yb[e],o=Zb[e],s=Xb[e]*Math.cos(o),c=Xb[e]*Math.sin(o),l=Qb*Math.cos(o),u=Qb*Math.sin(o),d=new H(s,0,c),f=new H(s+l,a,c+u),p=new H(s+l*.25,a*.55,c+u*.25);n.addStem(d,p,f,$b,rx);let m=t.nodDeg*Math.PI/180,h=new H(Math.sin(m)*Math.cos(o),Math.cos(m),Math.sin(m)*Math.sin(o)),g=new H(-Math.sin(o),0,Math.cos(o)),_=new H().crossVectors(h,g).normalize(),v=f.clone().addScaledVector(h,.012),y=t.cupDeg*Math.PI/180,b=e*.45;for(let e=0;e<t.petals;e++){let n=e/t.petals*Math.PI*2+b,i=g.clone().multiplyScalar(Math.cos(n)).addScaledVector(_,Math.sin(n)).normalize();r.addPetal(v,i,h,t,y)}n.addDisc(v.clone().addScaledVector(h,.004),g,_,t.centerRadius,i)}for(let e=0;e<t.leaves;e++){let t=nx[e%nx.length],r=1.1+e*2.3;n.addLeaf(new H(0,t,0),r,ix)}return{structure:n.build(),petals:r.build()}}var ox=class{positions=[];colors;indices=[];constructor(e){this.colors=e?[]:null}vertex(e,t){let n=this.positions.length/3;return this.positions.push(e.x,e.y,e.z),this.colors&&t&&this.colors.push(t.r,t.g,t.b),n}addStem(e,t,n,r,i){for(let a of[0,Math.PI/2]){let o=new H(Math.cos(a),0,Math.sin(a)),s=[];for(let a=0;a<=5;a++){let c=a/5,l=1-c,u=new H(l*l*e.x+2*l*c*t.x+c*c*n.x,l*l*e.y+2*l*c*t.y+c*c*n.y,l*l*e.z+2*l*c*t.z+c*c*n.z),d=r/2*(1-.4*c);s.push(this.vertex(u.clone().addScaledVector(o,-d),i),this.vertex(u.clone().addScaledVector(o,d),i))}for(let e=0;e<5;e++){let t=s[e*2],n=s[e*2+1],r=s[e*2+2],i=s[e*2+3];this.indices.push(t,n,r,r,n,i)}}}addPetal(e,t,n,r,i){let a=new H().crossVectors(n,t).normalize(),o=r.centerRadius*.85,s=Math.sin(i)*r.petalLength*.45,c=Math.sin(i)*r.petalLength*.95,l=Math.cos(i),u=e.clone().addScaledVector(t,o),d=o+r.petalLength*l*.55,f=o+r.petalLength*l,p=e.clone().addScaledVector(t,d).addScaledVector(a,r.petalWidth/2).addScaledVector(n,s),m=e.clone().addScaledVector(t,d).addScaledVector(a,-r.petalWidth/2).addScaledVector(n,s),h=e.clone().addScaledVector(t,f).addScaledVector(n,c),g=this.vertex(u,null),_=this.vertex(p,null),v=this.vertex(m,null),y=this.vertex(h,null);this.indices.push(g,_,y,g,y,v)}addDisc(e,t,n,r,i){let a=this.vertex(e,i),o=[];for(let a=0;a<6;a++){let s=a/6*Math.PI*2;o.push(this.vertex(e.clone().addScaledVector(t,Math.cos(s)*r).addScaledVector(n,Math.sin(s)*r),i))}for(let e=0;e<6;e++)this.indices.push(a,o[e],o[(e+1)%6])}addLeaf(e,t,n){let r=new H(Math.cos(t),0,Math.sin(t)),i=new H(-Math.sin(t),0,Math.cos(t)),a=Math.sin(.6),o=Math.cos(.6),s=e.clone().addScaledVector(r,ex*o*.5).add(new H(0,ex*a*.5,0)),c=e.clone().addScaledVector(r,ex*o).add(new H(0,ex*a*.8,0)),l=this.vertex(e,n),u=this.vertex(s.clone().addScaledVector(i,tx/2),n),d=this.vertex(s.clone().addScaledVector(i,-.05/2),n),f=this.vertex(c,n);this.indices.push(l,u,f,l,f,d)}build(){let e=new Ha;return e.setAttribute(`position`,new q(new Float32Array(this.positions),3)),this.colors&&e.setAttribute(`color`,new q(new Float32Array(this.colors),3)),e.setIndex(this.indices),e.computeVertexNormals(),e}},sx=2.6;function cx(e){let{seed:t,areaX:n,areaZ:r,count:i,clustering:a}=e,o=new b(t),s=n/2,c=r/2,l=1-.74*Math.min(Math.max(a,0),1),u=[];for(let e=0;e<i*16&&u.length<i;e++){let e=o.range(-s,s),t=o.range(-c,c),n=Lb(e,t,sx);n.lean>l&&o.next()>.06||u.push({x:e,z:t,rotY:o.next()*Math.PI*2,scale:.75+o.next()*.5,variety01:(n.hue+1)/2,color01:n.leanDir/(Math.PI*2)})}return u}var lx=1.05,ux=1;function dx(e,t=ux){e.computeBoundingSphere();let n=e.boundingSphere;n&&n.radius>=0&&(n.radius=n.radius*lx+t),e.frustumCulled=!0}function fx(e){let t=0,n=0,r=0;for(let i=e;i;i=i.parent)i instanceof Zv&&(t+=i.position[0]??0,n+=i.position[1]??0,r+=i.position[2]??0);return[t,n,r]}function px(e,t){if(t!==``){let n=e.getNodeOrNull(t);return n instanceof Fy?n:null}return mx(e.getRoot())}function mx(e){if(e instanceof Fy)return e;for(let t of e.children){let e=mx(t);if(e)return e}return null}var hx={lush:2.2,sparse:.35,none:0},gx=[`lush`,`sparse`,`none`];function _x(e){if(typeof e==`number`)return e;let t=hx[e];if(t===void 0)throw new y(`BAD_FORMAT`,`Flowers3D density must be one of [${gx.join(`, `)}] or a number (plants/m²), got '${e}'.`,{prop:`density`,validOptions:gx});return t}var vx=1e4,yx=[`#f5f1e2`,`#e9d052`,`#8d7bd9`],bx=/^#[0-9a-fA-F]{6}$/,xx=class extends Zv{static typeName=`Flowers3D`;static props={density:{default:`sparse`,options:gx},area:{default:[20,20]},seed:{default:1},varieties:{default:[]},palette:{default:[]},height:{default:.45},clustering:{default:.6},sway:{default:.5},drape:{default:!1},terrain:{default:``}};density=`sparse`;area=[20,20];seed=1;varieties=[];palette=[];height=.45;clustering=.6;sway=.5;drape=!1;terrain=``;drapeTerrain=null;drapeWorld=[0,0,0];static validateJson(e){let t=e;if(typeof t.density==`string`)_x(t.density);else if(!(Number.isFinite(t.density)&&t.density>=0))throw new y(`BAD_FORMAT`,`Flowers3D '${e.name}' density must be one of [${gx.join(`, `)}] or a number >= 0 (plants/m²), got ${t.density}.`,{prop:`density`,validOptions:gx});if(!((t.area[0]??0)>0)||!((t.area[1]??0)>0))throw new y(`BAD_FORMAT`,`Flowers3D '${e.name}' area must be positive [x, z] meters, got ${JSON.stringify(t.area)}.`,{prop:`area`});let n=Math.floor((t.area[0]??0)*(t.area[1]??0)*_x(t.density));if(n>vx)throw new y(`BAD_FORMAT`,`Flowers3D '${e.name}' blows the plant budget: area ${t.area[0]}×${t.area[1]} m × density ${_x(t.density)} = ${n} plants > ${vx}. Lower the density or split the field into multiple nodes.`,{prop:`density`});if(!Array.isArray(t.varieties)||new Set(t.varieties).size!==t.varieties.length||!t.varieties.every(e=>qb.includes(e)))throw new y(`BAD_FORMAT`,`Flowers3D '${e.name}' varieties must be a unique subset of [${qb.join(`, `)}] ([] = all three), got ${JSON.stringify(t.varieties)}.`,{prop:`varieties`,validOptions:[...qb]});if(!Array.isArray(t.palette)||!t.palette.every(e=>typeof e==`string`&&bx.test(e)))throw new y(`BAD_FORMAT`,`Flowers3D '${e.name}' palette must be an array of '#rrggbb' head colors ([] = white/yellow/violet), got ${JSON.stringify(t.palette)}.`,{prop:`palette`});if(!(t.height>0))throw new y(`BAD_FORMAT`,`Flowers3D '${e.name}' height must be > 0 meters, got ${t.height}.`,{prop:`height`});if(!(t.clustering>=0)||!(t.clustering<=1))throw new y(`BAD_FORMAT`,`Flowers3D '${e.name}' clustering must be in [0, 1] (0 uniform … 1 tight patches), got ${t.clustering}.`,{prop:`clustering`});if(!(t.sway>=0))throw new y(`BAD_FORMAT`,`Flowers3D '${e.name}' sway must be >= 0, got ${t.sway}.`,{prop:`sway`});if(typeof t.drape!=`boolean`)throw new y(`BAD_FORMAT`,`Flowers3D '${e.name}' drape must be a boolean, got ${JSON.stringify(t.drape)}.`,{prop:`drape`});if(typeof t.terrain!=`string`)throw new y(`BAD_FORMAT`,`Flowers3D '${e.name}' terrain must be a node-path string (empty = auto-find), got ${JSON.stringify(t.terrain)}.`,{prop:`terrain`})}time=0;field=null;varietyMeshes=[];fieldKey=``;timeUniform={value:0};swayUniform={value:.5};_createObject3D(){return new Fi}_varietyMeshes(){return this.rebuildIfNeeded(),this.varietyMeshes}update(e){this.time+=e,this.timeUniform.value=this.time}_syncObject3D(){super._syncObject3D(),this.rebuildIfNeeded(),this.swayUniform.value=this.sway}rebuildIfNeeded(){this.field||(this.field=new Fi,this._ensureObject3D().add(this.field));let e=JSON.stringify([this.density,this.area,this.seed,this.varieties,this.palette,this.height,this.clustering,this.drape,this.terrain]);e!==this.fieldKey&&(this.fieldKey=e,this.disposeMeshes(),this.resolveDrape(),this.buildField())}resolveDrape(){if(this.drapeTerrain=null,!this.drape)return;let e=px(this,this.terrain);if(e)this.drapeTerrain=e,this.drapeWorld=fx(this);else if(this.terrain!==``)throw new y(`NODE_NOT_FOUND`,`Flowers3D '${this.name}' drape terrain '${this.terrain}' is not a Terrain3D in the tree.`,{prop:`terrain`})}bedY(e,t){let n=this.drapeTerrain;return n?n.heightAt(this.drapeWorld[0]+e,this.drapeWorld[2]+t)-this.drapeWorld[1]:0}buildField(){let e=_x(this.density),t=Math.min(Math.floor((this.area[0]??0)*(this.area[1]??0)*e),vx);if(t<=0)return;let n=cx({seed:this.seed,areaX:this.area[0]??0,areaZ:this.area[1]??0,count:t,clustering:this.clustering}),r=this.varieties.length>0?this.varieties:qb,i=r.map(()=>[]);for(let e of n)i[Math.min(Math.floor(e.variety01*r.length),r.length-1)].push(e);let a=this.palette.length>0?this.palette:yx,o=new b(this.seed+15797765);for(let e=0;e<r.length;e++){let t=i[e];if(t.length===0)continue;let{structure:n,petals:s}=ax(r[e]),c=new Bs({vertexColors:!0,roughness:1,side:2}),l=new Bs({roughness:.85,side:2});Sx(c,this.timeUniform,this.swayUniform),Sx(l,this.timeUniform,this.swayUniform);let u=new Vo(n,c,t.length),d=new Vo(s,l,t.length);u.receiveShadow=!0,d.receiveShadow=!0;for(let e=0;e<t.length;e++){let n=t[e];Tx.set(n.x,this.bedY(n.x,n.z),n.z),Ex.setFromAxisAngle(Cx,n.rotY),Dx.setScalar(this.height*n.scale),wx.compose(Tx,Ex,Dx),u.setMatrixAt(e,wx),d.setMatrixAt(e,wx);let r=Math.min(Math.floor(n.color01*a.length),a.length-1);Ox.set(a[r]).offsetHSL(o.range(-.02,.02),0,o.range(-.05,.05)),d.setColorAt(e,Ox),Ox.setRGB(1,1,1).offsetHSL(o.range(-.02,.02),0,o.range(-.06,.04)),u.setColorAt(e,Ox)}u.instanceMatrix.needsUpdate=!0,d.instanceMatrix.needsUpdate=!0,u.instanceColor&&(u.instanceColor.needsUpdate=!0),d.instanceColor&&(d.instanceColor.needsUpdate=!0),dx(u),dx(d),this.varietyMeshes.push({variety:r[e],structure:u,petals:d}),this.field.add(u),this.field.add(d)}}disposeMeshes(){for(let{structure:e,petals:t}of this.varietyMeshes)for(let n of[e,t])n.removeFromParent(),n.geometry.dispose(),n.material.dispose(),n.dispose();this.varietyMeshes=[]}free(){this.disposeMeshes(),this.field=null,super.free()}};function Sx(e,t,n){e.userData.sway=!0,e.userData.timeUniform=t,e.onBeforeCompile=e=>{e.uniforms.uFlowerTime=t,e.uniforms.uFlowerSway=n,e.vertexShader=`uniform float uFlowerTime;\nuniform float uFlowerSway;\n${e.vertexShader}`.replace(`#include <project_vertex>`,`
|
|
5301
|
+
`;function yy(e,t){for(let n=0;n<4;n++){let r=n+1;e.uniforms[`splatTexture${r}`]={value:t.textures[n]??t.textures[0]??null},e.uniforms[`splatNormalMap${r}`]={value:t.normalMaps[n]??null},e.uniforms[`splatTextureRepeat${r}`]={value:t.repeats[n]??1},e.uniforms[`splatRoughness${r}`]={value:t.roughness[n]??1},e.uniforms[`splatMetalness${r}`]={value:t.metalness[n]??0},e.uniforms[`uvNoiseIntensity${r}`]={value:t.uvNoiseIntensity[n]??.15}}e.uniforms.splatTextureCount={value:Math.min(4,t.layerCount)},e.uniforms.splatEnvCut={value:t.envCut},e.vertexShader=uy(e.vertexShader,`#include <common>`,dy),e.vertexShader=uy(e.vertexShader,`#include <begin_vertex>`,fy);let n=e.fragmentShader;n=uy(n,`#include <common>`,py),n=uy(n,`#include <color_fragment>`,my),n=uy(n,`#include <roughnessmap_fragment>`,hy),n=uy(n,`#include <metalnessmap_fragment>`,_y),n=uy(n,`#include <normal_fragment_maps>`,vy),n=uy(n,`#include <lights_fragment_maps>`,gy),t.hasNormalMaps&&(n=`#define USE_TERRAIN_NORMALMAP\n${n}`),e.fragmentShader=n}var by=[`island`,`alpine`,`plains`,`desert`,`grassland`,`forest`,`savanna`,`snow`,`wetland`,`volcanic`,`custom`],xy=`https://agent8-games.verse8.io/assets/3D/default/textures/terrain`,Sy=`https://agent8-games.verse8.io/assets/3D/default/textures/vegetation/ground`,Cy=1/3,wy=Math.PI/4;function Ty(e,t,n){return{texture:`${e}/${t}.png`,normalMap:`${e}/${t}_normal.png`,...n}}function Ey(e,t){let n=t.replace(/\/$/,``);switch(e){case`island`:return[Ty(n,`sand`,{heightRange:[0,.12],slopeRange:[0,Math.PI/2],heightBlendRange:.05,roughness:.9}),Ty(n,`grass`,{heightRange:[.1,.7],slopeRange:[0,wy],heightBlendRange:.1,slopeBlendRange:.1,roughness:.8}),Ty(n,`stone`,{heightRange:[0,1],slopeRange:[wy,Math.PI],heightBlendRange:.5,slopeBlendRange:.5,roughness:.85}),Ty(n,`snow`,{heightRange:[.8,1],slopeRange:[0,Math.PI/3],heightBlendRange:.1,roughness:.6})];case`alpine`:return[Ty(n,`grass`,{heightRange:[0,.45],slopeRange:[0,Math.PI/5],heightBlendRange:.1,roughness:.8}),Ty(n,`stone`,{heightRange:[.3,1],slopeRange:[0,Math.PI],heightBlendRange:.25,slopeBlendRange:.5,roughness:.85}),Ty(n,`gravel`,{heightRange:[0,1],slopeRange:[Math.PI/5,Math.PI],heightBlendRange:.5,slopeBlendRange:.15,roughness:.9}),Ty(n,`snow`,{heightRange:[.55,1],slopeRange:[0,Math.PI/3],heightBlendRange:.12,roughness:.55})];case`plains`:return[Ty(n,`grass`,{heightRange:[0,1],slopeRange:[0,wy],heightBlendRange:.1,slopeBlendRange:.1,roughness:.8}),Ty(n,`dirt`,{heightRange:[.35,.65],slopeRange:[0,Math.PI/5],heightBlendRange:.2,roughness:.9}),Ty(n,`gravel`,{heightRange:[0,1],slopeRange:[wy,Math.PI],heightBlendRange:.5,slopeBlendRange:.3,roughness:.9})];case`desert`:return[Ty(n,`sand`,{heightRange:[0,1],slopeRange:[0,wy],heightBlendRange:.1,slopeBlendRange:.15,roughness:.95}),Ty(n,`gravel`,{heightRange:[0,.15],slopeRange:[0,Math.PI/3],heightBlendRange:.08,roughness:.9}),Ty(n,`stone`,{heightRange:[0,1],slopeRange:[wy,Math.PI],heightBlendRange:.5,slopeBlendRange:.4,roughness:.85})];case`grassland`:return n===`https://agent8-games.verse8.io/assets/3D/default/textures/terrain`?[{texture:`${Sy}/grass.jpg`,heightRange:[0,1],slopeRange:[0,wy],heightBlendRange:.1,slopeBlendRange:.15,roughness:1,repeat:Cy,uvNoiseIntensity:0},{texture:`${Sy}/dirt_color.jpg`,heightRange:[0,1],slopeRange:[wy,Math.PI],heightBlendRange:.5,slopeBlendRange:.3,roughness:1,repeat:Cy,uvNoiseIntensity:0}]:[Ty(n,`grass`,{heightRange:[0,1],slopeRange:[0,wy],heightBlendRange:.1,slopeBlendRange:.15,roughness:.8}),Ty(n,`dirt`,{heightRange:[0,1],slopeRange:[wy,Math.PI],heightBlendRange:.5,slopeBlendRange:.3,roughness:.9})];case`forest`:return n===`https://agent8-games.verse8.io/assets/3D/default/textures/terrain`?[{texture:`${Sy}/dirt_color.jpg`,heightRange:[0,1],slopeRange:[0,Math.PI],heightBlendRange:.1,roughness:1,repeat:Cy,uvNoiseIntensity:0},{texture:`${Sy}/grass.jpg`,heightRange:[-2,-1],roughness:1,repeat:Cy,uvNoiseIntensity:0}]:[Ty(n,`dirt`,{heightRange:[0,1],slopeRange:[0,Math.PI],heightBlendRange:.1,roughness:.9}),Ty(n,`grass`,{heightRange:[-2,-1],roughness:.8})];case`savanna`:return[Ty(n,`grass`,{heightRange:[0,1],slopeRange:[0,wy],heightBlendRange:.1,slopeBlendRange:.15,roughness:.95}),Ty(n,`sand`,{heightRange:[0,.22],slopeRange:[0,Math.PI/4],heightBlendRange:.12,roughness:.95}),Ty(n,`dirt`,{heightRange:[0,1],slopeRange:[wy,Math.PI],heightBlendRange:.5,slopeBlendRange:.3,roughness:.95})];case`snow`:return[Ty(n,`snow`,{heightRange:[0,1],slopeRange:[0,wy],heightBlendRange:.1,slopeBlendRange:.18,roughness:.55}),Ty(n,`stone`,{heightRange:[0,1],slopeRange:[wy,Math.PI],heightBlendRange:.5,slopeBlendRange:.35,roughness:.85})];case`wetland`:return[Ty(n,`grass`,{heightRange:[0,1],slopeRange:[0,wy],heightBlendRange:.1,slopeBlendRange:.15,roughness:.95}),Ty(n,`dirt`,{heightRange:[0,1],slopeRange:[wy,Math.PI],heightBlendRange:.5,slopeBlendRange:.3,roughness:.95})];case`volcanic`:return[Ty(n,`stone`,{heightRange:[0,1],slopeRange:[0,Math.PI],heightBlendRange:.1,roughness:.95}),Ty(n,`gravel`,{heightRange:[0,.28],slopeRange:[0,Math.PI/3],heightBlendRange:.14,slopeBlendRange:.2,roughness:.95})];case`custom`:return[]}}var Dy={scale:100,threshold:.7,blend:.1},Oy={scale:55,threshold:.58,blend:.12},ky={scale:85,threshold:.62,blend:.12},Ay={scale:48,threshold:.5,blend:.14};function jy(e,t){let n=Be(e+27927);return(e,r)=>{let i=(.5+.5*n(e/t.scale,r/t.scale)-(t.threshold-t.blend))/(2*t.blend),a=Math.min(1,Math.max(0,i));return a*a*(3-2*a)}}function My(e,t){return e===`grassland`?jy(t,Dy):e===`forest`?jy(t,Oy):e===`savanna`?jy(t,ky):e===`wetland`?jy(t,Ay):null}function Ny(e){return e===`savanna`?{from:0,to:2}:e===`grassland`||e===`forest`||e===`wetland`?{from:0,to:1}:null}var Py=128,Fy=class extends Zv{static typeName=`Terrain3D`;static props={size:{default:[200,200]},maxHeight:{default:28},seed:{default:1},resolution:{default:128},theme:{default:`island`,options:by},roughness:{default:.5},detail:{default:4},flatThreshold:{default:.95},islandEdge:{default:null},layers:{default:[]},textureBase:{default:xy},basins:{default:[]}};size=[200,200];maxHeight=28;seed=1;resolution=128;theme=`island`;roughness=.5;detail=4;flatThreshold=.95;islandEdge=null;layers=[];textureBase=xy;basins=[];static validateJson(e){let t=e;if(!by.includes(t.theme))throw new y(`BAD_FORMAT`,`Terrain3D '${e.name}' theme must be one of [${by.join(`, `)}], got '${t.theme}'.`,{prop:`theme`,validOptions:by});if(!((t.size[0]??0)>0)||!((t.size[1]??0)>0))throw new y(`BAD_FORMAT`,`Terrain3D '${e.name}' size must be positive [width, depth] meters, got ${JSON.stringify(t.size)}.`,{prop:`size`});if(!(t.maxHeight>0))throw new y(`BAD_FORMAT`,`Terrain3D '${e.name}' maxHeight must be > 0, got ${t.maxHeight}.`,{prop:`maxHeight`});if(!Number.isInteger(t.resolution)||t.resolution<2||t.resolution>Py)throw new y(`BAD_FORMAT`,`Terrain3D '${e.name}' resolution must be an integer in [2, ${Py}] (grid segments per side), got ${t.resolution}.`,{prop:`resolution`});if(!(t.roughness>0)||!Number.isFinite(t.roughness))throw new y(`BAD_FORMAT`,`Terrain3D '${e.name}' roughness must be a finite number > 0, got ${t.roughness}.`,{prop:`roughness`});if(!Number.isInteger(t.detail)||t.detail<0)throw new y(`BAD_FORMAT`,`Terrain3D '${e.name}' detail must be an integer ≥ 0 (octave count), got ${t.detail}.`,{prop:`detail`});if(!(t.flatThreshold>=0)||!Number.isFinite(t.flatThreshold))throw new y(`BAD_FORMAT`,`Terrain3D '${e.name}' flatThreshold must be a finite number ≥ 0, got ${t.flatThreshold}.`,{prop:`flatThreshold`});if(t.islandEdge!==null&&typeof t.islandEdge!=`boolean`)throw new y(`BAD_FORMAT`,`Terrain3D '${e.name}' islandEdge must be true, false or null (null = theme default), got ${JSON.stringify(t.islandEdge)}.`,{prop:`islandEdge`});if(!Array.isArray(t.basins))throw new y(`BAD_FORMAT`,`Terrain3D '${e.name}' basins must be an array of { x, z, radius, depth }, got ${JSON.stringify(t.basins)}.`,{prop:`basins`});if(t.basins.forEach((t,n)=>{if(!t||typeof t!=`object`||!Number.isFinite(t.x)||!Number.isFinite(t.z)||!(t.radius>0)||!(t.depth>0))throw new y(`BAD_FORMAT`,`Terrain3D '${e.name}' basins[${n}] needs finite x, z and positive radius, depth, got ${JSON.stringify(t)}.`,{prop:`basins`})}),t.theme===`custom`){if(!Array.isArray(t.layers)||t.layers.length<1||t.layers.length>4)throw new y(`BAD_FORMAT`,`Terrain3D '${e.name}' theme 'custom' needs 1–4 layers, got ${Array.isArray(t.layers)?t.layers.length:typeof t.layers}.`,{prop:`layers`});t.layers.forEach((t,n)=>{Ry(e.name,t,n)})}else if(Array.isArray(t.layers)&&t.layers.length>0)throw new y(`BAD_FORMAT`,`Terrain3D '${e.name}' layers is only read with theme 'custom' — preset theme '${t.theme}' defines its own. Drop layers or switch to theme: 'custom'.`,{prop:`layers`})}heightmap=null;heightmapKey=``;builtKey=``;loadedTextures=[];_createObject3D(){let e=new Ha;return e.setAttribute(`position`,new q(new Float32Array,3)),new ho(e,new Bs({vertexColors:!0}))}heightAt(e,t){let n=0,r=0,i=0;for(let e=this;e;e=e.parent)e instanceof Zv&&(n+=e.position[0]??0,r+=e.position[1]??0,i+=e.position[2]??0);return this._heightmap().heightAt(e-n,t-i)+r}_heightmap(){let e=this.segments(),t=JSON.stringify([this.size,this.maxHeight,this.seed,e,this.roughness,this.detail,this.flatThreshold,this.effectiveIslandEdge(),this.basins]);return(!this.heightmap||t!==this.heightmapKey)&&(this.heightmap=sy({width:this.size[0]??1,depth:this.size[1]??1,segsX:e,segsZ:e,maxHeight:this.maxHeight,seed:this.seed,roughness:this.roughness,detail:this.detail,flatThreshold:this.flatThreshold,islandEdge:this.effectiveIslandEdge(),basins:this.basins}),this.heightmapKey=t),this.heightmap}resolvedLayers(){return this.theme===`custom`?this.layers:Ey(this.theme,this.textureBase)}_syncObject3D(){super._syncObject3D(),this.rebuildIfNeeded()}effectiveIslandEdge(){return this.islandEdge??this.theme===`island`}segments(){return Math.max(2,Math.min(Math.floor(this.resolution),Py))}rebuildIfNeeded(){let e=JSON.stringify([this.size,this.maxHeight,this.seed,this.segments(),this.theme,this.roughness,this.detail,this.flatThreshold,this.islandEdge,this.layers,this.textureBase,this.basins]);if(e===this.builtKey)return;this.builtKey=e;let t=this._ensureObject3D();t.geometry.dispose(),t.geometry=this.buildGeometry(),t.material.dispose(),this.disposeTextures(),t.material=this.buildMaterial(),t.receiveShadow=!0,t.castShadow=!0}buildGeometry(){let e=this._heightmap(),t=new Os(e.width,e.depth,e.segsX,e.segsZ);t.rotateX(-Math.PI/2);let n=t.getAttribute(`position`);for(let t=0;t<n.count;t++)n.setY(t,e.heights[t]);n.needsUpdate=!0,t.computeVertexNormals();let r=this.resolvedLayers(),i=e.maxHeight-e.minHeight||1,a=new Float32Array(n.count*3),o=this.theme===`custom`?null:Ny(this.theme),s=o?My(this.theme,this.seed):null;for(let t=0;t<n.count;t++){let c=n.getX(t),l=n.getZ(t),u=ly(Math.min(1,Math.max(0,(n.getY(t)-e.minHeight)/i)),e.slopeAt(c,l),r);if(s&&o){let e=(u[o.from]??0)*s(c,l);u[o.from]=(u[o.from]??0)-e,u[o.to]=(u[o.to]??0)+e}a[t*3]=u[0],a[t*3+1]=u[1],a[t*3+2]=u[2]}return t.setAttribute(`color`,new q(a,3)),t}buildMaterial(){let e=this.resolvedLayers(),t=new Bs({vertexColors:!0}),n=typeof document<`u`?new bc:null,r=(e,t)=>{if(!n||!e)return null;let r=n.load(e);return r.wrapS=bt,r.wrapT=bt,r.anisotropy=8,t&&(r.colorSpace=Yn),this.loadedTextures.push(r),r},i=e.map(e=>r(e.texture,!0)),a=e.map(e=>r(e.normalMap,!1)),o=a.some(e=>e!==null);if(o)for(let e=0;e<a.length;e++)a[e]||(a[e]=Ly());let s={textures:i,normalMaps:a,repeats:e.map(e=>e.repeat??2),roughness:e.map(e=>e.roughness??1),metalness:e.map(e=>e.metalness??0),uvNoiseIntensity:e.map(e=>e.uvNoiseIntensity??.15),envCut:.45,layerCount:Math.min(4,e.length),hasNormalMaps:o};return t.onBeforeCompile=e=>{yy(e,s)},t.customProgramCacheKey=()=>`incanto-terrain-splat:${+!!o}`,t}disposeTextures(){for(let e of this.loadedTextures)e.dispose();this.loadedTextures=[]}free(){let e=this._ensureObject3D();e.geometry.dispose(),e.material.dispose(),this.disposeTextures(),this.heightmap=null,super.free()}},Iy=null;function Ly(){return Iy||(Iy=new ko(new Uint8Array([128,128,255,255]),1,1),Iy.needsUpdate=!0),Iy}function Ry(e,t,n){let r=`layers[${n}]`;if(typeof t!=`object`||!t||Array.isArray(t))throw new y(`BAD_FORMAT`,`Terrain3D '${e}' ${r} must be an object ({texture, heightRange?, slopeRange?, …}).`,{prop:`layers`});let i=t;if(typeof i.texture!=`string`||i.texture.length===0)throw new y(`BAD_FORMAT`,`Terrain3D '${e}' ${r} needs "texture": a texture URL string.`,{prop:`layers`});for(let t of[`heightRange`,`slopeRange`]){let n=i[t];if(n!==void 0&&(!Array.isArray(n)||n.length!==2||n.some(e=>typeof e!=`number`||!Number.isFinite(e))))throw new y(`BAD_FORMAT`,`Terrain3D '${e}' ${r}.${t} must be [min, max] numbers, got ${JSON.stringify(n)}.`,{prop:`layers`})}}var zy=Math.PI/180;async function By(e,t){let n=await Xf(()=>import(`./rapier-DE6a0vmv.js`),[],import.meta.url);return await n.init(),new Vy(n,e,t)}var Vy=class{R;engine;debugDraw=!1;dimension=`3d`;unregisterDebug;warnedNoCollider=new WeakSet;entries=new Map;byColliderHandle=new Map;world;events;kcc;disconnect;lastDt=1/60;optsGravity;lastScene=null;constructor(e,t,n){this.R=e,this.engine=t,this.optsGravity=n?.gravity,this.lastScene=t.scene;let r=t.scene?.physics?.gravity,i=n?.gravity??r??[0,-9.81,0];this.world=new e.World({x:i[0]??0,y:i[1]??0,z:i[2]??0}),this.kcc=this.world.createCharacterController(.01),this.events=new e.EventQueue(!0),this.unregisterDebug=Gf(this),this.disconnect=t.fixedUpdated.connect(e=>this.step(e)),this.syncBodies()}step(e){if(this.lastDt=e,this.engine.scene!==this.lastScene&&(this.lastScene=this.engine.scene,!this.optsGravity)){let e=this.engine.scene?.physics?.gravity??[0,-9.81,0];this.world.gravity={x:e[0]??0,y:e[1]??0,z:e[2]??0}}this.syncBodies(),this.world.timestep=e,this.world.step(this.events);for(let[e,t]of this.entries){let n=t.body.translation();if(!t.body.isFixed()){let[t,r,i]=Ky(e),a=[n.x-t,n.y-r,n.z-i];e._interpPrev=e._interpCurr??a,e._interpCurr=a,e.position=[a[0],a[1],a[2]]}if(e instanceof ty){if(!e.fixedRotation){let n=t.body.rotation();Uy.setFromQuaternion(Hy.set(n.x,n.y,n.z,n.w),`XYZ`),e.rotation=[Uy.x/zy,Uy.y/zy,Uy.z/zy]}let n=t.body.linvel();e.linearVelocity=[n.x,n.y,n.z],t.lastV=[n.x,n.y,n.z]}t.last=Gy(e)}this.events.drainCollisionEvents((e,t,n)=>{let r=this.byColliderHandle.get(e),i=this.byColliderHandle.get(t);if(!r||!i)return;let a=n?`triggerEnter`:`triggerExit`;r.emit(a,i),i.emit(a,r)})}moveAndSlide(e){let t=this.ensureEntry(e),n=e.velocity[1]??0;e.snapToGround&&n<=0?this.kcc.enableSnapToGround(.1):this.kcc.disableSnapToGround(),this.kcc.setMaxSlopeClimbAngle(e.slopeLimitDeg*zy);let r={x:(e.velocity[0]??0)*this.lastDt,y:n*this.lastDt,z:(e.velocity[2]??0)*this.lastDt};this.kcc.computeColliderMovement(t.collider,r,this.R.QueryFilterFlags.EXCLUDE_SENSORS);let i=this.kcc.computedMovement(),a=t.body.translation();t.body.setNextKinematicTranslation({x:a.x+i.x,y:a.y+i.y,z:a.z+i.z}),e._grounded=this.kcc.computedGrounded()}debugLines(){return this.debugDraw?this.world.debugRender().vertices:null}dispose(){this.disconnect(),this.unregisterDebug(),this.world.free()}syncBodies(){let e=this.engine.scene;if(!e)return;let t=new Set;Wy(e.root,t);for(let e of t){if(e.collider.shape===void 0){let t=this.entries.get(e);t&&(this.byColliderHandle.delete(t.collider.handle),this.world.removeRigidBody(t.body),this.entries.delete(e),e._physics=null,e._interpPrev=e._interpCurr=null),this.warnedNoCollider.has(e)||(this.warnedNoCollider.add(e),console.warn(`[incanto] '${e.name}' has no collider — physics skips it.`));continue}let t=this.entries.get(e);t&&t.colliderKey!==JSON.stringify(e.collider)&&(this.byColliderHandle.delete(t.collider.handle),this.world.removeRigidBody(t.body),this.entries.delete(e),e._physics=null,e._interpPrev=e._interpCurr=null),this.ensureEntry(e)}for(let[e,n]of this.entries)t.has(e)||(this.byColliderHandle.delete(n.collider.handle),this.world.removeRigidBody(n.body),this.entries.delete(e),e._physics=null,e._interpPrev=e._interpCurr=null)}ensureEntry(e){let t=this.entries.get(e),n=this.R;if(!t){let r;r=e instanceof ty?n.RigidBodyDesc.dynamic():e instanceof ny||e instanceof ey?n.RigidBodyDesc.kinematicPositionBased():n.RigidBodyDesc.fixed();let[i,a,o]=Gy(e);r.setTranslation(i,a,o),Hy.setFromEuler(Uy.set((e.rotation[0]??0)*zy,(e.rotation[1]??0)*zy,(e.rotation[2]??0)*zy,`XYZ`)),r.setRotation({x:Hy.x,y:Hy.y,z:Hy.z,w:Hy.w}),e instanceof ty&&(r.setGravityScale(e.gravityScale),r.setLinvel(e.linearVelocity[0]??0,e.linearVelocity[1]??0,e.linearVelocity[2]??0),e.fixedRotation&&r.lockRotations());let s=this.world.createRigidBody(r),c=qy(n,e);c.setActiveEvents(n.ActiveEvents.COLLISION_EVENTS),c.setActiveCollisionTypes(n.ActiveCollisionTypes.ALL),e instanceof ey&&c.setSensor(!0),e instanceof ty&&c.setMass(e.mass).setFriction(e.friction).setRestitution(e.restitution);let l=this.world.createCollider(c,s);return t={colliderKey:JSON.stringify(e.collider),body:s,collider:l,last:[i,a,o],lastV:[0,0,0]},this.entries.set(e,t),this.byColliderHandle.set(l.handle,e),e._physics=this,e instanceof ty&&(e._physics3d=this),t}let r=Gy(e);if((r[0]!==t.last[0]||r[1]!==t.last[1]||r[2]!==t.last[2])&&(t.body.setTranslation({x:r[0],y:r[1],z:r[2]},!0),t.last=r,e._interpPrev=e._interpCurr=null),e instanceof ty){let n=[e.linearVelocity[0]??0,e.linearVelocity[1]??0,e.linearVelocity[2]??0];(n[0]!==t.lastV[0]||n[1]!==t.lastV[1]||n[2]!==t.lastV[2])&&(t.body.setLinvel({x:n[0],y:n[1],z:n[2]},!0),t.lastV=n),e.gravityScale!==t.body.gravityScale()&&t.body.setGravityScale(e.gravityScale,!0)}return t}applyImpulse(e,t){this.entries.get(e)?.body.applyImpulse({x:t[0],y:t[1],z:t[2]},!0)}massOf(e){return this.entries.get(e)?.body.mass()??1}velocityOf(e){let t=this.entries.get(e)?.body.linvel();return t?[t.x,t.y,t.z]:[0,0,0]}castRay(e,t,n,r,i){let a=new this.R.Ray({x:e[0],y:e[1],z:e[2]},{x:t[0],y:t[1],z:t[2]}),o=r?this.entries.get(r)?.collider:void 0,s=i?.staticOnly?this.R.QueryFilterFlags.EXCLUDE_DYNAMIC|this.R.QueryFilterFlags.EXCLUDE_KINEMATIC:void 0,c=this.world.castRayAndGetNormal(a,n,!0,s,void 0,o,void 0,e=>!e.isSensor());return c?{distance:c.timeOfImpact,normal:[c.normal.x,c.normal.y,c.normal.z],node:this.byColliderHandle.get(c.collider.handle)??null}:null}},Hy=new V,Uy=new _i;function Wy(e,t){e instanceof Qv&&t.add(e);for(let n of e.children)Wy(n,t)}function Gy(e){let t=0,n=0,r=0;for(let i=e;i;i=i.parent)i instanceof Zv&&(t+=i.position[0]??0,n+=i.position[1]??0,r+=i.position[2]??0);return[t,n,r]}function Ky(e){let t=0,n=0,r=0;for(let i=e.parent;i;i=i.parent)i instanceof Zv&&(t+=i.position[0]??0,n+=i.position[1]??0,r+=i.position[2]??0);return[t,n,r]}function qy(e,t){let n=t.collider;if(Xv(n,t.name),n.shape===`heightfield`)return Jy(e,t);let r=Yy(e,n,t.name),i=n.offset;return Array.isArray(i)&&r.setTranslation(i[0]??0,i[1]??0,i[2]??0),r}function Jy(e,t){let n=t.children.find(e=>e instanceof Fy);if(!n)throw new y(`BAD_FORMAT`,`Collider on '${t.name}': heightfield needs a Terrain3D CHILD of this body — add { "type": "Terrain3D" } under the StaticBody3D; the collider reads its height grid.`,{prop:`collider`});let r=n._heightmap(),i=r.segsZ,a=r.segsX,o=new Float32Array((i+1)*(a+1));for(let e=0;e<=a;e++)for(let t=0;t<=i;t++)o[e*(i+1)+t]=r.heights[t*(a+1)+e];let s=e.ColliderDesc.heightfield(i,a,o,{x:r.width,y:1,z:r.depth}),c=Array.isArray(t.collider.offset)?t.collider.offset:[];return s.setTranslation((n.position[0]??0)+(c[0]??0),(n.position[1]??0)+(c[1]??0),(n.position[2]??0)+(c[2]??0)),s}function Yy(e,t,n){let r=t.shape;if(r===`box`){let r=t.size;if(!Array.isArray(r)||r.length!==3||r.some(e=>typeof e!=`number`))throw new y(`BAD_FORMAT`,`Collider on '${n}': box needs "size": [x, y, z].`);return e.ColliderDesc.cuboid(r[0]/2,r[1]/2,r[2]/2)}if(r===`sphere`){if(typeof t.radius!=`number`)throw new y(`BAD_FORMAT`,`Collider on '${n}': sphere needs "radius".`);return e.ColliderDesc.ball(t.radius)}if(r===`capsule`){if(typeof t.radius!=`number`||typeof t.height!=`number`)throw new y(`BAD_FORMAT`,`Collider on '${n}': capsule needs "radius" and "height".`);return e.ColliderDesc.capsule(t.height/2,t.radius)}if(r===`trimesh`){let r=t.vertices,i=t.indices;if(!Array.isArray(r)||!Array.isArray(i))throw new y(`BAD_FORMAT`,`Collider on '${n}': trimesh needs "vertices" (flat xyz) and "indices" (triangles).`);return e.ColliderDesc.trimesh(new Float32Array(r),new Uint32Array(i))}throw new y(`BAD_FORMAT`,`Collider on '${n}': "shape" must be 'box', 'sphere', 'capsule', 'trimesh', or 'heightfield', got ${JSON.stringify(r)}.`)}var Xy=new Os(1,1),Zy=null;function Qy(){if(Zy)return Zy;if(typeof document>`u`)return null;let e=document.createElement(`canvas`);e.width=64,e.height=64;let t=e.getContext(`2d`);if(!t)return null;let n=t.createRadialGradient(32,32,0,32,32,31);return n.addColorStop(0,`rgba(0,0,0,0.55)`),n.addColorStop(.55,`rgba(0,0,0,0.34)`),n.addColorStop(1,`rgba(0,0,0,0)`),t.fillStyle=n,t.fillRect(0,0,64,64),Zy=new vs(e),Zy}var $y=new H(0,0,1),eb=new H,tb=new H,nb=new V,rb=new V,ib=new H,ab=.5,ob=class extends Zv{static typeName=`Sprite3D`;static props={texture:{default:``},size:{default:[1,1]},billboard:{default:`y`,options:[`y`,`full`,`none`]},anchor:{default:[.5,0]},tint:{default:`#ffffff`},opacity:{default:1},flipX:{default:!1},pixelArt:{default:!0},alphaTest:{default:.5},castShadow:{default:!1},shadowMode:{default:`silhouette`,options:[`silhouette`,`blob`]}};texture=``;size=[1,1];billboard=`y`;anchor=[.5,0];tint=`#ffffff`;opacity=1;flipX=!1;pixelArt=!0;alphaTest=.5;castShadow=!1;shadowMode=`silhouette`;quadMesh=null;textureRef=``;loadedTexture=null;shadowMesh=null;blobMesh=null;_quad(){return this.quadMesh||(this.quadMesh=new ho(Xy,new no({transparent:!1,depthWrite:!0,side:2})),this._ensureObject3D().add(this.quadMesh)),this.quadMesh}resolveTexture(){if(!this.texture)return null;if(this.textureRef!==this.texture&&(this.loadedTexture?.dispose(),this.loadedTexture=null,this.textureRef=this.texture,typeof document<`u`)){let e=new bc().load(this.texture);e.colorSpace=Yn;let t=this.pixelArt?Ct:Et;e.magFilter=t,e.minFilter=t,this.loadedTexture=e}return this.loadedTexture}_syncObject3D(e=1){super._syncObject3D(e);let t=this._quad(),n=t.material,r=this.size[0]??1,i=this.size[1]??1;t.scale.set(this.flipX?-r:r,i,1);let a=this.anchor[0]??.5,o=this.anchor[1]??0;t.position.set((.5-a)*r,(.5-o)*i,0);let s=this.resolveTexture();n.map!==s&&(n.map=s,n.needsUpdate=!0),t.visible=s!==null,n.color.set(this.tint),n.opacity=this.opacity,n.transparent=this.opacity<1,n.alphaTest=this.alphaTest,t.renderOrder=this.renderOrder;let c=this.castShadow&&s!==null;if(t.castShadow=!1,c&&this.shadowMode===`silhouette`){let e=this._shadowCaster();e.visible=!0,e.castShadow=!0,e.scale.set(this.flipX?-r:r,i,1),e.position.set((.5-a)*r,(.5-o)*i,0);let t=e.material;t.map!==s&&(t.map=s,t.needsUpdate=!0);let n=e.customDepthMaterial;n.map!==s&&(n.map=s,n.needsUpdate=!0),t.alphaTest=this.alphaTest,n.alphaTest=this.alphaTest}else this.shadowMesh&&(this.shadowMesh.visible=!1,this.shadowMesh.castShadow=!1);if(c&&this.shadowMode===`blob`){let e=this._blob();e.visible=!0,e.scale.set(r*.52,r*.52,1)}else this.blobMesh&&(this.blobMesh.visible=!1)}_shadowCaster(){return this.shadowMesh||(this.shadowMesh=new ho(Xy,new no({colorWrite:!1,depthWrite:!1,side:2})),this.shadowMesh.customDepthMaterial=new Hs({depthPacking:Jn,side:2}),this._ensureObject3D().add(this.shadowMesh)),this.shadowMesh}_blob(){return this.blobMesh||(this.blobMesh=new ho(Xy,new no({map:Qy(),color:0,transparent:!0,depthWrite:!1})),this.blobMesh.rotation.x=-Math.PI/2,this.blobMesh.position.y=.02,this.blobMesh.renderOrder=-1,this._ensureObject3D().add(this.blobMesh)),this.blobMesh}_onRender3D(e){this.billboard!==`none`&&this._faceToward(this._quad(),e.camera.position);let t=this.shadowMesh;t?.castShadow&&e.sunDir&&(this._faceDir(t,e.sunDir),t.parent?.getWorldQuaternion(rb),ib.copy(e.sunDir).applyQuaternion(rb.invert()),t.position.addScaledVector(ib,ab))}_faceToward(e,t){e.getWorldPosition(eb),tb.copy(t).sub(eb),this.billboard===`y`&&(tb.y=0),!(tb.lengthSq()<1e-12)&&(tb.normalize(),nb.setFromUnitVectors($y,tb),e.parent?.getWorldQuaternion(rb),e.quaternion.copy(rb.invert()).multiply(nb))}_faceDir(e,t){tb.copy(t),this.billboard===`y`&&(tb.y=0),!(tb.lengthSq()<1e-12)&&(tb.normalize(),nb.setFromUnitVectors($y,tb),e.parent?.getWorldQuaternion(rb),e.quaternion.copy(rb.invert()).multiply(nb))}free(){this.loadedTexture?.dispose(),this.loadedTexture=null,this.shadowMesh&&(this.shadowMesh.material.dispose(),this.shadowMesh.customDepthMaterial?.dispose()),this.blobMesh&&this.blobMesh.material.dispose(),super.free()}},sb=class extends ob{static typeName=`AnimatedSprite3D`;static signals=[`animationFinished`];static props={sheet:{default:``},frameWidth:{default:0},frameHeight:{default:0},animations:{default:{}},autoplay:{default:``}};sheet=``;frameWidth=0;frameHeight=0;animations={};autoplay=``;playing=!1;currentFrame=0;currentAnimation=``;frameList=[];frameIndex=0;frameTime=0;sheetTexture=null;loadedSheetRef=``;play(e){let t=this.animations[e];if(!t)throw new y(`UNKNOWN_ANIMATION`,`No animation '${e}' on '${this.name}'. Available: [${Object.keys(this.animations).join(`, `)}].`);this.currentAnimation=e,this.frameList=cb(t.frames,e),this.frameIndex=0,this.currentFrame=this.frameList[0]??0,this.frameTime=0,this.playing=!0}stop(){this.playing=!1}onReady(){this.autoplay&&this.play(this.autoplay)}update(e){if(!this.playing||this.currentAnimation===``)return;let t=this.animations[this.currentAnimation];if(!t||!Number.isFinite(t.fps)||t.fps<=0)return;let n=1/t.fps;for(this.frameTime+=e;this.frameTime>=n;)if(this.frameTime-=n,this.frameIndex+=1,this.frameIndex>=this.frameList.length)if(t.loop)this.frameIndex=0;else{this.frameIndex=this.frameList.length-1,this.playing=!1,this.emit(`animationFinished`,this.currentAnimation);break}this.currentFrame=this.frameList[this.frameIndex]??0}resolveTexture(){if(!this.sheet||this.frameWidth<=0||this.frameHeight<=0)return null;if(this.loadedSheetRef!==this.sheet&&(this.sheetTexture?.dispose(),this.sheetTexture=null,this.loadedSheetRef=this.sheet,typeof document<`u`)){let e=new bc().load(this.sheet);e.colorSpace=Yn;let t=this.pixelArt?Ct:Et;e.magFilter=t,e.minFilter=t,this.sheetTexture=e}let e=this.sheetTexture;if(!e)return null;let t=e.image;if(!t?.width||!t.height)return e;let n=Math.max(1,Math.floor(t.width/this.frameWidth)),r=this.currentFrame%n,i=Math.floor(this.currentFrame/n);return e.repeat.set(this.frameWidth/t.width,this.frameHeight/t.height),e.offset.set(r*this.frameWidth/t.width,1-(i+1)*this.frameHeight/t.height),e}free(){this.sheetTexture?.dispose(),this.sheetTexture=null,super.free()}};function cb(e,t){if(!Array.isArray(e)||e.length===0)throw new y(`BAD_FORMAT`,`Animation '${t}': "frames" must be a non-empty array.`);if(e.length===2){let[t,n]=e;if(n>=t){let e=[];for(let r=t;r<=n;r++)e.push(r);return e}}return[...e]}var lb=class extends Zv{static typeName=`Camera3D`;static props={fov:{default:60},near:{default:.1},far:{default:1e3},current:{default:!1}};fov=60;near=.1;far=1e3;current=!1;_createObject3D(){return new Nc(this.fov,1,this.near,this.far)}_syncObject3D(){super._syncObject3D();let e=this._ensureObject3D();(e.fov!==this.fov||e.near!==this.near||e.far!==this.far)&&(e.fov=this.fov,e.near=this.near,e.far=this.far,e.updateProjectionMatrix())}},ub=8,db=.2,fb=.15,pb=1.2,mb=.08,hb=2.5,gb=-20,_b=.002,vb=180/Math.PI,yb=.3,bb=.4,xb=.3,Sb=new _i,Cb=new V,wb=new G,Tb=new H,Eb=new H,Db=new H(0,1,0),Ob=class extends Zv{static typeName=`CharacterController3D`;static signals=[`movementStateChanged`];static props={view:{default:`free`,options:[`free`,`firstPerson`,`quarter`,`side`]},camDistance:{default:4},eyeHeight:{default:.64},maxSpeed:{default:2.5},sprintMultiplier:{default:2},jumpVelocity:{default:4},sprintJumpMultiplier:{default:1.2},floatHeight:{default:.01},mouseLook:{default:!0},zoomMin:{default:0},zoomMax:{default:0},pitchMin:{default:-1.3},pitchMax:{default:1.5},cameraCollision:{default:!0},turnSpeed:{default:100},camLerp:{default:25},moveAction:{default:`move`},jumpAction:{default:`jump`},sprintAction:{default:`sprint`},skinPath:{default:`../Skin`},skinYawOffset:{default:0}};view=`free`;camDistance=4;eyeHeight=.64;maxSpeed=2.5;sprintMultiplier=2;jumpVelocity=4;sprintJumpMultiplier=1.2;floatHeight=.01;mouseLook=!0;zoomMin=0;zoomMax=0;pitchMin=-1.3;pitchMax=1.5;cameraCollision=!0;turnSpeed=100;camLerp=25;moveAction=`move`;jumpAction=`jump`;sprintAction=`sprint`;skinPath=`../Skin`;skinYawOffset=0;yaw=0;pitch=0;state=`idle`;grounded=!1;get body(){let e=this.parent;if(!(e instanceof ty))throw new y(`TREE_VIOLATION`,`CharacterController3D '${this.name}' must be a child of a RigidBody3D (parent is '${e?.name??`none`}').`);return e}onEnterTree(){if(this.body,this.skinPath!==``){let e=this.getNodeOrNull(this.skinPath);e&&(e.rotation=[e.rotation[0]??0,180+this.skinYawOffset,e.rotation[2]??0])}}fixedUpdate(e){let t=this.body,n=t._physics3d,r=this.tree?.engine?.input;if(!n||!r)return;let i=t.collider.radius??.32,a=(t.collider.height??1)/2,o=Ab(t),s=n.velocityOf(t),c=n.massOf(t),l=i+this.floatHeight,u=i+2,d=o[1]-a,f=null;for(let[e,r]of[[0,0],[i,0],[-i,0],[0,i],[0,-i]]){let i=n.castRay([o[0]+e,d,o[2]+r],[0,-1,0],u,t);if(i){let t=i.distance;if(f=f===null?t:Math.min(f,t),e===0&&r===0)break}}this.grounded=f!==null&&f<l*2;let p=0,m=0,h=!1;try{let e=r.getVector(this.moveAction);h=e.x!==0||e.y!==0;let t=this.view===`free`||this.view===`firstPerson`?this.yaw:0,n=this.view===`side`?[e.x,0,0]:qv(e.x,-e.y,t),i=Math.hypot(n[0],n[2])||1;p=n[0]/i,m=n[2]/i}catch{}let g=jb(r,this.sprintAction),_=Jv(h,g),v=1+(this.sprintMultiplier-1)*_,y=h?this.maxSpeed*v:0,b=this.grounded?1:db,x=(p*y-s[0])/ub*b,S=(m*y-s[2])/ub*b;if(t.applyImpulse([x*c,0,S*c]),!h&&this.grounded&&t.applyImpulse([-s[0]*fb*c,0,-s[2]*fb*c]),f!==null&&f<l*2){let e=pb*(l-f)-s[1]*mb;t.applyImpulse([0,e*c,0])}if(this.grounded&&jb(r,this.jumpAction)){let e=this.jumpVelocity*(1+(this.sprintJumpMultiplier-1)*_);t.linearVelocity=[s[0],e,s[2]],this.grounded=!1}s[1]<gb?t.gravityScale=0:s[1]<0&&!this.grounded?t.gravityScale=hb:t.gravityScale=1,this.view===`side`&&Math.abs(o[2])>.01&&(t.position=[t.position[0]??0,t.position[1]??0,0]);let C=Yv(this.grounded,h?_:0);if(C!==this.state&&(this.state=C,this.emit(`movementStateChanged`,C)),h&&this.skinPath!==``){let t=this.getNodeOrNull(this.skinPath);if(t){let n=Math.atan2(p,m)*vb+this.skinYawOffset,r=t.rotation[1]??0,i=(n-r+540)%360-180,a=this.turnSpeed*e*vb;Math.abs(i)>a&&(i=Math.sign(i)*a),t.rotation=[t.rotation[0]??0,r+i,t.rotation[2]??0]}}}update(e){let t=this.tree?.engine?.input;if(!t)return;if(this.mouseLook){let e=t.pointerDelta();this.yaw-=e.x*_b,this.pitch=kb(this.pitch+e.y*_b,-this.pitchMax,-this.pitchMin);let n=t.wheelDelta();n!==0&&this.zoomMax>this.zoomMin&&(this.camDistance=kb(this.camDistance+n*_b,this.zoomMin,this.zoomMax))}let n=this.findCamera();if(!n)return;let r=this.parent instanceof ty?this.parent:null;if(!r)return;let i=Ab(r),a=this.view===`free`?11:1e3,o=1-Math.exp(-a*e),s=this.pivot??[i[0],i[1],i[2]];this.pivot=s,s[0]+=(i[0]-s[0])*o,s[1]+=(i[1]-s[1])*o,s[2]+=(i[2]-s[2])*o;let c=Wv(this.view,s,this.eyeHeight,this.yaw,this.pitch,this.camDistance),l=[s[0],s[1]+this.eyeHeight,s[2]],u=[c.position[0],c.position[1],c.position[2]];if(this.cameraCollision&&this.view!==`firstPerson`){let e=r._physics3d;e&&(u=Gv(l,u,(t,n,i)=>e.castRay(t,n,i,r,{staticOnly:!0})?.distance??null,yb,bb),u=Kv(u,(t,n)=>e.castRay(t,[0,-1,0],n,r,{staticOnly:!0})?.distance??null,xb))}let d=this.view===`firstPerson`?1e3:this.camLerp,f=1-Math.exp(-d*e),p=n.position;n.position=[(p[0]??0)+(u[0]-(p[0]??0))*f,(p[1]??0)+(u[1]-(p[1]??0))*f,(p[2]??0)+(u[2]-(p[2]??0))*f],this.view===`free`?(Tb.set(n.position[0]??0,n.position[1]??0,n.position[2]??0),Eb.set(l[0],l[1],l[2]),wb.lookAt(Tb,Eb,Db),Cb.setFromRotationMatrix(wb),Sb.setFromQuaternion(Cb,`XYZ`)):(Sb.set(c.rotation[0],c.rotation[1],c.rotation[2],`YXZ`),Cb.setFromEuler(Sb),Sb.setFromQuaternion(Cb,`XYZ`)),n.rotation=[Sb.x*vb,Sb.y*vb,Sb.z*vb]}pivot=null;cameraCache=null;findCamera(){if(this.cameraCache?.tree===this.tree&&this.cameraCache.current)return this.cameraCache;this.cameraCache=null;let e=this.tree?.root;if(!e)return null;let t=e=>{if(!this.cameraCache){if(e instanceof lb&&e.current){this.cameraCache=e;return}for(let n of e.children)t(n)}};return t(e),this.cameraCache}};function kb(e,t,n){return Math.min(n,Math.max(t,e))}function Ab(e){let t=0,n=0,r=0;for(let i=e;i&&i instanceof Zv;i=i.parent)t+=i.position[0]??0,n+=i.position[1]??0,r+=i.position[2]??0;return[t,n,r]}function jb(e,t){try{return e.isPressed(t)}catch{return!1}}var Mb=.15,Nb=.7,Pb=.5,Fb=.7,Ib=.5;function Lb(e,t,n=3){let r=0,i=1,a=1,o=.65;for(let s=0;s<n;s++){let n=e*a,s=t*a,c=Math.sin(n*.1+s*.2)*.5+.5,l=Math.cos(n*.3+s*.1)*.5+.5,u=Math.sin((n+s)*.15)*.5+.5,d=Math.cos(n*n*.01+s*.2)*.5+.5,f=c*l*.7+u*d*.3;r+=f*i,i*=o,a*=2}return r/((1-o**n)/(1-o))}function Rb(e,t){let n=Lb(e*Mb,t*Mb,4),r=Lb(e*Mb*.2,t*Mb*.2,2);return n>Pb-Nb*.5-r*Ib}var zb=.9;function Bb(e,t,n){let r=(Math.imul(e,2246822507)^Math.imul(t,3266489909)^Math.imul(n+1,668265263))>>>0;return r=Math.imul(r^r>>>15,739982445)>>>0,r=Math.imul(r^r>>>12,695872825)>>>0,r^=r>>>15,(r>>>0)/4294967296}function Vb(e,t,n=zb){let r=Math.floor(e/n),i=Math.floor(t/n),a=1/0,o=r,s=i;for(let c=-1;c<=1;c++)for(let l=-1;l<=1;l++){let u=r+l,d=i+c,f=(u+Bb(u,d,0))*n,p=(d+Bb(u,d,1))*n,m=(e-f)*(e-f)+(t-p)*(t-p);m<a&&(a=m,o=u,s=d)}return{id:Math.imul(o,2654435761)^Math.imul(s,2246822519)|0,hue:Bb(o,s,2)*2-1,leanDir:Bb(o,s,3)*Math.PI*2,lean:Bb(o,s,4)}}function Hb(e,t,n,r){let i=e*r,a=t*r,o=Math.floor(i),s=Math.floor(a),c=i-o,l=a-s,u=c*c*(3-2*c),d=l*l*(3-2*l),f=Bb(o,s,n),p=Bb(o+1,s,n),m=Bb(o,s+1,n),h=Bb(o+1,s+1,n),g=f+(p-f)*u;return g+(m+(h-m)*u-g)*d}var Ub=[.55,1.7],Wb=.17,Gb=.23,Kb=.28;function qb(e,t,n){let r=Hb(e,t,n^6877377|0,Wb),i=r*r*(3-2*r);return Ub[0]+(Ub[1]-Ub[0])*i}function Jb(e,t,n){return Hb(e,t,n^3001105|0,Gb)}function Yb(e,t,n){let r=Hb(e,t,n^7840757|0,Kb)*.72+Hb(e,t,n^1688295|0,Kb*3.1)*.28,i=Math.min(1,Math.max(0,(r-.18)/.64));return i*i*(3-2*i)}function Xb(e){let{seed:t,areaX:n,areaZ:r,count:i,density:a}=e,o=new b(t),s=n/2,c=r/2,l=[],u=Math.floor(i*Fb);for(let e=0;e<u&&l.length<i;e++){let e=o.range(-s,s),t=o.range(-c,c),n=o.next()<.3,r=Rb(e,t);if(!n&&!r&&o.next()>.15)continue;let i=o.next()*Math.PI*2,a=.5+o.next()*.7,u=r?a*(1+o.next()*.2):a*(.9+o.next()*.2);l.push({x:e,z:t,rotY:i,scale:u})}let d=Math.min(3,Math.max(1,a*10)),f=Math.ceil(Math.sqrt(i*d)),p=n/f,m=r/f,h=Math.floor((i-l.length)*.8),g=0;for(let e=0;e<f&&l.length<i;e++)for(let t=0;t<f&&l.length<i;t++){let i=t/f*n-s+p/2,a=e/f*r-c+m/2,u=i+(o.next()-.5)*p*1.5,d=a+(o.next()-.5)*m*1.5;if(u<-s||u>s||d<-c||d>c)continue;let _=Rb(u,d);if(g<h){if(!_&&o.next()>.05)continue}else if(_){if(o.next()>.2)continue}else if(o.next()>.4)continue;if(o.next()<.2)continue;let v=o.next()*Math.PI*2,y=_?.5:.3;l.push({x:u,z:d,rotY:v,scale:.5+o.next()*y}),g++}for(;l.length<i;)l.push({x:o.range(-s,s),z:o.range(-c,c),rotY:o.next()*Math.PI*2,scale:.4+o.next()*.6});return l.sort((e,t)=>t.z-e.z),l}var Zb=[`daisy`,`cosmos`,`bellflower`],Qb={daisy:{petals:8,blooms:3,leaves:3,petalLength:.1,petalWidth:.052,cupDeg:16,nodDeg:14,centerRadius:.024,centerColor:`#e0a72e`},cosmos:{petals:8,blooms:2,leaves:2,petalLength:.12,petalWidth:.064,cupDeg:26,nodDeg:18,centerRadius:.023,centerColor:`#d2941f`},bellflower:{petals:5,blooms:3,leaves:2,petalLength:.088,petalWidth:.058,cupDeg:46,nodDeg:52,centerRadius:.015,centerColor:`#cfe06a`}},$b=[1,.78,.62],ex=[0,.07,.11],tx=[.6,2.7,4.9],nx=.07,rx=.011,ix=.17,ax=.05,ox=[.12,.2,.28],sx=new K(`#3e6a26`),cx=new K(`#548331`);function lx(e){let t=Qb[e],n=new ux(!0),r=new ux(!1),i=new K(t.centerColor);for(let e=0;e<t.blooms;e++){let a=$b[e],o=tx[e],s=ex[e]*Math.cos(o),c=ex[e]*Math.sin(o),l=nx*Math.cos(o),u=nx*Math.sin(o),d=new H(s,0,c),f=new H(s+l,a,c+u),p=new H(s+l*.25,a*.55,c+u*.25);n.addStem(d,p,f,rx,sx);let m=t.nodDeg*Math.PI/180,h=new H(Math.sin(m)*Math.cos(o),Math.cos(m),Math.sin(m)*Math.sin(o)),g=new H(-Math.sin(o),0,Math.cos(o)),_=new H().crossVectors(h,g).normalize(),v=f.clone().addScaledVector(h,.012),y=t.cupDeg*Math.PI/180,b=e*.45;for(let e=0;e<t.petals;e++){let n=e/t.petals*Math.PI*2+b,i=g.clone().multiplyScalar(Math.cos(n)).addScaledVector(_,Math.sin(n)).normalize();r.addPetal(v,i,h,t,y)}n.addDisc(v.clone().addScaledVector(h,.004),g,_,t.centerRadius,i)}for(let e=0;e<t.leaves;e++){let t=ox[e%ox.length],r=1.1+e*2.3;n.addLeaf(new H(0,t,0),r,cx)}return{structure:n.build(),petals:r.build()}}var ux=class{positions=[];colors;indices=[];constructor(e){this.colors=e?[]:null}vertex(e,t){let n=this.positions.length/3;return this.positions.push(e.x,e.y,e.z),this.colors&&t&&this.colors.push(t.r,t.g,t.b),n}addStem(e,t,n,r,i){for(let a of[0,Math.PI/2]){let o=new H(Math.cos(a),0,Math.sin(a)),s=[];for(let a=0;a<=5;a++){let c=a/5,l=1-c,u=new H(l*l*e.x+2*l*c*t.x+c*c*n.x,l*l*e.y+2*l*c*t.y+c*c*n.y,l*l*e.z+2*l*c*t.z+c*c*n.z),d=r/2*(1-.4*c);s.push(this.vertex(u.clone().addScaledVector(o,-d),i),this.vertex(u.clone().addScaledVector(o,d),i))}for(let e=0;e<5;e++){let t=s[e*2],n=s[e*2+1],r=s[e*2+2],i=s[e*2+3];this.indices.push(t,n,r,r,n,i)}}}addPetal(e,t,n,r,i){let a=new H().crossVectors(n,t).normalize(),o=r.centerRadius*.85,s=Math.sin(i)*r.petalLength*.45,c=Math.sin(i)*r.petalLength*.95,l=Math.cos(i),u=e.clone().addScaledVector(t,o),d=o+r.petalLength*l*.55,f=o+r.petalLength*l,p=e.clone().addScaledVector(t,d).addScaledVector(a,r.petalWidth/2).addScaledVector(n,s),m=e.clone().addScaledVector(t,d).addScaledVector(a,-r.petalWidth/2).addScaledVector(n,s),h=e.clone().addScaledVector(t,f).addScaledVector(n,c),g=this.vertex(u,null),_=this.vertex(p,null),v=this.vertex(m,null),y=this.vertex(h,null);this.indices.push(g,_,y,g,y,v)}addDisc(e,t,n,r,i){let a=this.vertex(e,i),o=[];for(let a=0;a<6;a++){let s=a/6*Math.PI*2;o.push(this.vertex(e.clone().addScaledVector(t,Math.cos(s)*r).addScaledVector(n,Math.sin(s)*r),i))}for(let e=0;e<6;e++)this.indices.push(a,o[e],o[(e+1)%6])}addLeaf(e,t,n){let r=new H(Math.cos(t),0,Math.sin(t)),i=new H(-Math.sin(t),0,Math.cos(t)),a=Math.sin(.6),o=Math.cos(.6),s=e.clone().addScaledVector(r,ix*o*.5).add(new H(0,ix*a*.5,0)),c=e.clone().addScaledVector(r,ix*o).add(new H(0,ix*a*.8,0)),l=this.vertex(e,n),u=this.vertex(s.clone().addScaledVector(i,ax/2),n),d=this.vertex(s.clone().addScaledVector(i,-.05/2),n),f=this.vertex(c,n);this.indices.push(l,u,f,l,f,d)}build(){let e=new Ha;return e.setAttribute(`position`,new q(new Float32Array(this.positions),3)),this.colors&&e.setAttribute(`color`,new q(new Float32Array(this.colors),3)),e.setIndex(this.indices),e.computeVertexNormals(),e}},dx=2.6;function fx(e){let{seed:t,areaX:n,areaZ:r,count:i,clustering:a}=e,o=new b(t),s=n/2,c=r/2,l=1-.74*Math.min(Math.max(a,0),1),u=[];for(let e=0;e<i*16&&u.length<i;e++){let e=o.range(-s,s),t=o.range(-c,c),n=Vb(e,t,dx);n.lean>l&&o.next()>.06||u.push({x:e,z:t,rotY:o.next()*Math.PI*2,scale:.75+o.next()*.5,variety01:(n.hue+1)/2,color01:n.leanDir/(Math.PI*2)})}return u}var px=1.05,mx=1;function hx(e,t=mx){e.computeBoundingSphere();let n=e.boundingSphere;n&&n.radius>=0&&(n.radius=n.radius*px+t),e.frustumCulled=!0}function gx(e){let t=0,n=0,r=0;for(let i=e;i;i=i.parent)i instanceof Zv&&(t+=i.position[0]??0,n+=i.position[1]??0,r+=i.position[2]??0);return[t,n,r]}function _x(e,t){if(t!==``){let n=e.getNodeOrNull(t);return n instanceof Fy?n:null}return vx(e.getRoot())}function vx(e){if(e instanceof Fy)return e;for(let t of e.children){let e=vx(t);if(e)return e}return null}var yx={lush:2.2,sparse:.35,none:0},bx=[`lush`,`sparse`,`none`];function xx(e){if(typeof e==`number`)return e;let t=yx[e];if(t===void 0)throw new y(`BAD_FORMAT`,`Flowers3D density must be one of [${bx.join(`, `)}] or a number (plants/m²), got '${e}'.`,{prop:`density`,validOptions:bx});return t}var Sx=1e4,Cx=[`#f5f1e2`,`#e9d052`,`#8d7bd9`],wx=/^#[0-9a-fA-F]{6}$/,Tx=class extends Zv{static typeName=`Flowers3D`;static props={density:{default:`sparse`,options:bx},area:{default:[20,20]},seed:{default:1},varieties:{default:[]},palette:{default:[]},height:{default:.45},clustering:{default:.6},sway:{default:.5},drape:{default:!1},terrain:{default:``}};density=`sparse`;area=[20,20];seed=1;varieties=[];palette=[];height=.45;clustering=.6;sway=.5;drape=!1;terrain=``;drapeTerrain=null;drapeWorld=[0,0,0];static validateJson(e){let t=e;if(typeof t.density==`string`)xx(t.density);else if(!(Number.isFinite(t.density)&&t.density>=0))throw new y(`BAD_FORMAT`,`Flowers3D '${e.name}' density must be one of [${bx.join(`, `)}] or a number >= 0 (plants/m²), got ${t.density}.`,{prop:`density`,validOptions:bx});if(!((t.area[0]??0)>0)||!((t.area[1]??0)>0))throw new y(`BAD_FORMAT`,`Flowers3D '${e.name}' area must be positive [x, z] meters, got ${JSON.stringify(t.area)}.`,{prop:`area`});let n=Math.floor((t.area[0]??0)*(t.area[1]??0)*xx(t.density));if(n>Sx)throw new y(`BAD_FORMAT`,`Flowers3D '${e.name}' blows the plant budget: area ${t.area[0]}×${t.area[1]} m × density ${xx(t.density)} = ${n} plants > ${Sx}. Lower the density or split the field into multiple nodes.`,{prop:`density`});if(!Array.isArray(t.varieties)||new Set(t.varieties).size!==t.varieties.length||!t.varieties.every(e=>Zb.includes(e)))throw new y(`BAD_FORMAT`,`Flowers3D '${e.name}' varieties must be a unique subset of [${Zb.join(`, `)}] ([] = all three), got ${JSON.stringify(t.varieties)}.`,{prop:`varieties`,validOptions:[...Zb]});if(!Array.isArray(t.palette)||!t.palette.every(e=>typeof e==`string`&&wx.test(e)))throw new y(`BAD_FORMAT`,`Flowers3D '${e.name}' palette must be an array of '#rrggbb' head colors ([] = white/yellow/violet), got ${JSON.stringify(t.palette)}.`,{prop:`palette`});if(!(t.height>0))throw new y(`BAD_FORMAT`,`Flowers3D '${e.name}' height must be > 0 meters, got ${t.height}.`,{prop:`height`});if(!(t.clustering>=0)||!(t.clustering<=1))throw new y(`BAD_FORMAT`,`Flowers3D '${e.name}' clustering must be in [0, 1] (0 uniform … 1 tight patches), got ${t.clustering}.`,{prop:`clustering`});if(!(t.sway>=0))throw new y(`BAD_FORMAT`,`Flowers3D '${e.name}' sway must be >= 0, got ${t.sway}.`,{prop:`sway`});if(typeof t.drape!=`boolean`)throw new y(`BAD_FORMAT`,`Flowers3D '${e.name}' drape must be a boolean, got ${JSON.stringify(t.drape)}.`,{prop:`drape`});if(typeof t.terrain!=`string`)throw new y(`BAD_FORMAT`,`Flowers3D '${e.name}' terrain must be a node-path string (empty = auto-find), got ${JSON.stringify(t.terrain)}.`,{prop:`terrain`})}time=0;field=null;varietyMeshes=[];fieldKey=``;timeUniform={value:0};swayUniform={value:.5};_createObject3D(){return new Fi}_varietyMeshes(){return this.rebuildIfNeeded(),this.varietyMeshes}update(e){this.time+=e,this.timeUniform.value=this.time}_syncObject3D(){super._syncObject3D(),this.rebuildIfNeeded(),this.swayUniform.value=this.sway}rebuildIfNeeded(){this.field||(this.field=new Fi,this._ensureObject3D().add(this.field));let e=JSON.stringify([this.density,this.area,this.seed,this.varieties,this.palette,this.height,this.clustering,this.drape,this.terrain]);e!==this.fieldKey&&(this.fieldKey=e,this.disposeMeshes(),this.resolveDrape(),this.buildField())}resolveDrape(){if(this.drapeTerrain=null,!this.drape)return;let e=_x(this,this.terrain);if(e)this.drapeTerrain=e,this.drapeWorld=gx(this);else if(this.terrain!==``)throw new y(`NODE_NOT_FOUND`,`Flowers3D '${this.name}' drape terrain '${this.terrain}' is not a Terrain3D in the tree.`,{prop:`terrain`})}bedY(e,t){let n=this.drapeTerrain;return n?n.heightAt(this.drapeWorld[0]+e,this.drapeWorld[2]+t)-this.drapeWorld[1]:0}buildField(){let e=xx(this.density),t=Math.min(Math.floor((this.area[0]??0)*(this.area[1]??0)*e),Sx);if(t<=0)return;let n=fx({seed:this.seed,areaX:this.area[0]??0,areaZ:this.area[1]??0,count:t,clustering:this.clustering}),r=this.varieties.length>0?this.varieties:Zb,i=r.map(()=>[]);for(let e of n)i[Math.min(Math.floor(e.variety01*r.length),r.length-1)].push(e);let a=this.palette.length>0?this.palette:Cx,o=new b(this.seed+15797765);for(let e=0;e<r.length;e++){let t=i[e];if(t.length===0)continue;let{structure:n,petals:s}=lx(r[e]),c=new Bs({vertexColors:!0,roughness:1,side:2}),l=new Bs({roughness:.85,side:2});Ex(c,this.timeUniform,this.swayUniform),Ex(l,this.timeUniform,this.swayUniform);let u=new Vo(n,c,t.length),d=new Vo(s,l,t.length);u.receiveShadow=!0,d.receiveShadow=!0;for(let e=0;e<t.length;e++){let n=t[e];kx.set(n.x,this.bedY(n.x,n.z),n.z),Ax.setFromAxisAngle(Dx,n.rotY),jx.setScalar(this.height*n.scale),Ox.compose(kx,Ax,jx),u.setMatrixAt(e,Ox),d.setMatrixAt(e,Ox);let r=Math.min(Math.floor(n.color01*a.length),a.length-1);Mx.set(a[r]).offsetHSL(o.range(-.02,.02),0,o.range(-.05,.05)),d.setColorAt(e,Mx),Mx.setRGB(1,1,1).offsetHSL(o.range(-.02,.02),0,o.range(-.06,.04)),u.setColorAt(e,Mx)}u.instanceMatrix.needsUpdate=!0,d.instanceMatrix.needsUpdate=!0,u.instanceColor&&(u.instanceColor.needsUpdate=!0),d.instanceColor&&(d.instanceColor.needsUpdate=!0),hx(u),hx(d),this.varietyMeshes.push({variety:r[e],structure:u,petals:d}),this.field.add(u),this.field.add(d)}}disposeMeshes(){for(let{structure:e,petals:t}of this.varietyMeshes)for(let n of[e,t])n.removeFromParent(),n.geometry.dispose(),n.material.dispose(),n.dispose();this.varietyMeshes=[]}free(){this.disposeMeshes(),this.field=null,super.free()}};function Ex(e,t,n){e.userData.sway=!0,e.userData.timeUniform=t,e.onBeforeCompile=e=>{e.uniforms.uFlowerTime=t,e.uniforms.uFlowerSway=n,e.vertexShader=`uniform float uFlowerTime;\nuniform float uFlowerSway;\n${e.vertexShader}`.replace(`#include <project_vertex>`,`
|
|
5302
5302
|
vec4 mvPosition = vec4(transformed, 1.0);
|
|
5303
5303
|
#ifdef USE_INSTANCING
|
|
5304
5304
|
mvPosition = instanceMatrix * mvPosition;
|
|
@@ -5311,7 +5311,7 @@ normal = perturbSplatNormal(normal, vViewPosition, vUvSplat, blendedNormalSample
|
|
|
5311
5311
|
mvPosition.z += uFlowerSway * swayW * 0.020 * cos(uFlowerTime * 1.3 + swayPhase * 1.4);
|
|
5312
5312
|
mvPosition = modelViewMatrix * mvPosition;
|
|
5313
5313
|
gl_Position = projectionMatrix * mvPosition;
|
|
5314
|
-
`)},e.customProgramCacheKey=()=>`incanto-flowers-sway`}var
|
|
5314
|
+
`)},e.customProgramCacheKey=()=>`incanto-flowers-sway`}var Dx=new H(0,1,0),Ox=new G,kx=new H,Ax=new V,jx=new H,Mx=new K;function Nx(e,t,n=4){let r=t.areaX/2,i=t.areaZ/2,a=e.filter(e=>Math.abs(e.x-t.x)<=r+e.radius&&Math.abs(e.z-t.z)<=i+e.radius);return a.sort((e,n)=>(e.x-t.x)**2+(e.z-t.z)**2-((n.x-t.x)**2+(n.z-t.z)**2)),a.slice(0,n)}var Px=[1,.78,.42];function Fx(e){let t=new Float32Array(21),n=new Float32Array(14);for(let r=0;r<3;r++){let i=r/3,a=Px[r]*e/2;for(let e=0;e<2;e++){let o=r*2+e;t[o*3]=e===0?-a:a,t[o*3+1]=i,t[o*3+2]=0,n[o*2]=e,n[o*2+1]=i}}t[18]=0,t[19]=1,t[20]=0,n[12]=.5,n[13]=1;let r=new Ha;return r.setAttribute(`position`,new q(t,3)),r.setAttribute(`uv`,new q(n,2)),r.setIndex([0,1,2,2,1,3,2,3,4,4,3,5,4,5,6]),r}function Ix(e){let t=new Float32Array([0,1.025,0,-e,.985,0,0,.985,e,e,.985,0,0,.985,-e]),n=new Ha;return n.setAttribute(`position`,new q(t,3)),n.setIndex([0,1,2,0,2,3,0,3,4,0,4,1]),n.computeVertexNormals(),n}var Lx=`
|
|
5315
5315
|
uniform float time;
|
|
5316
5316
|
uniform float windStrength;
|
|
5317
5317
|
uniform float grassHeight;
|
|
@@ -5389,7 +5389,7 @@ void main() {
|
|
|
5389
5389
|
gl_Position = projectionMatrix * mvPosition;
|
|
5390
5390
|
#include <fog_vertex>
|
|
5391
5391
|
}
|
|
5392
|
-
`,
|
|
5392
|
+
`,Rx=`
|
|
5393
5393
|
uniform float time;
|
|
5394
5394
|
varying vec2 vUv;
|
|
5395
5395
|
varying float vHeight;
|
|
@@ -5547,7 +5547,7 @@ void main() {
|
|
|
5547
5547
|
// patches melt into the same fog as the terrain instead of staying vivid.
|
|
5548
5548
|
#include <fog_fragment>
|
|
5549
5549
|
}
|
|
5550
|
-
`,
|
|
5550
|
+
`,zx=`
|
|
5551
5551
|
// incanto atmosphere: standard fog + shadow-receive chunks — both compile
|
|
5552
5552
|
// out unless the scene declares fog / a light casts (USE_FOG, USE_SHADOWMAP)
|
|
5553
5553
|
#include <common>
|
|
@@ -5651,7 +5651,7 @@ void main() {
|
|
|
5651
5651
|
#include <fog_vertex>
|
|
5652
5652
|
#include <shadowmap_vertex>
|
|
5653
5653
|
}
|
|
5654
|
-
`,
|
|
5654
|
+
`,Bx=`
|
|
5655
5655
|
// incanto atmosphere: fog + shadow-receive (grass RECEIVES, never casts —
|
|
5656
5656
|
// per-blade casting would double the vertex bill for invisible gain).
|
|
5657
5657
|
// receiveShadow is declared by lights_pars_begin in BUILT-IN materials; a
|
|
@@ -5723,7 +5723,7 @@ void main() {
|
|
|
5723
5723
|
#include <colorspace_fragment>
|
|
5724
5724
|
#include <fog_fragment>
|
|
5725
5725
|
}
|
|
5726
|
-
`,
|
|
5726
|
+
`,Vx=.4;function Hx(e){if(typeof e.radius==`number`)return e.radius;let t=e.size;if(Array.isArray(t)){let e=typeof t[0]==`number`?t[0]:0,n=typeof t[2]==`number`?t[2]:0;if(e>0||n>0)return Math.max(e,n)/2}return Vx}function Ux(e){let t=typeof e.radius==`number`?e.radius:0,n=typeof e.height==`number`?e.height:0;if(n>0)return n/2+t;if(t>0)return t;let r=e.size;return Array.isArray(r)&&typeof r[1]==`number`?r[1]/2:Vx}function Wx(e,t){t.length=0,Gx(e,t)}function Gx(e,t){if(e instanceof ny||e instanceof ty){let[n,r,i]=gx(e);t.push({x:n,y:r,z:i,radius:Hx(e.collider),footY:r-Ux(e.collider)})}for(let n of e.children)Gx(n,t)}function Kx(e){let t=e.tree;if(!t){let t=[];return Wx(e.getRoot(),t),t}let n=t._frameScratch;if(n&&n.frameId===t.frameId)return n.bodies;let r=n?.bodies??[],i=t.root;return i?Wx(i,r):r.length=0,t._frameScratch={frameId:t.frameId,bodies:r},r}var qx=6,Jx=512;function Yx(e,t=`grass`){if(t===`fern`)return Xx(e);let n=new b(e+28919),r=[];for(let e=0;e<180;e++){let e=.5+(n.next()+n.next()-1)*.17,t=(e>=.5?1:-1)*n.range(.04,.5)+n.range(-.1,.1),i=n.range(.45,1),a=Math.min(1,Math.max(0,e+t*(1.5-.9*i)));r.push({rootX:e,tipX:a,height:i,width:n.range(.0028,.0085),shade:n.range(.45,.98),bow:t*n.range(.25,.6)})}return r}function Xx(e){let t=new b(e+65121),n=[];for(let e=0;e<56;e++){let r=.5+(t.next()+t.next()-1)*.1,i=(e%2==0?1:-1)*t.range(.12,.55)+t.range(-.06,.06),a=t.range(.32,.85),o=Math.min(1,Math.max(0,r+i*(1.6-.7*a)));n.push({rootX:r,tipX:o,height:a,width:t.range(.014,.03),shade:t.range(.4,.85),bow:i*t.range(.5,.85)})}return n}function Zx(e,t=`grass`){let n=e.shade,r=Math.max(.3,e.shade*.62);if(t===`fern`){let e=[{offset:0,gray:r,alpha:0}],t=.14;for(let i=0;i<qx;i++){let a=t+(1-t)*i/qx,o=t+(1-t)*(i+1)/qx,s=r+(n-r)*((a+o)/2);e.push({offset:a+(o-a)*.12,gray:s,alpha:1}),e.push({offset:a+(o-a)*.74,gray:s,alpha:1}),i<qx-1&&e.push({offset:a+(o-a)*.92,gray:s,alpha:.2})}return e[e.length-1].offset<1&&e.push({offset:1,gray:n,alpha:1}),e}return[{offset:0,gray:r,alpha:0},{offset:.22,gray:r,alpha:1},{offset:1,gray:n,alpha:1}]}function Qx(e,t=`grass`){if(typeof document>`u`)return null;let n=Jx,r=document.createElement(`canvas`);r.width=n,r.height=n;let i=r.getContext(`2d`);if(!i)return null;i.clearRect(0,0,n,n);for(let r of Yx(e,t)){let e=r.rootX*n,a=n,o=r.tipX*n,s=(1-r.height)*n,c=(e+o)/2+r.bow*n,l=(a+s)/2,u=Math.max(r.width*n,.75);i.beginPath(),i.moveTo(e-u,a),i.quadraticCurveTo(c-u*.55,l,o,s),i.quadraticCurveTo(c+u*.55,l,e+u,a),i.closePath();let d=i.createLinearGradient(e,a,o,s);for(let e of Zx(r,t)){let t=Math.round(e.gray*255);d.addColorStop(e.offset,`rgba(${t},${t},${t},${e.alpha})`)}i.fillStyle=d,i.fill()}let a=new vs(r);return a.colorSpace=Yn,a}var $x=.08,eS=.5,tS=12,nS=`
|
|
5727
5727
|
// GLSL Simplex Noise 3D — https://github.com/ashima/webgl-noise (MIT)
|
|
5728
5728
|
vec3 ezMod289(vec3 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }
|
|
5729
5729
|
vec4 ezMod289(vec4 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }
|
|
@@ -5775,9 +5775,9 @@ float ezSimplex3(vec3 v) {
|
|
|
5775
5775
|
m = m * m;
|
|
5776
5776
|
return 42.0 * dot(m * m, vec4(dot(g0, x0), dot(g1, x1), dot(g2, x2), dot(g3, x3)));
|
|
5777
5777
|
}
|
|
5778
|
-
`;function
|
|
5778
|
+
`;function rS(e){return`#include <lights_fragment_maps>
|
|
5779
5779
|
iblIrradiance *= ${e.toFixed(3)};
|
|
5780
|
-
radiance *= ${e.toFixed(3)};`}function
|
|
5780
|
+
radiance *= ${e.toFixed(3)};`}function iS(e,t){e.userData.envCut=t,e.onBeforeCompile=e=>{e.fragmentShader=e.fragmentShader.replace(`#include <lights_fragment_maps>`,rS(t))},e.customProgramCacheKey=()=>`incanto-env-cut-${t.toFixed(3)}`}function aS(e,t,n=1,r){e.userData.sway=!0,e.userData.timeUniform=t,e.userData.envCut=n,r&&(e.userData.leafFade=r);let i=r?`uniform float uLeafFadeStart;
|
|
5781
5781
|
uniform float uLeafFadeEnd;
|
|
5782
5782
|
attribute vec3 anchor;
|
|
5783
5783
|
`:``,a=r?`
|
|
@@ -5787,16 +5787,16 @@ attribute vec3 anchor;
|
|
|
5787
5787
|
#endif
|
|
5788
5788
|
vec4 ezAnchorWorld = modelMatrix * ezAnchor;
|
|
5789
5789
|
float ezFade = step(uLeafFadeStart + 1e-4, uLeafFadeEnd) * smoothstep(uLeafFadeStart, max(uLeafFadeEnd, uLeafFadeStart + 1e-4), distance(cameraPosition, ezAnchorWorld.xyz));
|
|
5790
|
-
mvPosition.xyz = mix(mvPosition.xyz, ezAnchor.xyz, ezFade);`:``;e.onBeforeCompile=e=>{e.uniforms.uTreeTime=t,r&&(e.uniforms.uLeafFadeStart=r.start,e.uniforms.uLeafFadeEnd=r.end),e.vertexShader=`uniform float uTreeTime;\n${i}${e.vertexShader}`.replace(`void main() {`,`${
|
|
5790
|
+
mvPosition.xyz = mix(mvPosition.xyz, ezAnchor.xyz, ezFade);`:``;e.onBeforeCompile=e=>{e.uniforms.uTreeTime=t,r&&(e.uniforms.uLeafFadeStart=r.start,e.uniforms.uLeafFadeEnd=r.end),e.vertexShader=`uniform float uTreeTime;\n${i}${e.vertexShader}`.replace(`void main() {`,`${nS}\nvoid main() {`).replace(`#include <project_vertex>`,`
|
|
5791
5791
|
vec4 mvPosition = vec4(transformed, 1.0);
|
|
5792
5792
|
#ifdef USE_INSTANCING
|
|
5793
5793
|
mvPosition = instanceMatrix * mvPosition;
|
|
5794
5794
|
#endif
|
|
5795
|
-
float windOffset = 2.0 * 3.14 * ezSimplex3(mvPosition.xyz / ${
|
|
5796
|
-
mvPosition.xyz += uv.y * ${
|
|
5797
|
-
0.5 * sin(uTreeTime * ${
|
|
5798
|
-
0.3 * sin(2.0 * uTreeTime * ${
|
|
5799
|
-
0.2 * sin(5.0 * uTreeTime * ${
|
|
5795
|
+
float windOffset = 2.0 * 3.14 * ezSimplex3(mvPosition.xyz / ${tS.toFixed(1)});
|
|
5796
|
+
mvPosition.xyz += uv.y * ${$x.toFixed(3)} * vec3(1.0, 0.0, 1.0) * (
|
|
5797
|
+
0.5 * sin(uTreeTime * ${eS.toFixed(2)} + windOffset) +
|
|
5798
|
+
0.3 * sin(2.0 * uTreeTime * ${eS.toFixed(2)} + 1.3 * windOffset) +
|
|
5799
|
+
0.2 * sin(5.0 * uTreeTime * ${eS.toFixed(2)} + 1.5 * windOffset)
|
|
5800
5800
|
);${a}
|
|
5801
5801
|
mvPosition = modelViewMatrix * mvPosition;
|
|
5802
5802
|
gl_Position = projectionMatrix * mvPosition;
|
|
@@ -5810,18 +5810,18 @@ attribute vec3 anchor;
|
|
|
5810
5810
|
if (diffuseColor.a < ezCutoff) discard;
|
|
5811
5811
|
#endif
|
|
5812
5812
|
#endif
|
|
5813
|
-
`).replace(`#include <lights_fragment_maps
|
|
5813
|
+
`).replace(`#include <lights_fragment_maps>`,rS(n))},e.customProgramCacheKey=r?()=>`incanto-eztree-leaf-wind-v5-fade-${n.toFixed(3)}`:()=>`incanto-eztree-leaf-wind-v4-${n.toFixed(3)}`}var oS=40,sS=1;function cS(e,t,n){e.userData.sway=!0,e.userData.timeUniform=t,e.userData.strengthUniform=n,e.onBeforeCompile=e=>{e.uniforms.uTuftTime=t,e.uniforms.uTuftWind=n,e.vertexShader=`uniform float uTuftTime;\nuniform float uTuftWind;\n${e.vertexShader}`.replace(`void main() {`,`${nS}\nvoid main() {`).replace(`#include <project_vertex>`,`
|
|
5814
5814
|
vec4 mvPosition = vec4(transformed, 1.0);
|
|
5815
5815
|
#ifdef USE_INSTANCING
|
|
5816
5816
|
mvPosition = instanceMatrix * mvPosition;
|
|
5817
5817
|
#endif
|
|
5818
|
-
float tuftPhase = 6.2832 * ezSimplex3(vec3(mvPosition.xz / ${
|
|
5818
|
+
float tuftPhase = 6.2832 * ezSimplex3(vec3(mvPosition.xz / ${oS.toFixed(1)}, 0.0));
|
|
5819
5819
|
mvPosition.xz += uv.y * uTuftWind *
|
|
5820
|
-
sin(uTuftTime * ${
|
|
5821
|
-
cos(uTuftTime * ${(1.4*
|
|
5820
|
+
sin(uTuftTime * ${sS.toFixed(1)} + tuftPhase) *
|
|
5821
|
+
cos(uTuftTime * ${(1.4*sS).toFixed(1)} + tuftPhase) * vec2(1.0, 0.8);
|
|
5822
5822
|
mvPosition = modelViewMatrix * mvPosition;
|
|
5823
5823
|
gl_Position = projectionMatrix * mvPosition;
|
|
5824
|
-
`)},e.customProgramCacheKey=()=>`incanto-tuft-wind`}var aS=[`grass`,`flowers`,`reeds`],oS=[`blades`,`simple`,`mesh`,`tufts`],sS=[`grass`,`fern`],cS={grass:.08,flowers:.14,reeds:.05},lS=[.75,1.25],uS=.05,dS=.25,fS=2e5,pS=8,mS=.05,hS=.38,gS=[.75,1.3],_S=.1,vS=.42,yS=[.6,1.6],bS=.8,xS=.9,SS=.04,CS=[`#f5f1e2`,`#e9d052`,`#8d7bd9`],wS=1/3,TS=1.5,ES=1.45,DS=.4,OS=.13,kS=[.85,1.2],AS=[.8,1.3],jS=`#b3a24e`,MS=.8,NS=.5,PS=class e extends Zv{static typeName=`Foliage3D`;static props={kind:{default:`grass`,options:aS},style:{default:`mesh`,options:oS},tuftStyle:{default:`grass`,options:sS},area:{default:[20,20]},density:{default:12},maxInstances:{default:5e4},height:{default:.25},colorA:{default:`#4d7232`},colorB:{default:`#9aab55`},groundColor:{default:`#1f3015`},sunDirection:{default:[.5,.8,.3]},fadeStart:{default:60},fadeEnd:{default:90},sway:{default:.6},interaction:{default:!0},coverage:{default:.85},flowers:{default:0},seed:{default:1},drape:{default:!1},terrain:{default:``},renderOrder:{default:2}};renderOrder=2;kind=`grass`;style=`mesh`;tuftStyle=`grass`;area=[20,20];density=12;maxInstances=5e4;height=.25;colorA=`#4d7232`;colorB=`#9aab55`;groundColor=`#1f3015`;sunDirection=[.5,.8,.3];fadeStart=60;fadeEnd=90;sway=.6;interaction=!0;coverage=.85;flowers=0;seed=1;drape=!1;terrain=``;drapeTerrain=null;drapeWorld=[0,0,0];static validateJson(e){let t=e;if(!aS.includes(t.kind))throw new y(`BAD_FORMAT`,`Foliage3D '${e.name}' kind must be one of [${aS.join(`, `)}], got '${t.kind}'.`,{prop:`kind`,validOptions:aS});if(!oS.includes(t.style))throw new y(`BAD_FORMAT`,`Foliage3D '${e.name}' style must be one of [${oS.join(`, `)}], got '${t.style}'.`,{prop:`style`,validOptions:oS});if(!sS.includes(t.tuftStyle))throw new y(`BAD_FORMAT`,`Foliage3D '${e.name}' tuftStyle must be one of [${sS.join(`, `)}], got '${t.tuftStyle}'.`,{prop:`tuftStyle`,validOptions:sS});if(!((t.area[0]??0)>0)||!((t.area[1]??0)>0))throw new y(`BAD_FORMAT`,`Foliage3D '${e.name}' area must be positive [x, z] meters, got ${JSON.stringify(t.area)}.`,{prop:`area`});if(!(t.density>0)||!(t.maxInstances>=1))throw new y(`BAD_FORMAT`,`Foliage3D '${e.name}' needs density > 0 and maxInstances >= 1, got density ${t.density}, maxInstances ${t.maxInstances}.`,{prop:`density`});if(t.maxInstances>fS)throw new y(`BAD_FORMAT`,`Foliage3D '${e.name}' maxInstances is capped at ${fS} (got ${t.maxInstances}) — split giant fields into multiple nodes.`,{prop:`maxInstances`});if(!(t.fadeStart>=0)||!(t.fadeEnd>t.fadeStart))throw new y(`BAD_FORMAT`,`Foliage3D '${e.name}' needs fadeStart >= 0 and fadeEnd > fadeStart (the LOD fade window), got fadeStart ${t.fadeStart}, fadeEnd ${t.fadeEnd}.`,{prop:`fadeStart`});let n=t.sunDirection;if(!Array.isArray(n)||n.length!==3||!n.every(e=>Number.isFinite(e))||Math.hypot(n[0]??0,n[1]??0,n[2]??0)===0)throw new y(`BAD_FORMAT`,`Foliage3D '${e.name}' sunDirection must be a non-zero [x, y, z] vector, got ${JSON.stringify(n)}.`,{prop:`sunDirection`});if(!(t.coverage>0)||!(t.coverage<=1))throw new y(`BAD_FORMAT`,`Foliage3D '${e.name}' coverage must be in (0, 1] (carpet fill — 1 is solid, lower carves dirt patches), got ${t.coverage}.`,{prop:`coverage`});if(!(t.flowers>=0)||!(t.flowers<=.2))throw new y(`BAD_FORMAT`,`Foliage3D '${e.name}' flowers must be in [0, 0.2] (fraction of instances rendered as flower heads — for a whole flower FIELD use kind: 'flowers'), got ${t.flowers}.`,{prop:`flowers`});if(typeof t.drape!=`boolean`)throw new y(`BAD_FORMAT`,`Foliage3D '${e.name}' drape must be a boolean, got ${JSON.stringify(t.drape)}.`,{prop:`drape`});if(typeof t.terrain!=`string`)throw new y(`BAD_FORMAT`,`Foliage3D '${e.name}' terrain must be a node-path string (empty = auto-find), got ${JSON.stringify(t.terrain)}.`,{prop:`terrain`})}time=0;pivot=null;meshes=[];bladesMaterial=null;benderSlots=null;pickedBenders=[];fieldKey=``;tuftTime=null;tuftStrength=null;tuftTexture=null;_createObject3D(){return new Fi}get isBlades(){return this.kind===`grass`&&this.style===`blades`}get isMesh(){return this.kind===`grass`&&this.style===`mesh`}get isTufts(){return this.kind===`grass`&&this.style===`tufts`}get isShaderField(){return this.isMesh||this.isBlades}_field(){return this.rebuildIfNeeded(),this.meshes[0]}_fields(){return this.rebuildIfNeeded(),this.meshes}update(e){this.time+=e,this.pickedBenders=this.computeBenders()}_syncObject3D(){super._syncObject3D(),this.rebuildIfNeeded();let e=this.pivot;if(this.isShaderField){e.matrix.identity();let t=this.bladesMaterial.uniforms;t.time&&(t.time.value=this.time),t.windStrength&&(t.windStrength.value=this.sway*(this.isMesh?hS:dS)),(t.bottomColor?.value).set(this.colorA),(t.topColor?.value).set(this.colorB),this.isMesh&&((t.groundColor?.value).set(this.groundColor),(t.sunDirection?.value).set(this.sunDirection[0]??0,this.sunDirection[1]??1,this.sunDirection[2]??0).normalize(),t.fadeStart&&(t.fadeStart.value=this.fadeStart),t.fadeEnd&&(t.fadeEnd.value=this.fadeEnd));let n=this.benderSlots;for(let e=0;e<4;e++){let t=this.pickedBenders[e];t?n[e]?.set(t.x,t.y,t.z,t.radius):n[e]?.set(0,0,0,0)}}else if(this.isTufts)e.matrix.identity(),this.tuftTime&&(this.tuftTime.value=this.time),this.tuftStrength&&(this.tuftStrength.value=this.sway*OS);else if(this.sway>0){let t=Math.sin(this.time*1.7)*uS*this.sway,n=Math.sin(this.time*1.3+1.7)*uS*this.sway*.6;e.matrix.makeShear(0,0,t,n,0,0)}else e.matrix.identity();e.matrixWorldNeedsUpdate=!0}_applySunDirection(t){if(!this.isMesh||!this.bladesMaterial)return;let n=e.props.sunDirection?.default;this.sunDirection.every((e,t)=>e===n[t])&&(this.bladesMaterial.uniforms.sunDirection?.value)?.set(t[0],t[1],t[2])}computeBenders(){if(!this.interaction||!this.isShaderField)return[];let[e,t,n]=fx(this);this.drape&&!this.drapeTerrain&&this.resolveDrape();let r=this.drapeTerrain,i=[];for(let e of Hx(this)){let n=r?r.heightAt(e.x,e.z):t,a=e.footY-n;a<=NS&&a>=-2.5&&i.push({x:e.x,y:e.y,z:e.z,radius:e.radius+MS})}return kx(i,{x:e,z:n,areaX:this.area[0]??0,areaZ:this.area[1]??0})}rebuildIfNeeded(){this.pivot||(this.pivot=new Fi,this.pivot.matrixAutoUpdate=!1,this._ensureObject3D().add(this.pivot));let e=JSON.stringify([this.kind,this.style,this.tuftStyle,this.area,this.density,this.maxInstances,this.height,this.colorA,this.colorB,this.coverage,this.flowers,this.seed,this.drape,this.terrain]);if(e===this.fieldKey&&this.meshes.length>0)return;this.fieldKey=e,this.disposeMeshes(),this.resolveDrape();let t=this.isMesh?pS:this.isTufts?wS:1,n=Math.min(Math.floor((this.area[0]??0)*(this.area[1]??0)*this.density*t),this.maxInstances);this.isMesh?this.buildMeshField(n):this.isTufts?this.buildTuftField(n):this.isBlades?this.buildBladesField(n):this.buildSimpleField(n);for(let e of this.meshes)dx(e),this.pivot.add(e)}resolveDrape(){if(this.drapeTerrain=null,!this.drape)return;let e=px(this,this.terrain);if(e)this.drapeTerrain=e,this.drapeWorld=fx(this);else if(this.terrain!==``)throw new y(`NODE_NOT_FOUND`,`Foliage3D '${this.name}' drape terrain '${this.terrain}' is not a Terrain3D in the tree.`,{prop:`terrain`})}bedY(e,t){let n=this.drapeTerrain;return n?n.heightAt(this.drapeWorld[0]+e,this.drapeWorld[2]+t)-this.drapeWorld[1]:0}buildMeshField(e){let t=Kb({seed:this.seed,areaX:this.area[0]??0,areaZ:this.area[1]??0,count:e,density:this.density*pS}),n=this.coverage>=1?t:t.filter(e=>Gb(e.x,e.z,this.seed)<=this.coverage),r=this.flowers>0?Math.floor(n.length*this.flowers):0,i=new Set;for(let e=0;e<r;e++)i.add(Math.floor((e+.5)*n.length/r));let a=n.filter((e,t)=>!i.has(t)),o=n.filter((e,t)=>i.has(t));this.benderSlots=Array.from({length:4},()=>new ri(0,0,0,0));let s=Fs.merge([Y.lights,Y.fog]);Object.assign(s,{time:{value:0},windStrength:{value:this.sway*hS},topColor:{value:new K(this.colorB)},bottomColor:{value:new K(this.colorA)},groundColor:{value:new K(this.groundColor)},sunDirection:{value:new H(this.sunDirection[0]??0,this.sunDirection[1]??1,this.sunDirection[2]??0).normalize()},fadeStart:{value:this.fadeStart},fadeEnd:{value:this.fadeEnd},benders:{value:this.benderSlots}}),this.bladesMaterial=new Rs({vertexShader:Fx,fragmentShader:Ix,uniforms:s,fog:!0,lights:!0,side:2});let c=jx(mS),l=a.length,u=Math.max(l,1),d=new Vo(c,this.bladesMaterial,u);d.count=l,d.receiveShadow=!0;let f=new b(this.seed+5352221),p=new Float32Array(u*4),m=new Float32Array(u),h=new Float32Array(u);for(let e=0;e<l;e++){let t=a[e],n=Lb(t.x,t.z),r=Ub(t.x,t.z,this.seed),i=(r-.55)/1.15,o=f.range(gS[0],gS[1]),s=this.height*r*(.55+t.scale*.6);BS.set(t.x,this.bedY(t.x,t.z),t.z),VS.setFromAxisAngle(RS,t.rotY),HS.set(o,s,o),d.setMatrixAt(e,zS.compose(BS,VS,HS));let c=n.leanDir+f.range(-.6,.6),l=(_S+vS*(.5*n.lean+.5*f.next()))*(1+bS*i);p[e*4]=Math.cos(c)*l,p[e*4+1]=Math.sin(c)*l,p[e*4+2]=f.range(yS[0],yS[1])*(1+xS*i),p[e*4+3]=f.next(),m[e]=n.hue;let u=Wb(t.x,t.z,this.seed)*.85+(f.next()-.5)*.3;h[e]=Math.min(1,Math.max(0,u))}c.setAttribute(`aBlade`,new No(p,4)),c.setAttribute(`aClumpHue`,new No(m,1)),c.setAttribute(`aDry`,new No(h,1)),d.instanceMatrix.needsUpdate=!0,this.meshes=[d],o.length>0&&this.meshes.push(this.buildFlowerMesh(o))}buildFlowerMesh(e){let t=new Vo(Mx(SS),new Bs({roughness:1,side:2}),Math.max(e.length,1));t.count=e.length,t.receiveShadow=!0;let n=new b(this.seed+15797765);for(let r=0;r<e.length;r++){let i=e[r],a=Ub(i.x,i.z,this.seed),o=this.height*a*(.85+n.next()*.35);BS.set(i.x,this.bedY(i.x,i.z),i.z),VS.setFromAxisAngle(RS,i.rotY),HS.set(1,o,1),t.setMatrixAt(r,zS.compose(BS,VS,HS));let s=n.next(),c=CS[s<.45?0:s<.75?1:2];t.setColorAt(r,GS.set(c))}return t.instanceMatrix.needsUpdate=!0,t.instanceColor&&(t.instanceColor.needsUpdate=!0),t}buildTuftField(e){let t=Kb({seed:this.seed,areaX:this.area[0]??0,areaZ:this.area[1]??0,count:e,density:this.density*wS}),n=this.coverage>=1?t:t.filter(e=>Gb(e.x,e.z,this.seed)<=this.coverage);this.tuftTime={value:this.time},this.tuftStrength={value:this.sway*OS};let r=new Bs({alphaTest:.5,side:2,roughness:1}),i=Jx(this.seed,this.tuftStyle);i&&(r.map=i,this.tuftTexture=i),iS(r,this.tuftTime,this.tuftStrength);let a=new Vo(IS(),r,Math.max(n.length,1));a.count=n.length,a.castShadow=!1,a.receiveShadow=!0;let o=new b(this.seed+7403357),s=US.set(this.colorA),c=WS.set(this.colorB);for(let e=0;e<n.length;e++){let t=n[e],r=1+(Ub(t.x,t.z,this.seed)-1)*DS,i=this.height*TS*r*o.range(kS[0],kS[1]),l=i*ES*o.range(AS[0],AS[1]);BS.set(t.x,this.bedY(t.x,t.z),t.z),VS.setFromAxisAngle(RS,t.rotY),HS.set(l,i,l),a.setMatrixAt(e,zS.compose(BS,VS,HS));let u=Lb(t.x,t.z),d=Math.min(1,Math.max(0,.5+.35*u.hue+(o.next()-.5)*.3)),f=Math.min(1,Math.max(0,Wb(t.x,t.z,this.seed)*.8-.1));GS.copy(s).lerp(c,d).lerp(KS.set(jS),f*.5).multiplyScalar(o.range(.62,1.18)),a.setColorAt(e,GS)}a.instanceMatrix.needsUpdate=!0,a.instanceColor&&(a.instanceColor.needsUpdate=!0),this.meshes=[a]}buildBladesField(e){let t=Kb({seed:this.seed,areaX:this.area[0]??0,areaZ:this.area[1]??0,count:e,density:this.density});this.benderSlots=Array.from({length:4},()=>new ri(0,0,0,0));let n=Fs.merge([Y.fog]);Object.assign(n,{time:{value:0},windStrength:{value:this.sway*dS},topColor:{value:new K(this.colorB)},bottomColor:{value:new K(this.colorA)},grassHeight:{value:this.height},grassScale:{value:1},benders:{value:this.benderSlots}}),this.bladesMaterial=new Rs({vertexShader:Nx,fragmentShader:Px,uniforms:n,fog:!0,transparent:!0,side:2,depthWrite:!0,depthTest:!0,alphaTest:.1,blending:0});let r=FS(this.height),i=new Vo(r,this.bladesMaterial,Math.max(e,1)),a=new Vo(r,this.bladesMaterial,Math.max(e,1));i.count=e,a.count=e,i.renderOrder=this.renderOrder,a.renderOrder=this.renderOrder;for(let n=0;n<e;n++){let e=t[n];BS.set(e.x,this.bedY(e.x,e.z),e.z),HS.setScalar(e.scale),VS.setFromAxisAngle(RS,e.rotY),i.setMatrixAt(n,zS.compose(BS,VS,HS)),VS.setFromAxisAngle(RS,e.rotY+Math.PI/2),a.setMatrixAt(n,zS.compose(BS,VS,HS))}i.instanceMatrix.needsUpdate=!0,a.instanceMatrix.needsUpdate=!0,this.meshes=[i,a]}buildSimpleField(e){let t=new Bs({side:2,roughness:1}),n=new Vo(LS(cS[this.kind]??.08),t,Math.max(e,1));n.count=e,n.receiveShadow=!0;let r=new b(this.seed),i=(this.area[0]??0)/2,a=(this.area[1]??0)/2,o=US.set(this.colorA),s=WS.set(this.colorB);for(let t=0;t<e;t++){let e=r.range(-i,i),c=r.range(-a,a);BS.set(e,this.bedY(e,c),c),VS.setFromAxisAngle(RS,r.range(0,Math.PI*2)),HS.set(1,this.height*r.range(lS[0],lS[1]),1),zS.compose(BS,VS,HS),n.setMatrixAt(t,zS),n.setColorAt(t,GS.copy(o).lerp(s,r.next()))}n.instanceMatrix.needsUpdate=!0,n.instanceColor&&(n.instanceColor.needsUpdate=!0),this.meshes=[n]}disposeMeshes(){let e=new Set(this.meshes.map(e=>e.geometry)),t=new Set(this.meshes.map(e=>e.material));for(let e of this.meshes)e.removeFromParent(),e.dispose();for(let t of e)t.dispose();for(let e of t)e.dispose?.();this.bladesMaterial?.dispose(),this.bladesMaterial=null,this.benderSlots=null,this.tuftTexture?.dispose(),this.tuftTexture=null,this.tuftTime=null,this.tuftStrength=null,this.meshes=[]}free(){this.disposeMeshes(),this.pivot=null,super.free()}};function FS(e){let t=new Os(1,e,1,1);return t.translate(0,e/2,0),t}function IS(){let e=[],t=[],n=[],r=[];for(let i=0;i<3;i++){let a=i*Math.PI/3,o=Math.cos(a)/2,s=Math.sin(a)/2,c=-Math.sin(a),l=Math.cos(a),u=i*4;e.push(-o,0,-s,o,0,s,-o,1,-s,o,1,s);for(let e=0;e<4;e++)t.push(c,0,l);n.push(0,0,1,0,0,1,1,1),r.push(u,u+1,u+3,u,u+3,u+2)}let i=new Ha;return i.setAttribute(`position`,new q(new Float32Array(e),3)),i.setAttribute(`normal`,new q(new Float32Array(t),3)),i.setAttribute(`uv`,new q(new Float32Array(n),2)),i.setIndex(r),i}function LS(e){let t=e/2,n=new Float32Array([-t,0,0,t,0,0,-t,1,0,t,1,0,0,0,-t,0,0,t,0,1,-t,0,1,t]),r=new Float32Array([0,0,1,0,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,0,1,0,0]),i=new Ha;return i.setAttribute(`position`,new q(n,3)),i.setAttribute(`normal`,new q(r,3)),i.setIndex([0,1,2,2,1,3,4,5,6,6,5,7]),i}var RS=new H(0,1,0),zS=new G,BS=new H,VS=new V,HS=new H,US=new K,WS=new K,GS=new K,KS=new K,qS=class extends Zv{static typeName=`DirectionalLight3D`;static props={color:{default:`#ffffff`},intensity:{default:1},castShadow:{default:!1},shadowArea:{default:75},shadowMapSize:{default:2048},shadowFollowsCamera:{default:!1}};color=`#ffffff`;intensity=1;castShadow=!1;shadowArea=75;shadowMapSize=2048;shadowFollowsCamera=!1;_createObject3D(){return new Bc}_syncObject3D(){super._syncObject3D();let e=this._ensureObject3D();if(e.color.set(this.color),e.intensity=this.intensity,e.castShadow=this.castShadow,this.castShadow&&`shadow`in e){let t=e.shadow.camera;`left`in t&&(t.left=-this.shadowArea,t.right=this.shadowArea,t.top=this.shadowArea,t.bottom=-this.shadowArea,t.near=.5,t.far=500,t.updateProjectionMatrix()),e.shadow.mapSize.set(this.shadowMapSize,this.shadowMapSize),e.shadow.bias=-2e-4,e.shadow.normalBias=4*this.shadowArea/this.shadowMapSize}}},JS=class extends Zv{static typeName=`OmniLight3D`;static props={color:{default:`#ffffff`},intensity:{default:1},range:{default:0}};color=`#ffffff`;intensity=1;range=0;_createObject3D(){return new Lc}_syncObject3D(){super._syncObject3D();let e=this._ensureObject3D();e.color.set(this.color),e.intensity=this.intensity,e.distance=this.range}},YS=[`color`,`metalness`,`roughness`,`emissive`,`emissiveIntensity`,`wireframe`,`flatShading`,`opacity`,`map`,`normalMap`,`repeat`],XS=class extends Zv{static typeName=`MeshInstance3D`;static props={mesh:{default:`box`,options:[`box`,`sphere`,`capsule`,`plane`,`cylinder`,`gem`]},size:{default:[1,1,1]},material:{default:{}},castShadow:{default:!1},receiveShadow:{default:!1}};mesh=`box`;size=[1,1,1];material={};castShadow=!1;receiveShadow=!1;static validateJson(e){ZS(e.material,e.name)}geometryKey=``;textureKey=``;loadedTextures=[];_createObject3D(){return new ho(new Ss(1,1,1),new Bs)}_syncObject3D(){super._syncObject3D();let e=this._ensureObject3D(),t=`${this.mesh}:${JSON.stringify(this.size)}`;t!==this.geometryKey&&(e.geometry.dispose(),e.geometry=QS(this.mesh,this.size),this.geometryKey=t);let n=e.material;n.color.set(this.material.color??`#ffffff`),n.metalness=this.material.metalness??0,n.roughness=this.material.roughness??1,n.wireframe=this.material.wireframe??!1,n.emissive.set(this.material.emissive??`#000000`),n.emissiveIntensity=this.material.emissiveIntensity??1;let r=this.material.opacity??1;n.opacity=r,n.transparent=r<1;let i=this.material.flatShading??!1;n.flatShading!==i&&(n.flatShading=i,n.needsUpdate=!0),this.syncTextures(n),e.castShadow=this.castShadow,e.receiveShadow=this.receiveShadow}syncTextures(e){let t=JSON.stringify([this.material.map??``,this.material.normalMap??``,this.material.repeat??null]);if(t===this.textureKey)return;this.textureKey=t;for(let e of this.loadedTextures)e.dispose();this.loadedTextures=[];let n=e.map!==null||e.normalMap!==null;e.map=null,e.normalMap=null;let r=typeof document<`u`?new bc:null;r&&this.material.map&&(e.map=this.loadTexture(r,this.material.map,!0)),r&&this.material.normalMap&&(e.normalMap=this.loadTexture(r,this.material.normalMap,!1)),(n||e.map||e.normalMap)&&(e.needsUpdate=!0)}loadTexture(e,t,n){let r=e.load(t);r.wrapS=bt,r.wrapT=bt;let[i,a]=this.material.repeat??[1,1];return r.repeat.set(i,a),r.anisotropy=4,n&&(r.colorSpace=Yn),this.loadedTextures.push(r),r}free(){for(let e of this.loadedTextures)e.dispose();this.loadedTextures=[],super.free()}};function ZS(e,t){let n=e=>{throw new y(`BAD_FORMAT`,`MeshInstance3D '${t}' ${e}`,{prop:`material`,validOptions:[...YS]})};(typeof e!=`object`||!e||Array.isArray(e))&&n(`material must be an object, got ${JSON.stringify(e)}.`);for(let t of Object.keys(e))YS.includes(t)||n(`material has unknown key '${t}' — must be one of [${YS.join(`, `)}].`);for(let t of[`map`,`normalMap`]){let r=e[t];r!==void 0&&(typeof r!=`string`||r.length===0)&&n(`material.${t} must be a non-empty texture URL string, got ${JSON.stringify(r)}.`)}if(e.repeat!==void 0){let t=e.repeat;(!Array.isArray(t)||t.length!==2||!t.every(e=>typeof e==`number`&&Number.isFinite(e)&&e>0))&&n(`material.repeat must be [u, v] with positive numbers, got ${JSON.stringify(t)}.`),!e.map&&!e.normalMap&&n(`material.repeat tiles nothing — add a 'map' and/or 'normalMap' texture URL.`)}for(let t of[`metalness`,`roughness`,`opacity`]){let r=e[t];r!==void 0&&!(typeof r==`number`&&r>=0&&r<=1)&&n(`material.${t} must be a number in 0..1, got ${JSON.stringify(r)}.`)}for(let t of[`wireframe`,`flatShading`]){let r=e[t];r!==void 0&&typeof r!=`boolean`&&n(`material.${t} must be a boolean, got ${JSON.stringify(r)}.`)}}function QS(e,t){let n=t[0]??1,r=t[1]??1,i=t[2]??1;switch(e){case`box`:return new Ss(n,r,i);case`sphere`:return new ks(n,32,16);case`gem`:return new Ds(n,0);case`capsule`:return new Cs(n,r,8,16);case`plane`:{let e=new Os(n,i);return e.rotateX(-Math.PI/2),e}case`cylinder`:return new ws(n,n,r,24);default:throw new y(`PROP_TYPE_MISMATCH`,`MeshInstance3D.mesh must be one of box|sphere|capsule|plane|cylinder|gem, got '${e}'.`)}}var $S={Hips:`hips`,Spine:`spine`,Spine1:`chest`,Spine2:`upperChest`,Neck:`neck`,Head:`head`,LeftShoulder:`leftShoulder`,LeftArm:`leftUpperArm`,LeftForeArm:`leftLowerArm`,LeftHand:`leftHand`,RightShoulder:`rightShoulder`,RightArm:`rightUpperArm`,RightForeArm:`rightLowerArm`,RightHand:`rightHand`,LeftUpLeg:`leftUpperLeg`,LeftLeg:`leftLowerLeg`,LeftFoot:`leftFoot`,LeftToeBase:`leftToes`,RightUpLeg:`rightUpperLeg`,RightLeg:`rightLowerLeg`,RightFoot:`rightFoot`,RightToeBase:`rightToes`,LeftHandThumb1:`leftThumbMetacarpal`,LeftHandThumb2:`leftThumbProximal`,LeftHandThumb3:`leftThumbDistal`,LeftHandIndex1:`leftIndexProximal`,LeftHandIndex2:`leftIndexIntermediate`,LeftHandIndex3:`leftIndexDistal`,LeftHandMiddle1:`leftMiddleProximal`,LeftHandMiddle2:`leftMiddleIntermediate`,LeftHandMiddle3:`leftMiddleDistal`,LeftHandRing1:`leftRingProximal`,LeftHandRing2:`leftRingIntermediate`,LeftHandRing3:`leftRingDistal`,LeftHandPinky1:`leftLittleProximal`,LeftHandPinky2:`leftLittleIntermediate`,LeftHandPinky3:`leftLittleDistal`,RightHandThumb1:`rightThumbMetacarpal`,RightHandThumb2:`rightThumbProximal`,RightHandThumb3:`rightThumbDistal`,RightHandIndex1:`rightIndexProximal`,RightHandIndex2:`rightIndexIntermediate`,RightHandIndex3:`rightIndexDistal`,RightHandMiddle1:`rightMiddleProximal`,RightHandMiddle2:`rightMiddleIntermediate`,RightHandMiddle3:`rightMiddleDistal`,RightHandRing1:`rightRingProximal`,RightHandRing2:`rightRingIntermediate`,RightHandRing3:`rightRingDistal`,RightHandPinky1:`rightLittleProximal`,RightHandPinky2:`rightLittleIntermediate`,RightHandPinky3:`rightLittleDistal`},eC={};for(let[e,t]of Object.entries($S))eC[`mixamorig${e}`]=t,eC[`mixamorig:${e}`]=t;function tC(e,t,n){let r=[],i=new Set,a=new V,o=new V,s=new V,c=n.meta?.metaVersion===`0`,l=t.getObjectByName(`mixamorigHips`)??t.getObjectByName(`mixamorig:Hips`),u=n.humanoid?.getNormalizedBoneNode(`hips`),d=l&&u?Math.abs(u.getWorldPosition(new H).y)/Math.max(1e-6,Math.abs(l.getWorldPosition(new H).y)):1;for(let l of e.tracks){let e=l.name.lastIndexOf(`.`),u=l.name.slice(0,e),f=l.name.slice(e+1),p=eC[u],m=p?n.humanoid?.getNormalizedBoneNode(p)?.name:void 0,h=t.getObjectByName(u);if(!m||!h){i.add(u);continue}if(h.getWorldQuaternion(a).invert(),h.parent?h.parent.getWorldQuaternion(o):o.identity(),l instanceof ic&&f===`quaternion`){let e=Float32Array.from(l.values);for(let t=0;t<e.length;t+=4)s.set(e[t]??0,e[t+1]??0,e[t+2]??0,e[t+3]??1),s.premultiply(o).multiply(a),e[t]=c?-s.x:s.x,e[t+1]=s.y,e[t+2]=c?-s.z:s.z,e[t+3]=s.w;r.push(new ic(`${m}.quaternion`,[...l.times],[...e]))}else if(l instanceof oc&&f===`position`){let e=Float32Array.from(l.values);for(let t=0;t<e.length;t+=3)e[t]=(c?-(e[t]??0):e[t]??0)*d,e[t+1]=(e[t+1]??0)*d,e[t+2]=(c?-(e[t+2]??0):e[t+2]??0)*d;r.push(new oc(`${m}.position`,[...l.times],[...e]))}}return{clip:new sc(`${e.name} (VRM)`,e.duration,r),retargetedTracks:r.length,skippedBones:[...i]}}var nC=.2,rC=class extends Zv{static typeName=`ModelInstance3D`;static signals=[`animationFinished`];static props={...Zv.props,model:{default:``},animation:{default:``},targetHeight:{default:0},castShadow:{default:!1},animationLoop:{default:!0},receiveShadow:{default:!1},tint:{default:``}};model=``;animation=``;targetHeight=0;castShadow=!1;animationLoop=!0;receiveShadow=!1;tint=``;store=null;entry=null;mountedRef=``;fittedHeight=-1;fitGroup=null;mixer=null;playing=``;currentAction=null;actionKeys=new WeakMap;warnedClaim=!1;tintTargets=[];tintedClones=[];appliedTint=``;_createObject3D(){return new Fi}availableAnimations(){let e=this.entry?.animations.map(e=>e.name)??[],t=this.store?.animationRefs()??[];return[...e,...t]}_syncModel(e){this.store=e;let t=this._ensureObject3D();if(this.model===``){this.fitGroup&&this.unmount(t);return}let n=e.getModel(this.model);if(!(n.status!==`ready`||!n.scene)){if(this.mountedRef!==this.model){this.unmount(t);let e;if(n.isVrm){if(n.claimedBy&&n.claimedBy!==this){this.warnedClaim||(this.warnedClaim=!0,console.warn(`incanto: VRM '${this.model}' is already displayed by another node — a VRM asset can back only one ModelInstance3D.`));return}n.claimedBy=this,e=n.scene}else e=V_(n.scene);let r=new Fi;r.add(e),t.add(r),this.fitGroup=r,this.entry=n,this.mountedRef=this.model,this.mixer=new ul(e),this.mixer.addEventListener(`finished`,e=>{let t=e.action,n=(t&&this.actionKeys.get(t))??this.animation;this.emit(`animationFinished`,n)}),this.playing=``,this.currentAction=null,this.fittedHeight=-1,e.traverse(e=>{e.userData.incantoModelShared=!0}),this.tintTargets=[],this.tintedClones=[],this.appliedTint=``,e.traverse(e=>{let t=e;t.isMesh&&t.material&&this.tintTargets.push({mesh:t,original:t.material})})}if(this.fitGroup&&this.fittedHeight!==this.targetHeight)if(this.fittedHeight=this.targetHeight,this.targetHeight>0){let e=this.fitGroup.scale.x;this.fitGroup.scale.setScalar(1);let t=iC(this.fitGroup).getSize(new H),n=t.y>0?this.targetHeight/t.y:e;n<.05||n>20?(this.fitGroup.scale.setScalar(1),console.warn(`[incanto] ModelInstance3D '${this.name}': targetHeight fit measured an implausible scale (${n.toFixed(2)}x) — skinned rigs defeat bounding-box measurement. Rendering at the model's authored scale instead.`)):this.fitGroup.scale.setScalar(n)}else this.fitGroup.scale.setScalar(1);this.fitGroup&&this.fitGroup.traverse(e=>{let t=e;t.isMesh&&(t.castShadow=this.castShadow,t.receiveShadow=this.receiveShadow)}),this.applyTint(),this.syncAnimation(e)}}applyTint(){if(this.tint===this.appliedTint)return;for(let e of this.tintedClones)e.dispose();this.tintedClones=[];let e=this.tint?new K(this.tint):null;for(let t of this.tintTargets){if(!e){t.mesh.material=t.original;continue}let n=t=>{let n=t.clone(),r=n,i=t.color;return r.color&&=i?i.clone().multiply(e):e.clone(),n.userData.incantoTinted=!0,this.tintedClones.push(n),n};t.mesh.material=Array.isArray(t.original)?t.original.map(n):n(t.original)}this.appliedTint=this.tint}syncAnimation(e){if(!this.mixer||!this.entry||this.playing===this.animation)return;if(this.animation===``){this.currentAction&&this.currentAction.fadeOut(nC),this.currentAction=null,this.playing=``;return}let t=this.entry.animations.find(e=>e.name===this.animation)??null;if(!t&&(this.animation.startsWith(`$`)||/\.(glb|gltf)$/i.test(this.animation))){let n=e.getAnimation(this.animation);if(n.status===`loading`)return;if(n.status===`ready`&&n.scene){let e=(n.clip?n.clips.find(e=>e.name===n.clip):void 0)??n.clips[0];if(e)if(this.entry.isVrm&&this.entry.vrm){let r=this.entry.retargeted.get(this.animation);if(r)t=r;else{let r=tC(e,n.scene,this.entry.vrm);r.skippedBones.length>0&&console.warn(`incanto: retarget '${this.animation}' skipped bones: ${r.skippedBones.join(`, `)}`),this.entry.retargeted.set(this.animation,r.clip),t=r.clip}}else t=e}}if(this.playing=this.animation,t){let e=this.mixer.clipAction(t);if(e.reset(),this.animationLoop?e.setLoop(In,1/0):(e.setLoop(Fn,1),e.clampWhenFinished=!0),e.setEffectiveWeight(1),e.play(),this.actionKeys.set(e,this.animation),this.currentAction&&this.currentAction!==e){let n=t.duration>0&&this.currentAction.getClip().duration>0;e.crossFadeFrom(this.currentAction,nC,n)}this.currentAction=e}else console.warn(`incanto: model '${this.model}' has no animation '${this.animation}' — embedded: ${this.entry.animations.map(e=>e.name).join(`, `)||`(none)`}; assets: ${e.animationRefs().join(`, `)||`(none)`}`)}update(e){this.mixer?.update(e),this.entry?.isVrm&&this.entry.claimedBy===this&&this.entry.vrm?.update(e)}onExitTree(){this.unmount(this._ensureObject3D())}unmount(e){this.entry?.claimedBy===this&&(this.entry.claimedBy=null),this.fitGroup&&e.remove(this.fitGroup);for(let e of this.tintedClones)e.dispose();this.tintedClones=[],this.tintTargets=[],this.appliedTint=``,this.fitGroup=null,this.mixer=null,this.currentAction=null,this.entry=null,this.mountedRef=``,this.playing=``,this.fittedHeight=-1}};function iC(e){e.updateWorldMatrix(!0,!0);let t=new aa,n=new aa,r=e.matrixWorld.clone().invert(),i=new G,a=new H;return e.traverse(e=>{let o=e;if(o.isSkinnedMesh&&o.skeleton){for(let e of o.skeleton.boneInverses)i.copy(e).invert().premultiply(o.matrixWorld).premultiply(r),a.setFromMatrixPosition(i),t.expandByPoint(a);return}let s=e;s.isMesh&&s.geometry&&(s.geometry.computeBoundingBox(),s.geometry.boundingBox&&(n.copy(s.geometry.boundingBox).applyMatrix4(s.matrixWorld).applyMatrix4(r),t.union(n)))}),t}var aC=.01,oC=class e extends Zv{static typeName=`Particles3D`;static signals=[`finished`];static props={preset:{default:`custom`,options:[`custom`,...He]},emitting:{default:!0},rate:{default:40},burst:{default:0},lifetime:{default:[.6,1.2]},speed:{default:[40,120]},directionDeg:{default:-90},spreadDeg:{default:30},gravity:{default:[0,0]},drag:{default:0},sizeStart:{default:8},sizeEnd:{default:2},colorStart:{default:`#ffffff`},colorEnd:{default:`#ffffff`},paletteColors:{default:[]},alphaStart:{default:1},alphaEnd:{default:0},shimmer:{default:0},blend:{default:`add`,options:[`add`,`normal`]},depthTest:{default:!0},maxParticles:{default:256}};preset=`custom`;emitting=!0;rate=40;burst=0;lifetime=[.6,1.2];speed=[40,120];directionDeg=-90;spreadDeg=30;gravity=[0,0];drag=0;sizeStart=8;sizeEnd=2;colorStart=`#ffffff`;colorEnd=`#ffffff`;paletteColors=[];alphaStart=1;alphaEnd=0;shimmer=0;blend=`add`;depthTest=!0;maxParticles=256;static validateJson(e){let t=e;if(t.preset!==`custom`&&!He.includes(t.preset))throw new y(`BAD_FORMAT`,`Particles3D '${e.name}' preset must be 'custom' or one of [${He.join(`, `)}], got '${t.preset}'.`,{prop:`preset`,validOptions:He});if(t.blend!==`add`&&t.blend!==`normal`)throw new y(`BAD_FORMAT`,`Particles3D '${e.name}' blend must be 'add' or 'normal', got '${t.blend}'.`,{prop:`blend`,validOptions:[`add`,`normal`]})}sim=null;bursted=!1;finishedEmitted=!1;_ensureSim(){if(!this.sim){Ue(this,this.preset,fe(e.typeName));let t=this.tree?.engine?.rng??new b(4660);this.sim=new Ge({rate:this.emitting?this.rate:0,lifetime:[this.lifetime[0]??1,this.lifetime[1]??1],speed:[(this.speed[0]??0)*aC,(this.speed[1]??0)*aC],directionDeg:this.directionDeg,spreadDeg:this.spreadDeg,gravity:[(this.gravity[0]??0)*aC,(this.gravity[1]??0)*aC,0],drag:this.drag,maxParticles:this.maxParticles,spreadZ:!0},t)}return this.sim}update(e){let t=this._ensureSim();this.burst>0&&!this.bursted&&(this.bursted=!0,t.burst(this.burst)),t.update(e),(!this.emitting||this.rate===0)&&this.bursted&&t.done&&!this.finishedEmitted&&(this.finishedEmitted=!0,this.emit(`finished`))}replay(){this.bursted=!1,this.finishedEmitted=!1}points=null;positions=null;colors=null;paletteCache=[];paletteCacheKey=``;_syncObject3D(){super._syncObject3D(),this.syncParticles()}refreshPalette(){let e=this.paletteColors;if(e.length===0)return null;let t=e.join(`|`);if(t!==this.paletteCacheKey){this.paletteCacheKey=t;for(let t=0;t<e.length;t++){let n=this.paletteCache[t];n||(n=new K,this.paletteCache[t]=n),n.set(e[t])}this.paletteCache.length=e.length}return this.paletteCache}syncParticles(){let e=this._ensureSim();if(!this.points||(this.positions?.length??0)!==this.maxParticles*3){this.points?.removeFromParent(),this.positions=new Float32Array(this.maxParticles*3),this.colors=new Float32Array(this.maxParticles*4);let e=new Ha;e.setAttribute(`position`,new q(this.positions,3)),e.setAttribute(`color`,new q(this.colors,4));let t=new us({size:(this.sizeStart+this.sizeEnd)/2*aC,map:dC(),vertexColors:!0,transparent:!0,depthWrite:!1,depthTest:this.depthTest,alphaTest:.02,sizeAttenuation:!0,blending:this.blend===`add`?2:1});this.points=new hs(e,t),this.points.frustumCulled=!1,this._ensureObject3D().add(this.points)}this.points.renderOrder=this.renderOrder;let t=this.positions,n=this.colors,r=this.refreshPalette(),i=r?null:sC.set(this.colorStart),a=cC.set(this.colorEnd),o=this.shimmer,s=0;e.forEach(e=>{t[s*3]=e.x,t[s*3+1]=-e.y,t[s*3+2]=e.z;let c=r?r[Math.floor(e.seed*r.length)]:i,l=Math.max(0,this.alphaStart+(this.alphaEnd-this.alphaStart)*e.t);o>0&&(l*=.5+.5*Math.sin(2*Math.PI*(e.age*o+e.seed))),lC.copy(c).lerp(a,e.t),n[s*4]=lC.r,n[s*4+1]=lC.g,n[s*4+2]=lC.b,n[s*4+3]=l,s+=1});let c=this.points.geometry;c.setDrawRange(0,s),c.getAttribute(`position`).needsUpdate=!0,c.getAttribute(`color`).needsUpdate=!0}},sC=new K,cC=new K,lC=new K,uC=null;function dC(){if(uC)return uC;let e=63/2,t=new Uint8Array(4096*4);for(let n=0;n<64;n++)for(let r=0;r<64;r++){let i=(r-e)/e,a=(n-e)/e,o=Math.hypot(i,a),s=.35,c;if(o<=s)c=1;else{let e=Math.max(0,1-(o-s)/(1-s));c=e*e}let l=Math.round(c*255),u=(n*64+r)*4;t[u]=255,t[u+1]=255,t[u+2]=255,t[u+3]=l}let n=new ko(t,64,64,Wt);return n.magFilter=Et,n.minFilter=Et,n.generateMipmaps=!1,n.needsUpdate=!0,uC=n,n}function fC(e){let{branch:t,leaves:n,type:r}=e,i=0,a=0,o=[{level:0,sections:t.sections[0],segments:t.segments[0]}];for(;o.length>0;){let e=o.shift();if(i+=e.sections*e.segments*2,e.level>=t.levels){a+=n.count===0?0:n.count+ +(r===`deciduous`);continue}r===`deciduous`&&o.push({level:e.level+1,sections:e.sections,segments:e.segments});let s=t.children[e.level]??0;for(let n=0;n<s;n++)o.push({level:e.level+1,sections:t.sections[e.level+1]??1,segments:t.segments[e.level+1]??3})}let s=n.billboard===`double`?2:1,c=a*s*2;return{branches:i,leaves:c,total:i+c}}var pC={ash:{type:`deciduous`,branch:{levels:3,angle:[0,48,75,60],children:[7,4,3,0],force:{direction:{x:0,y:1,z:0},strength:-.06},gnarliness:[.03,.25,.2,.09],length:[43.47,27.14,9.51,4.6],radius:[2,.63,.76,.7],sections:[12,8,6,4],segments:[12,6,4,3],start:[0,.23,.33,0],taper:[.7,.7,.7,.7],twist:[.09,-.07,0,0]},leaves:{style:`ash`,billboard:`double`,angle:55,count:16,start:0,size:2.67,sizeVariance:.72,alphaTest:.5}},aspen:{type:`deciduous`,branch:{levels:2,angle:[0,75,32,7],children:[10,3,3,0],force:{direction:{x:0,y:1,z:0},strength:.0148},gnarliness:[.05,.12,.12,.02],length:[50,6.07,11.19,1],radius:[.72,.41,.7,.7],sections:[12,10,8,6],segments:[8,6,4,3],start:[0,.59,.35,0],taper:[.37,.13,.7,.7],twist:[0,0,0,0]},leaves:{style:`aspen`,billboard:`double`,angle:30,count:11,start:.124,size:2.5,sizeVariance:.7,alphaTest:.5}},oak:{type:`deciduous`,branch:{levels:3,angle:[0,54,58,32],children:[6,4,3,0],force:{direction:{x:0,y:1,z:0},strength:-.01},gnarliness:[0,-.1,-.15,.09],length:[37.24,11.08,12.39,7.16],radius:[1.41,.9,.69,1.19],sections:[8,6,3,1],segments:[7,5,3,3],start:[0,.49,.06,.12],taper:[.73,.42,.69,.75],twist:[-.23,.42,0,0]},leaves:{style:`oak`,billboard:`double`,angle:42,count:18,start:.16,size:2.5,sizeVariance:.7,alphaTest:.5}},pine:{type:`evergreen`,branch:{levels:1,angle:[0,110,16,60],children:[82,3,5,0],force:{direction:{x:0,y:1,z:0},strength:-.003},gnarliness:[.05,.08,0,0],length:[50,23.87,14.08,1],radius:[1.05,.36,.7,.7],sections:[12,10,8,6],segments:[8,6,4,3],start:[0,.27,.14,.3],taper:[.7,.7,.7,.7],twist:[0,0,0,0]},leaves:{style:`pine`,billboard:`double`,angle:39,count:30,start:.09,size:1.435,sizeVariance:.201,alphaTest:.3}},bush:{type:`deciduous`,branch:{levels:3,angle:[0,21.5,62.6,60],children:[7,3,2,0],force:{direction:{x:0,y:1,z:0},strength:-.02},gnarliness:[.11,.09,.05,.09],length:[.1,15.3,5.59,4.6],radius:[.58,.95,.76,.7],sections:[6,6,4,3],segments:[4,4,4,3],start:[0,.53,.33,0],taper:[.7,.7,.7,.7],twist:[.3,-.07,0,0]},leaves:{style:`ash`,billboard:`double`,angle:55,count:8,start:0,size:2.45,sizeVariance:.717,alphaTest:.5}},"ash-forest":{type:`deciduous`,branch:{levels:2,angle:[0,48,75,60],children:[5,3,0,0],force:{direction:{x:0,y:1,z:0},strength:-.06},gnarliness:[.03,.25,.2,.09],length:[43.47,27.14,9.51,4.6],radius:[2,.63,.76,.7],sections:[6,5,4,4],segments:[6,4,3,3],start:[0,.3,.33,0],taper:[.7,.7,.7,.7],twist:[.09,-.07,0,0]},leaves:{style:`ash`,billboard:`double`,angle:55,count:15,start:0,size:3,sizeVariance:.6,alphaTest:.5}},"oak-forest":{type:`deciduous`,branch:{levels:2,angle:[0,54,58,32],children:[5,3,0,0],force:{direction:{x:0,y:1,z:0},strength:-.01},gnarliness:[0,-.1,-.15,.09],length:[37.24,11.08,12.39,7.16],radius:[1.41,.9,.69,1.19],sections:[6,4,3,1],segments:[6,4,3,3],start:[0,.49,.06,.12],taper:[.73,.42,.69,.75],twist:[-.23,.42,0,0]},leaves:{style:`oak`,billboard:`double`,angle:42,count:16,start:.1,size:2.9,sizeVariance:.7,alphaTest:.5}},"pine-forest":{type:`evergreen`,branch:{levels:1,angle:[0,110,16,60],children:[44,3,5,0],force:{direction:{x:0,y:1,z:0},strength:-.005},gnarliness:[.07,.09,0,0],length:[50,23.87,14.08,1],radius:[1.05,.36,.7,.7],sections:[6,5,8,6],segments:[6,3,4,3],start:[0,.27,.14,.3],taper:[.7,.7,.7,.7],twist:[0,0,0,0]},leaves:{style:`pine`,billboard:`double`,angle:39,count:9,start:.09,size:1.8,sizeVariance:.201,alphaTest:.3}},"bush-forest":{type:`deciduous`,branch:{levels:2,angle:[0,21.5,62.6,60],children:[6,3,0,0],force:{direction:{x:0,y:1,z:0},strength:-.02},gnarliness:[.11,.09,.05,.09],length:[.1,15.3,5.59,4.6],radius:[.58,.95,.76,.7],sections:[5,4,3,3],segments:[4,3,3,3],start:[0,.53,.33,0],taper:[.7,.7,.7,.7],twist:[.3,-.07,0,0]},leaves:{style:`ash`,billboard:`double`,angle:55,count:10,start:0,size:3.2,sizeVariance:.6,alphaTest:.5}}},mC=[`ash`,`aspen`,`oak`,`pine`,`bush`,`ash-forest`,`oak-forest`,`pine-forest`,`bush-forest`];function hC(e){let t=pC[e];if(!t)throw new y(`BAD_FORMAT`,`Unknown eztree preset '${e}' — must be one of [${mC.join(`, `)}].`,{prop:`preset`,validOptions:mC});return structuredClone(t)}var gC={ash:{texture:`oak`,tint:`#cecbbe`,repeat:[.5,1/5]},aspen:{texture:`birch`,tint:`#ffffff`,repeat:[1,1]},oak:{texture:`oak`,tint:`#fff3d1`,repeat:[1,1/10]},pine:{texture:`pine`,tint:`#ffffff`,repeat:[1,1]},bush:{texture:`oak`,tint:`#cecbbe`,repeat:[.5,1/5]}};function _C(e){if(!mC.includes(e))throw new y(`BAD_FORMAT`,`Unknown eztree preset '${e}' — must be one of [${mC.join(`, `)}].`,{prop:`preset`,validOptions:mC});return structuredClone(gC[e.replace(/-forest$/,``)])}var vC=.45,yC=.45,bC=.55,xC=.15,SC=.07,CC=.035;function wC(e,t){let n=Math.imul(e+1,374761393)+Math.imul(t+1,668265263)>>>0;return n=Math.imul(n^n>>>13,1274126177)>>>0,((n^n>>>16)>>>0)/4294967296}function TC(e,t,n){let r=e.length/3,i=new Float32Array(e.length),a=new Float32Array(e.length),o=1/0,s=1/0,c=1/0,l=-1/0,u=-1/0,d=-1/0;for(let t=0;t<r;t++){let n=e[t*3],r=e[t*3+1],i=e[t*3+2];n<o&&(o=n),n>l&&(l=n),r<s&&(s=r),r>u&&(u=r),i<c&&(c=i),i>d&&(d=i)}let f=(o+l)/2,p=(s+u)/2,m=(c+d)/2,h=Math.max((l-o)/2,.001),g=Math.max((u-s)/2,.001),_=Math.max((d-c)/2,.001),v=new Map;for(let n=0;n<r;n++){let r=t[n],i=v.get(r);i||(i={x:0,y:0,z:0,n:0},v.set(r,i)),i.x+=e[n*3],i.y+=e[n*3+1],i.z+=e[n*3+2],i.n++}for(let e of v.values())e.x/=e.n,e.y/=e.n,e.z/=e.n;for(let o=0;o<r;o++){let r=e[o*3],c=e[o*3+1],l=e[o*3+2],d=(r-f)/h,y=(c-p)/g,b=(l-m)/_,x,S,C,w;if(n===`evergreen`){let e=Math.hypot(r-f,l-m);if(e<1e-6)x=0,S=1,C=0;else{let t=1/Math.hypot(1,yC);x=(r-f)/e*t,S=yC*t,C=(l-m)/e*t}let t=(c-s)/Math.max(u-s,.001),n=Math.max(xC,1-t);w=Math.min(1,Math.hypot(d,b)/n)}else{let e=d/h,t=y/g,n=b/_,r=Math.hypot(e,t,n);r<1e-6?(x=0,S=1,C=0):(x=e/r,S=t/r,C=n/r),w=Math.min(1,Math.hypot(d,y,b))}let T=v.get(t[o]);if(T){let e=r-T.x,t=c-T.y,n=l-T.z,i=Math.hypot(e,t,n);if(i>1e-6){x=x*(1-bC)+e/i*bC,S=S*(1-bC)+t/i*bC,C=C*(1-bC)+n/i*bC;let r=Math.hypot(x,S,C);r>1e-6?(x/=r,S/=r,C/=r):(x=0,S=1,C=0)}}i[o*3]=x,i[o*3+1]=S,i[o*3+2]=C;let E=vC+(1-vC)*w,D=t[o],O=E*(1-SC*wC(D,1)),k=(wC(D,2)-.5)*2*CC;a[o*3]=O*(1+k),a[o*3+1]=O,a[o*3+2]=O*(1-k)}return{normals:i,colors:a}}var EC=class e{static MASK=4294967295;mW;mZ;constructor(t){this.mW=123456789+t&e.MASK,this.mZ=987654321-t&e.MASK}random(t=1,n=0){this.mZ=36969*(this.mZ&65535)+(this.mZ>>16)&e.MASK,this.mW=18e3*(this.mW&65535)+(this.mW>>16)&e.MASK;let r=((this.mZ<<16)+(this.mW&65535)>>>0)/4294967296;return(t-n)*r+n}};function DC(e,t,n){let r=new EC(t),i={branchVerts:[],branchNormals:[],branchUvs:[],branchIndices:[],leafVerts:[],leafIndices:[],leafUvs:[],leafAnchors:[],leafClusters:[],leafClusterNext:0},a=[{origin:new H,orientation:new _i,length:e.branch.length[0],radius:e.branch.radius[0],level:0,sectionCount:e.branch.sections[0],segmentCount:e.branch.segments[0]}];for(;a.length>0;)OC(e,r,i,a,a.shift());FC(i,n);let o=new Ha;o.setAttribute(`position`,new q(new Float32Array(i.branchVerts),3)),o.setAttribute(`normal`,new q(new Float32Array(i.branchNormals),3)),o.setAttribute(`uv`,new q(new Float32Array(i.branchUvs),2)),o.setIndex(new q(IC(i.branchIndices,i.branchVerts),1)),o.computeBoundingSphere();let s=null;if(i.leafVerts.length>0){s=new Ha,s.setAttribute(`position`,new q(new Float32Array(i.leafVerts),3)),s.setAttribute(`anchor`,new q(new Float32Array(i.leafAnchors),3)),s.setAttribute(`uv`,new q(new Float32Array(i.leafUvs),2)),s.setIndex(new q(IC(i.leafIndices,i.leafVerts),1));let t=TC(i.leafVerts,i.leafClusters,e.type);s.setAttribute(`normal`,new q(t.normals,3)),s.setAttribute(`color`,new q(t.colors,3)),s.computeBoundingSphere()}return{branches:o,leaves:s}}function OC(e,t,n,r,i){let a=n.branchVerts.length/3,o=i.orientation.clone(),s=i.origin.clone(),c=i.length/i.sectionCount,l=[];for(let r=0;r<=i.sectionCount;r++){let a=i.radius;r===i.sectionCount&&i.level===e.branch.levels?a=.001:e.type===`deciduous`?a*=1-(e.branch.taper[i.level]??0)*(r/i.sectionCount):a*=1-r/i.sectionCount;let u=new H,d=new H,f=0;for(let e=0;e<i.segmentCount;e++){let t=2*Math.PI*e/i.segmentCount,c=new H(Math.cos(t),0,Math.sin(t)).multiplyScalar(a).applyEuler(o).add(s),l=new H(Math.cos(t),0,Math.sin(t)).applyEuler(o).normalize(),p=r%2==0?0:1;n.branchVerts.push(c.x,c.y,c.z),n.branchNormals.push(l.x,l.y,l.z),n.branchUvs.push(e/i.segmentCount,p),e===0&&(u=c,d=l,f=p)}n.branchVerts.push(u.x,u.y,u.z),n.branchNormals.push(d.x,d.y,d.z),n.branchUvs.push(1,f),l.push({origin:s.clone(),orientation:o.clone(),radius:a}),s.add(new H(0,c,0).applyEuler(o));let p=Math.max(1,1/Math.sqrt(a))*(e.branch.gnarliness[i.level]??0);o.x+=t.random(p,-p),o.z+=t.random(p,-p);let m=new V().setFromEuler(o),h=new V().setFromAxisAngle(new H(0,1,0),e.branch.twist[i.level]??0),g=e.branch.force,_=new V().setFromUnitVectors(new H(0,1,0),new H(g.direction.x,g.direction.y,g.direction.z));m.multiply(h),m.rotateTowards(_,g.strength/a),o.setFromQuaternion(m)}PC(n,a,i);let u=i.level===e.branch.levels?n.leafClusterNext++:0;if(e.type===`deciduous`){let a=l[l.length-1];i.level<e.branch.levels?r.push({origin:a.origin,orientation:a.orientation,length:e.branch.length[i.level+1]??0,radius:a.radius,level:i.level+1,sectionCount:i.sectionCount,segmentCount:i.segmentCount}):e.leaves.count>0&&NC(e,t,n,a.origin,a.orientation,u)}i.level===e.branch.levels?MC(e,t,n,l,u):i.level<e.branch.levels&&jC(e,t,r,e.branch.children[i.level]??0,i.level+1,l)}function kC(e,t){let n=Math.floor(t*(e.length-1)),r=e[n],i=n===e.length-1?r:e[n+1],a=(t-n/(e.length-1))/(1/(e.length-1)),o=new H().lerpVectors(r.origin,i.origin,a),s=new V().setFromEuler(r.orientation),c=new V().setFromEuler(i.orientation);return{origin:o,orientation:new _i().setFromQuaternion(c.slerp(s,a)),radius:{a:r.radius,b:i.radius,alpha:a}}}function AC(e,t,n){let r=new V().setFromAxisAngle(new H(1,0,0),t/(180/Math.PI)),i=new V().setFromAxisAngle(new H(0,1,0),n),a=new V().setFromEuler(e);return new _i().setFromQuaternion(a.multiply(i.multiply(r)))}function jC(e,t,n,r,i,a){let o=t.random();for(let s=0;s<r;s++){let c=t.random(1,e.branch.start[i]??0),l=kC(a,c),u=(e.branch.radius[i]??0)*((1-l.radius.alpha)*l.radius.a+l.radius.alpha*l.radius.b),d=2*Math.PI*(o+s/r),f=AC(l.orientation,e.branch.angle[i]??0,d),p=(e.branch.length[i]??0)*(e.type===`evergreen`?1-c:1);n.push({origin:l.origin,orientation:f,length:p,radius:u,level:i,sectionCount:e.branch.sections[i]??1,segmentCount:e.branch.segments[i]??3})}}function MC(e,t,n,r,i){let a=t.random();for(let o=0;o<e.leaves.count;o++){let s=kC(r,t.random(1,e.leaves.start)),c=2*Math.PI*(a+o/e.leaves.count),l=AC(s.orientation,e.leaves.angle,c);NC(e,t,n,s.origin,l,i)}}function NC(e,t,n,r,i,a){let o=n.leafVerts.length/3,s=e.leaves.size*(1+t.random(e.leaves.sizeVariance,-e.leaves.sizeVariance)),c=e=>{let t=s/2,c=[new H(-t,s,0),new H(-t,0,0),new H(t,0,0),new H(t,s,0)];for(let t of c)t.applyEuler(new _i(0,e,0)).applyEuler(i).add(r),n.leafVerts.push(t.x,t.y,t.z),n.leafAnchors.push(r.x,r.y,r.z),n.leafClusters.push(a);n.leafUvs.push(0,1,0,0,1,0,1,1),n.leafIndices.push(o,o+1,o+2,o,o+2,o+3),o+=4};c(0),e.leaves.billboard===`double`&&c(Math.PI/2)}function PC(e,t,n){let r=n.segmentCount+1;for(let i=0;i<n.sectionCount;i++)for(let a=0;a<n.segmentCount;a++){let n=t+i*r+a,o=t+i*r+(a+1),s=n+r,c=o+r;e.branchIndices.push(n,s,o,o,s,c)}}function FC(e,t){let n=0;for(let t=1;t<e.branchVerts.length;t+=3){let r=e.branchVerts[t];r>n&&(n=r)}for(let t=1;t<e.leafVerts.length;t+=3){let r=e.leafVerts[t];r>n&&(n=r)}if(n<=0)return;let r=t/n;for(let t=0;t<e.branchVerts.length;t++)e.branchVerts[t]=e.branchVerts[t]*r;for(let t=0;t<e.leafVerts.length;t++)e.leafVerts[t]=e.leafVerts[t]*r;for(let t=0;t<e.leafAnchors.length;t++)e.leafAnchors[t]=e.leafAnchors[t]*r}function IC(e,t){return t.length/3>65535?new Uint32Array(e):new Uint16Array(e)}var LC=[`simple`,`medium`,`high`],RC=[`conifer`,`broadleaf`,`dead`,`bush`];function zC(e,t,n){return new y(`BAD_FORMAT`,`Tree3D ${e} must be one of [${n.join(`, `)}], got '${t}'.`,{prop:e,validOptions:n})}var BC={simple:0,medium:1,high:2},VC=[5,7,10],HC=[2,3,4],UC=[6,8,12],WC=[2,3,3],GC=[0,1,2],KC=[3,4,5],qC=3;function JC(e,t){if(!LC.includes(e))throw zC(`tier`,e,LC);if(!RC.includes(t))throw zC(`type`,t,RC);let n=e,r=t,i=BC[n];return{tier:n,type:r,trunkRadialSegments:VC[i],canopyLayers:r===`conifer`?HC[i]:0,canopySegments:UC[i],blobCount:r===`broadleaf`||r===`bush`?WC[i]:0,blobDetail:GC[i],branchCount:r===`dead`?KC[i]:r===`conifer`&&n===`high`?qC:0,hasCanopy:r!==`dead`,canopySway:n!==`simple`}}var YC=.02,XC={conifer:{trunkHeight:.4,trunkRadiusBottom:.05,trunkRadiusTop:.035},broadleaf:{trunkHeight:.42,trunkRadiusBottom:.05,trunkRadiusTop:.032},dead:{trunkHeight:.72,trunkRadiusBottom:.045,trunkRadiusTop:.018},bush:{trunkHeight:.14,trunkRadiusBottom:.045,trunkRadiusTop:.035}};function ZC(e,t,n){let{trunkHeight:r,trunkRadiusBottom:i,trunkRadiusTop:a}=XC[e.type],o=[],s=new ws(a,i,r,e.trunkRadialSegments).toNonIndexed();if(s.translate(0,r/2,0),o.push(s),e.type===`dead`)for(let n=0;n<e.branchCount;n++){let r=.22+t.next()*.14,a=new ws(.006,.02,r,5).toNonIndexed();a.translate(0,r/2,0);let s=.7+t.next()*.6,c=n/e.branchCount*Math.PI*2+t.next()*.8,l=.34+(n+t.next())/e.branchCount*.34;a.applyMatrix4(new G().makeRotationY(c).multiply(new G().makeRotationX(s)).setPosition(Math.sin(c)*i*.6,l,Math.cos(c)*i*.6)),o.push(a)}let c=$C(o);return tw(c,new K(n),.7,1.05,t),c}function QC(e,t,n){if(!e.hasCanopy)return null;let r=[];if(e.type===`conifer`){let n=.74;for(let i=0;i<e.canopyLayers;i++){let a=i/e.canopyLayers,o=.3*(1-a*.62)*(.92+t.next()*.16),s=n/e.canopyLayers*1.7,c=new Ts(o,s,e.canopySegments).toNonIndexed();ew(c,t,YC),c.translate(0,.26+n*a+s/2,0),r.push(c)}for(let n=0;n<e.branchCount;n++){let i=new Ts(.06,.2,5).toNonIndexed(),a=n/Math.max(e.branchCount,1)*Math.PI*2+t.next(),o=1.1+t.next()*.4;i.applyMatrix4(new G().makeRotationY(a).multiply(new G().makeRotationX(o)).setPosition(Math.sin(a)*.22,.34+t.next()*.18,Math.cos(a)*.22)),r.push(i)}}else{let{trunkHeight:n}=XC[e.type];for(let i=0;i<e.blobCount;i++){let a=new Ds((i===0?.3:.24-i*.02)*(.92+t.next()*.16),e.blobDetail).toNonIndexed();a.scale(1,.72,1),ew(a,t,YC);let o=i===0?0:.13,s=t.next()*Math.PI*2;a.translate(Math.cos(s)*o,n+.16+i*.1+t.next()*.05,Math.sin(s)*o),r.push(a)}}let i=$C(r);return tw(i,new K(n),.62,1.18,t),i}function $C(e){let t=0;for(let n of e)t+=n.getAttribute(`position`).count;let n=new Float32Array(t*3),r=new Float32Array(t*3),i=0;for(let t of e)n.set(t.getAttribute(`position`).array,i),r.set(t.getAttribute(`normal`).array,i),i+=t.getAttribute(`position`).count*3,t.dispose();let a=new Ha;return a.setAttribute(`position`,new q(n,3)),a.setAttribute(`normal`,new q(r,3)),a}function ew(e,t,n){let r=e.getAttribute(`position`);for(let e=0;e<r.count;e++)r.setXYZ(e,r.getX(e)+t.range(-n,n),r.getY(e)+t.range(-n,n),r.getZ(e)+t.range(-n,n))}function tw(e,t,n,r,i){let a=e.getAttribute(`position`),o=new Float32Array(a.count*3),s=1/0,c=-1/0;for(let e=0;e<a.count;e++){let t=a.getY(e);t<s&&(s=t),t>c&&(c=t)}let l=Math.max(c-s,1e-6),u=new K;for(let e=0;e<a.count;e++){let c=(a.getY(e)-s)/l,d=(n+(r-n)*c)*(.96+i.next()*.08);u.copy(t).multiplyScalar(d),o[e*3]=u.r,o[e*3+1]=u.g,o[e*3+2]=u.b}e.setAttribute(`color`,new q(o,3))}var nw=500,rw=15e5,iw=[.8,1.2],aw=.28,ow=.12,sw=.055,cw=.11,lw=3,uw=101,dw=[.78,.7,.45],fw=.45,pw=.55,mw=`https://agent8-games.verse8.io/assets/3D/default/textures/vegetation`,hw=`https://agent8-games.verse8.io/assets/3D/default/textures/vegetation/bark`,gw={high:{conifer:[`pine`,`pine`,`pine`],broadleaf:[`oak`,`ash`,`oak`],dead:[`ash`,`ash`,`ash`],bush:[`bush`,`bush`,`bush`]},medium:{conifer:[`pine-forest`,`pine-forest`,`pine-forest`],broadleaf:[`oak-forest`,`ash-forest`,`oak-forest`],dead:[`ash-forest`,`ash-forest`,`ash-forest`],bush:[`bush-forest`,`bush-forest`,`bush-forest`]}},_w=class e extends Zv{static typeName=`Tree3D`;static props={tier:{default:`medium`,options:LC},type:{default:`conifer`,options:RC},seed:{default:1},height:{default:6},count:{default:1},area:{default:[10,10]},trunkColor:{default:`#7a5a3a`},canopyColor:{default:`#4a7c3f`},leafTexture:{default:``},leafFadeStart:{default:0},leafFadeEnd:{default:0},leafShadows:{default:!0},drape:{default:!1},terrain:{default:``}};tier=`medium`;type=`conifer`;seed=1;height=6;count=1;area=[10,10];trunkColor=`#7a5a3a`;canopyColor=`#4a7c3f`;leafTexture=``;leafFadeStart=0;leafFadeEnd=0;leafShadows=!0;drape=!1;terrain=``;drapeTerrain=null;drapeWorld=[0,0,0];static validateJson(e){let t=e;if(JC(t.tier,t.type),!(t.height>0))throw new y(`BAD_FORMAT`,`Tree3D '${e.name}' height must be > 0 meters, got ${t.height}.`,{prop:`height`});if(!Number.isInteger(t.count)||t.count<1||t.count>nw)throw new y(`BAD_FORMAT`,`Tree3D '${e.name}' count must be an integer in 1..${nw}, got ${t.count}.`,{prop:`count`});if(!((t.area[0]??0)>0)||!((t.area[1]??0)>0))throw new y(`BAD_FORMAT`,`Tree3D '${e.name}' area must be positive [x, z] meters, got ${JSON.stringify(t.area)}.`,{prop:`area`});if(t.leafFadeEnd!==0&&!(t.leafFadeEnd>t.leafFadeStart&&t.leafFadeStart>=0))throw new y(`BAD_FORMAT`,`Tree3D '${e.name}' needs leafFadeEnd > leafFadeStart >= 0 (the leaf LOD window) or 0/0 to disable, got start ${t.leafFadeStart}, end ${t.leafFadeEnd}.`,{prop:`leafFadeStart`});if(typeof t.leafShadows!=`boolean`)throw new y(`BAD_FORMAT`,`Tree3D '${e.name}' leafShadows must be a boolean, got ${JSON.stringify(t.leafShadows)}.`,{prop:`leafShadows`});if(typeof t.drape!=`boolean`)throw new y(`BAD_FORMAT`,`Tree3D '${e.name}' drape must be a boolean, got ${JSON.stringify(t.drape)}.`,{prop:`drape`});if(typeof t.terrain!=`string`)throw new y(`BAD_FORMAT`,`Tree3D '${e.name}' terrain must be a node-path string (empty = auto-find), got ${JSON.stringify(t.terrain)}.`,{prop:`terrain`});if(t.tier===`simple`)return;let n=bw(t.tier,t.type),r=n*t.count;if(r>rw)throw new y(`BAD_FORMAT`,`Tree3D '${e.name}' blows the grove triangle budget: count ${t.count} × ${n} tris/tree (tier '${t.tier}', type '${t.type}') = ${r} > ${rw}. Lower count (max ${Math.floor(rw/n)} at this tier), drop tier to 'medium'/'simple', or split the grove into multiple nodes.`,{prop:`count`})}time=0;grove=null;variants=[];simpleTrunk=null;simpleCanopy=null;swayUniform=null;leafFadeUniform=null;loadedTextures=[];groveKey=``;_createObject3D(){return new Fi}_trunk(){return this.rebuildIfNeeded(),this.simpleTrunk??this.variants[0]?.branches}_canopy(){return this.rebuildIfNeeded(),this.simpleTrunk?this.simpleCanopy:this.variants[0]?.leaves??null}_variantMeshes(){return this.rebuildIfNeeded(),this.variants}update(e){this.time+=e,this.swayUniform&&(this.swayUniform.value=this.time),this.leafFadeUniform&&(this.leafFadeUniform.start.value=this.leafFadeStart,this.leafFadeUniform.end.value=this.leafFadeEnd)}_syncObject3D(){super._syncObject3D(),this.rebuildIfNeeded()}rebuildIfNeeded(){this.grove||(this.grove=new Fi,this._ensureObject3D().add(this.grove));let e=JSON.stringify([this.tier,this.type,this.seed,this.height,this.count,this.area,this.trunkColor,this.canopyColor,this.leafTexture,this.leafShadows,this.leafFadeEnd>this.leafFadeStart,this.drape,this.terrain]);e===this.groveKey&&(this.simpleTrunk||this.variants.length>0)||(this.groveKey=e,this.disposeMeshes(),this.resolveDrape(),this.tier===`simple`?this.buildSimpleGrove():this.buildEzGrove())}resolveDrape(){if(this.drapeTerrain=null,!this.drape)return;let e=px(this,this.terrain);if(e)this.drapeTerrain=e,this.drapeWorld=fx(this);else if(this.terrain!==``)throw new y(`NODE_NOT_FOUND`,`Tree3D '${this.name}' drape terrain '${this.terrain}' is not a Terrain3D in the tree.`,{prop:`terrain`})}bedY(e,t){let n=this.drapeTerrain;return n?n.heightAt(this.drapeWorld[0]+e,this.drapeWorld[2]+t)-this.drapeWorld[1]:0}buildSimpleGrove(){let e=JC(this.tier,this.type),t=new b(this.seed),n=ZC(e,t,this.trunkColor),r=QC(e,t,this.canopyColor),i=new Vo(n,Ew(),this.count);Ow(i);let a=null;r&&(a=new Vo(r,Ew(),this.count),Ow(a)),this.scatter(t,(e,t,n)=>{i.setMatrixAt(e,t),i.setColorAt(e,n),a?.setMatrixAt(e,t),a?.setColorAt(e,n)}),Dw(i),a&&Dw(a),this.simpleTrunk=i,this.simpleCanopy=a,this.grove.add(i),a&&this.grove.add(a)}buildEzGrove(){let e=gw[this.tier][this.type],t=Math.min(lw,this.count),n=Array.from({length:t},(e,n)=>Math.floor(this.count/t)+ +(n<this.count%t));this.swayUniform={value:this.time},this.leafFadeUniform=this.leafFadeEnd>this.leafFadeStart?{start:{value:this.leafFadeStart},end:{value:this.leafFadeEnd}}:null;let r=Cw(this.canopyColor),i=null;typeof document<`u`&&typeof createImageBitmap==`function`&&(i=new Wc,i.setOptions({imageOrientation:`flipY`}));for(let a=0;a<t;a++){let t=n[a],o=e[a],s=hC(o);this.type===`dead`&&(s.leaves.count=0);let c=DC(s,this.seed+a*uw,1),l=this.buildBarkMaterial(o,i),u=new Vo(c.branches,l,t);Ow(u);let d=null;if(c.leaves){let e=new Bs({color:r.clone().multiply(new K(dw[0],dw[1],dw[2])),roughness:1,side:2,vertexColors:!0,alphaTest:s.leaves.alphaTest}),n=this.leafTexture||xw(s.leaves.style);i&&(e.map=yw(i,n,e=>{e.colorSpace=Yn,e.flipY=!1},e=>Sw(e))),tS(e,this.swayUniform,fw,this.leafFadeUniform??void 0),d=new Vo(c.leaves,e,t),Ow(d),this.leafShadows||(d.castShadow=!1)}this.variants.push({branches:u,leaves:d}),this.grove.add(u),d&&this.grove.add(d)}let a=Array(t).fill(0),o=new b(this.seed);this.scatter(o,(e,n,r)=>{let i=e%t,o=this.variants[i],s=a[i];a[i]=s+1,o.branches.setMatrixAt(s,n),o.branches.setColorAt(s,r),o.leaves?.setMatrixAt(s,n),o.leaves?.setColorAt(s,r)});for(let e of this.variants)Dw(e.branches),e.leaves&&Dw(e.leaves)}buildBarkMaterial(t,n){if(this.type===`dead`||!n)return new Bs({color:new K(this.trunkColor),roughness:1});let r=_C(t),i=new Bs({color:this.trunkColor===e.props.trunkColor?.default?new K(r.tint):Tw(this.trunkColor),roughness:1});return eS(i,pw),i.map=this.loadBarkTexture(n,r,`color`,!0),i.normalMap=this.loadBarkTexture(n,r,`normal`,!1),i.roughnessMap=this.loadBarkTexture(n,r,`roughness`,!1),i}loadBarkTexture(e,t,n,r){return yw(e,`${hw}/${t.texture}_${n}_1k.jpg`,e=>{e.wrapS=bt,e.wrapT=bt,e.repeat.set(t.repeat[0],t.repeat[1]),e.anisotropy=4,e.flipY=!1,r&&(e.colorSpace=Yn)})}scatter(e,t){let n=(this.area[0]??0)/2,r=(this.area[1]??0)/2;for(let i=0;i<this.count;i++){let a=this.count===1?0:e.range(-n,n),o=this.count===1?0:e.range(-r,r);jw.set(a,this.drape?this.bedY(a,o):0,o),Mw.setFromAxisAngle(kw,e.range(0,Math.PI*2));let s=this.height*e.range(iw[0],iw[1]),c=s*e.range(.9,1.1);Nw.set(c,s,c),Aw.compose(jw,Mw,Nw),Pw.setHSL(aw,ow,.92).offsetHSL(e.range(-.055,sw),0,e.range(-.11,cw)),t(i,Aw,Pw)}}disposeMeshes(){let e=[this.simpleTrunk,this.simpleCanopy];for(let t of this.variants)e.push(t.branches,t.leaves);let t=new Set;for(let n of e)n&&(n.removeFromParent(),n.geometry.dispose(),t.add(n.material),n.dispose());for(let e of t)e.dispose();for(let e of this.loadedTextures)e.dispose();this.loadedTextures=[],this.simpleTrunk=null,this.simpleCanopy=null,this.variants=[],this.swayUniform=null,this.leafFadeUniform=null}free(){this.disposeMeshes(),this.grove=null,super.free()}},vw=new Map;function yw(e,t,n,r){let i=vw.get(t);if(i)return i;let a=new ni;return n(a),e.load(t,e=>{a.image=e,r?r(a):a.needsUpdate=!0}),vw.set(t,a),a}function bw(e,t){let n=0;for(let r of new Set(gw[e][t]??[])){let e=hC(r);t===`dead`&&(e.leaves.count=0),n=Math.max(n,fC(e).total)}return n}function xw(e){return`${mw}/${e}_color.png`}function Sw(e){let t=e.image;if(!t||typeof document>`u`)return;let n=document.createElement(`canvas`);n.width=t.naturalWidth||t.width,n.height=t.naturalHeight||t.height;let r=n.getContext(`2d`,{willReadFrequently:!0});if(!r)return;r.drawImage(t,0,0);let i;try{i=r.getImageData(0,0,n.width,n.height)}catch{return}let a=i.data,o=0,s=0,c=0,l=0;for(let e=0;e<a.length;e+=4)a[e+3]>240&&(o+=a[e],s+=a[e+1],c+=a[e+2],l++);if(l!==0){o=Math.round(o/l),s=Math.round(s/l),c=Math.round(c/l);for(let e=0;e<a.length;e+=4)a[e+3]<=240&&(a[e]=o,a[e+1]=s,a[e+2]=c);r.putImageData(i,0,0),e.image=n,e.needsUpdate=!0}}function Cw(e){let t=new K(e),n=Math.max(t.r,t.g,t.b);return n>0?t.multiplyScalar(1/n):t.setRGB(1,1,1),t.lerp(ww,.18)}var ww=new K(1,1,1);function Tw(e){let t=new K(e),n=Math.max(t.r,t.g,t.b);return n>0?t.multiplyScalar(1/n):t.setRGB(1,1,1),t}function Ew(){return new Bs({vertexColors:!0,roughness:1,flatShading:!0})}function Dw(e){e.instanceMatrix.needsUpdate=!0,e.instanceColor&&(e.instanceColor.needsUpdate=!0),dx(e)}function Ow(e){e.castShadow=!0,e.receiveShadow=!0}var kw=new H(0,1,0),Aw=new G,jw=new H,Mw=new V,Nw=new H,Pw=new K,Fw=[[[.4,.4,.4],[.4,.4,.4],[.4,.4,.4],[.4,.4,.4],[.4,.4,.4],[.4,.4,.4]],[[.6,.3,0],[.6,.3,0],[.6,.3,0],[.6,.3,0],[0,.8,0],[.6,.3,0]],[[.6,.3,0],[.6,.3,0],[.6,.3,0],[.6,.3,0],[.6,.3,0],[.6,.3,0]],[[.6,.55,.5],[.55,.5,.45],[.5,.45,.4],[.55,.5,.45],[.65,.6,.55],[.5,.45,.4]],[[.45,.45,.4],[.4,.4,.35],[.35,.35,.3],[.4,.4,.35],[.5,.5,.45],[.35,.35,.3]],[[.2,.2,.2],[.2,.2,.2],[.2,.2,.2],[.2,.2,.2],[.2,.2,.2],[.2,.2,.2]],[[.8,.5,.2],[.75,.45,.15],[.7,.4,.1],[.75,.45,.15],[.85,.55,.25],[.7,.4,.1]],[[0,.7,.1],[0,.65,.1],[0,.6,.1],[0,.65,.1],[0,.75,.1],[0,.6,.1]],[[.3,.3,.3],[.3,.3,.3],[.3,.3,.3],[.3,.3,.3],[.3,.3,.3],[.3,.3,.3]],[[0,0,.7],[0,0,.7],[0,0,.7],[0,0,.7],[0,.2,.9],[0,0,.6]],[[.9,.3,0],[.9,.3,0],[.9,.3,0],[.9,.3,0],[1,.4,0],[.7,.2,0]],[[.9,.8,.2],[.85,.75,.18],[.85,.75,.18],[.85,.75,.18],[.95,.85,.25],[.85,.75,.18]],[[.6,.6,.6],[.55,.55,.55],[.5,.5,.5],[.55,.55,.55],[.65,.65,.65],[.5,.5,.5]],[[.7,.7,.4],[.65,.65,.35],[.6,.6,.3],[.65,.65,.35],[.75,.75,.45],[.6,.6,.3]],[[.7,.5,.5],[.65,.45,.45],[.6,.4,.4],[.65,.45,.45],[.75,.55,.55],[.6,.4,.4]],[[.35,.3,.25],[.3,.25,.2],[.25,.2,.15],[.3,.25,.2],[.4,.35,.3],[.25,.2,.15]],[[.5,.3,0],[.5,.3,0],[.5,.3,0],[.5,.3,0],[.7,.4,.1],[.7,.4,.1]],[[0,.5,0],[0,.45,0],[0,.45,0],[0,.45,0],[0,.6,0],[0,.4,0]],[[.2,.8,.8],[.15,.7,.7],[.1,.6,.6],[.15,.7,.7],[.3,.9,.9],[.1,.6,.6]],[[1,.5,.8],[.9,.4,.7],[.8,.3,.6],[.9,.4,.7],[1,.6,.9],[.8,.3,.6]],[[1,.9,.3],[.95,.85,.25],[.9,.8,.2],[.95,.85,.25],[1,1,.4],[.9,.8,.2]],[[1,0,0],[.9,0,0],[.8,0,0],[.9,0,0],[1,.2,.2],[.8,0,0]],[[.6,.4,.2],[.5,.3,.15],[.4,.25,.1],[.5,.3,.15],[.7,.45,.25],[.4,.25,.1]],[[.6,.2,.8],[.5,.15,.7],[.4,.1,.6],[.5,.15,.7],[.7,.25,.9],[.4,.1,.6]],[[1,.8,0],[.95,.75,0],[.95,.75,0],[.95,.75,0],[1,.9,0],[.9,.7,0]],[[.6,.65,.7],[.55,.6,.65],[.5,.55,.6],[.55,.6,.65],[.65,.7,.75],[.5,.55,.6]]],Iw=2e5,Lw=class extends Zv{static typeName=`VoxelGrid3D`;static signals=[`blocksChanged`];static props={voxels:{default:[]}};voxels=[];appliedVoxels=null;map=new Map;mesh=null;dirty=!1;blockCount(){return this.map.size}tileAt(e,t,n){return this.map.get(`${e},${t},${n}`)}setBlocks(e){this.map.clear();for(let t of e)this.map.set(`${t.x},${t.y},${t.z}`,t.tile);this.dirty=!0,this.emit(`blocksChanged`)}addBlock(e){this.map.set(`${e.x},${e.y},${e.z}`,e.tile),this.dirty=!0,this.emit(`blocksChanged`)}removeBlockAt(e,t,n){let r=this.map.delete(`${e},${t},${n}`);return r&&(this.dirty=!0,this.emit(`blocksChanged`)),r}blocks(){let e=[];for(let[t,n]of this.map){let[r,i,a]=t.split(`,`).map(Number);e.push({x:r,y:i,z:a,tile:n})}return e}_createObject3D(){let e=new Ss(1,1,1),t=new Rs({vertexShader:zw,fragmentShader:Bw});this.mesh=new Vo(e,t,Iw),this.mesh.count=0,this.mesh.frustumCulled=!1;for(let e of Rw)this.mesh.geometry.setAttribute(e,new No(new Float32Array(Iw*3),3));return this.dirty=!0,this.mesh}update(){if(this.voxels.length>0&&this.voxels!==this.appliedVoxels&&(this.appliedVoxels=this.voxels,this.setBlocks(this.voxels.map(e=>({x:e[0]??0,y:e[1]??0,z:e[2]??0,tile:e[3]??0})))),!this.dirty||!this.mesh)return;this.dirty=!1;let e=new G,t=0;for(let[n,r]of this.map){if(t>=Iw)break;let[i,a,o]=n.split(`,`).map(Number);e.setPosition(i,a,o),this.mesh.setMatrixAt(t,e);let s=Fw[r]??Fw[0];for(let e=0;e<6;e++){let n=this.mesh.geometry.getAttribute(Rw[e]),r=s[e];n.setXYZ(t,r[0],r[1],r[2]),n.needsUpdate=!0}t++}this.mesh.count=t,this.mesh.instanceMatrix.needsUpdate=!0}},Rw=[`colorFront`,`colorRight`,`colorBack`,`colorLeft`,`colorTop`,`colorBottom`],zw=`
|
|
5824
|
+
`)},e.customProgramCacheKey=()=>`incanto-tuft-wind`}var lS=[`grass`,`flowers`,`reeds`],uS=[`blades`,`simple`,`mesh`,`tufts`],dS=[`grass`,`fern`],fS={grass:.08,flowers:.14,reeds:.05},pS=[.75,1.25],mS=.05,hS=.25,gS=2e5,_S=8,vS=.05,yS=.38,bS=[.75,1.3],xS=.1,SS=.42,CS=[.6,1.6],wS=.8,TS=.9,ES=.04,DS=[`#f5f1e2`,`#e9d052`,`#8d7bd9`],OS=1/3,kS=1.5,AS=1.45,jS=.4,MS=.13,NS=[.85,1.2],PS=[.8,1.3],FS=`#b3a24e`,IS=.8,LS=.5,RS=class e extends Zv{static typeName=`Foliage3D`;static props={kind:{default:`grass`,options:lS},style:{default:`mesh`,options:uS},tuftStyle:{default:`grass`,options:dS},area:{default:[20,20]},density:{default:12},maxInstances:{default:5e4},height:{default:.25},colorA:{default:`#4d7232`},colorB:{default:`#9aab55`},groundColor:{default:`#1f3015`},sunDirection:{default:[.5,.8,.3]},fadeStart:{default:60},fadeEnd:{default:90},sway:{default:.6},interaction:{default:!0},coverage:{default:.85},flowers:{default:0},seed:{default:1},drape:{default:!1},terrain:{default:``},renderOrder:{default:2}};renderOrder=2;kind=`grass`;style=`mesh`;tuftStyle=`grass`;area=[20,20];density=12;maxInstances=5e4;height=.25;colorA=`#4d7232`;colorB=`#9aab55`;groundColor=`#1f3015`;sunDirection=[.5,.8,.3];fadeStart=60;fadeEnd=90;sway=.6;interaction=!0;coverage=.85;flowers=0;seed=1;drape=!1;terrain=``;drapeTerrain=null;drapeWorld=[0,0,0];static validateJson(e){let t=e;if(!lS.includes(t.kind))throw new y(`BAD_FORMAT`,`Foliage3D '${e.name}' kind must be one of [${lS.join(`, `)}], got '${t.kind}'.`,{prop:`kind`,validOptions:lS});if(!uS.includes(t.style))throw new y(`BAD_FORMAT`,`Foliage3D '${e.name}' style must be one of [${uS.join(`, `)}], got '${t.style}'.`,{prop:`style`,validOptions:uS});if(!dS.includes(t.tuftStyle))throw new y(`BAD_FORMAT`,`Foliage3D '${e.name}' tuftStyle must be one of [${dS.join(`, `)}], got '${t.tuftStyle}'.`,{prop:`tuftStyle`,validOptions:dS});if(!((t.area[0]??0)>0)||!((t.area[1]??0)>0))throw new y(`BAD_FORMAT`,`Foliage3D '${e.name}' area must be positive [x, z] meters, got ${JSON.stringify(t.area)}.`,{prop:`area`});if(!(t.density>0)||!(t.maxInstances>=1))throw new y(`BAD_FORMAT`,`Foliage3D '${e.name}' needs density > 0 and maxInstances >= 1, got density ${t.density}, maxInstances ${t.maxInstances}.`,{prop:`density`});if(t.maxInstances>gS)throw new y(`BAD_FORMAT`,`Foliage3D '${e.name}' maxInstances is capped at ${gS} (got ${t.maxInstances}) — split giant fields into multiple nodes.`,{prop:`maxInstances`});if(!(t.fadeStart>=0)||!(t.fadeEnd>t.fadeStart))throw new y(`BAD_FORMAT`,`Foliage3D '${e.name}' needs fadeStart >= 0 and fadeEnd > fadeStart (the LOD fade window), got fadeStart ${t.fadeStart}, fadeEnd ${t.fadeEnd}.`,{prop:`fadeStart`});let n=t.sunDirection;if(!Array.isArray(n)||n.length!==3||!n.every(e=>Number.isFinite(e))||Math.hypot(n[0]??0,n[1]??0,n[2]??0)===0)throw new y(`BAD_FORMAT`,`Foliage3D '${e.name}' sunDirection must be a non-zero [x, y, z] vector, got ${JSON.stringify(n)}.`,{prop:`sunDirection`});if(!(t.coverage>0)||!(t.coverage<=1))throw new y(`BAD_FORMAT`,`Foliage3D '${e.name}' coverage must be in (0, 1] (carpet fill — 1 is solid, lower carves dirt patches), got ${t.coverage}.`,{prop:`coverage`});if(!(t.flowers>=0)||!(t.flowers<=.2))throw new y(`BAD_FORMAT`,`Foliage3D '${e.name}' flowers must be in [0, 0.2] (fraction of instances rendered as flower heads — for a whole flower FIELD use kind: 'flowers'), got ${t.flowers}.`,{prop:`flowers`});if(typeof t.drape!=`boolean`)throw new y(`BAD_FORMAT`,`Foliage3D '${e.name}' drape must be a boolean, got ${JSON.stringify(t.drape)}.`,{prop:`drape`});if(typeof t.terrain!=`string`)throw new y(`BAD_FORMAT`,`Foliage3D '${e.name}' terrain must be a node-path string (empty = auto-find), got ${JSON.stringify(t.terrain)}.`,{prop:`terrain`})}time=0;pivot=null;meshes=[];bladesMaterial=null;benderSlots=null;pickedBenders=[];fieldKey=``;tuftTime=null;tuftStrength=null;tuftTexture=null;_createObject3D(){return new Fi}get isBlades(){return this.kind===`grass`&&this.style===`blades`}get isMesh(){return this.kind===`grass`&&this.style===`mesh`}get isTufts(){return this.kind===`grass`&&this.style===`tufts`}get isShaderField(){return this.isMesh||this.isBlades}_field(){return this.rebuildIfNeeded(),this.meshes[0]}_fields(){return this.rebuildIfNeeded(),this.meshes}update(e){this.time+=e,this.pickedBenders=this.computeBenders()}_syncObject3D(){super._syncObject3D(),this.rebuildIfNeeded();let e=this.pivot;if(this.isShaderField){e.matrix.identity();let t=this.bladesMaterial.uniforms;t.time&&(t.time.value=this.time),t.windStrength&&(t.windStrength.value=this.sway*(this.isMesh?yS:hS)),(t.bottomColor?.value).set(this.colorA),(t.topColor?.value).set(this.colorB),this.isMesh&&((t.groundColor?.value).set(this.groundColor),(t.sunDirection?.value).set(this.sunDirection[0]??0,this.sunDirection[1]??1,this.sunDirection[2]??0).normalize(),t.fadeStart&&(t.fadeStart.value=this.fadeStart),t.fadeEnd&&(t.fadeEnd.value=this.fadeEnd));let n=this.benderSlots;for(let e=0;e<4;e++){let t=this.pickedBenders[e];t?n[e]?.set(t.x,t.y,t.z,t.radius):n[e]?.set(0,0,0,0)}}else if(this.isTufts)e.matrix.identity(),this.tuftTime&&(this.tuftTime.value=this.time),this.tuftStrength&&(this.tuftStrength.value=this.sway*MS);else if(this.sway>0){let t=Math.sin(this.time*1.7)*mS*this.sway,n=Math.sin(this.time*1.3+1.7)*mS*this.sway*.6;e.matrix.makeShear(0,0,t,n,0,0)}else e.matrix.identity();e.matrixWorldNeedsUpdate=!0}_applySunDirection(t){if(!this.isMesh||!this.bladesMaterial)return;let n=e.props.sunDirection?.default;this.sunDirection.every((e,t)=>e===n[t])&&(this.bladesMaterial.uniforms.sunDirection?.value)?.set(t[0],t[1],t[2])}computeBenders(){if(!this.interaction||!this.isShaderField)return[];let[e,t,n]=gx(this);this.drape&&!this.drapeTerrain&&this.resolveDrape();let r=this.drapeTerrain,i=[];for(let e of Kx(this)){let n=r?r.heightAt(e.x,e.z):t,a=e.footY-n;a<=LS&&a>=-2.5&&i.push({x:e.x,y:e.y,z:e.z,radius:e.radius+IS})}return Nx(i,{x:e,z:n,areaX:this.area[0]??0,areaZ:this.area[1]??0})}rebuildIfNeeded(){this.pivot||(this.pivot=new Fi,this.pivot.matrixAutoUpdate=!1,this._ensureObject3D().add(this.pivot));let e=JSON.stringify([this.kind,this.style,this.tuftStyle,this.area,this.density,this.maxInstances,this.height,this.colorA,this.colorB,this.coverage,this.flowers,this.seed,this.drape,this.terrain]);if(e===this.fieldKey&&this.meshes.length>0)return;this.fieldKey=e,this.disposeMeshes(),this.resolveDrape();let t=this.isMesh?_S:this.isTufts?OS:1,n=Math.min(Math.floor((this.area[0]??0)*(this.area[1]??0)*this.density*t),this.maxInstances);this.isMesh?this.buildMeshField(n):this.isTufts?this.buildTuftField(n):this.isBlades?this.buildBladesField(n):this.buildSimpleField(n);for(let e of this.meshes)hx(e),this.pivot.add(e)}resolveDrape(){if(this.drapeTerrain=null,!this.drape)return;let e=_x(this,this.terrain);if(e)this.drapeTerrain=e,this.drapeWorld=gx(this);else if(this.terrain!==``)throw new y(`NODE_NOT_FOUND`,`Foliage3D '${this.name}' drape terrain '${this.terrain}' is not a Terrain3D in the tree.`,{prop:`terrain`})}bedY(e,t){let n=this.drapeTerrain;return n?n.heightAt(this.drapeWorld[0]+e,this.drapeWorld[2]+t)-this.drapeWorld[1]:0}buildMeshField(e){let t=Xb({seed:this.seed,areaX:this.area[0]??0,areaZ:this.area[1]??0,count:e,density:this.density*_S}),n=this.coverage>=1?t:t.filter(e=>Yb(e.x,e.z,this.seed)<=this.coverage),r=this.flowers>0?Math.floor(n.length*this.flowers):0,i=new Set;for(let e=0;e<r;e++)i.add(Math.floor((e+.5)*n.length/r));let a=n.filter((e,t)=>!i.has(t)),o=n.filter((e,t)=>i.has(t));this.benderSlots=Array.from({length:4},()=>new ri(0,0,0,0));let s=Fs.merge([Y.lights,Y.fog]);Object.assign(s,{time:{value:0},windStrength:{value:this.sway*yS},topColor:{value:new K(this.colorB)},bottomColor:{value:new K(this.colorA)},groundColor:{value:new K(this.groundColor)},sunDirection:{value:new H(this.sunDirection[0]??0,this.sunDirection[1]??1,this.sunDirection[2]??0).normalize()},fadeStart:{value:this.fadeStart},fadeEnd:{value:this.fadeEnd},benders:{value:this.benderSlots}}),this.bladesMaterial=new Rs({vertexShader:zx,fragmentShader:Bx,uniforms:s,fog:!0,lights:!0,side:2});let c=Fx(vS),l=a.length,u=Math.max(l,1),d=new Vo(c,this.bladesMaterial,u);d.count=l,d.receiveShadow=!0;let f=new b(this.seed+5352221),p=new Float32Array(u*4),m=new Float32Array(u),h=new Float32Array(u);for(let e=0;e<l;e++){let t=a[e],n=Vb(t.x,t.z),r=qb(t.x,t.z,this.seed),i=(r-.55)/1.15,o=f.range(bS[0],bS[1]),s=this.height*r*(.55+t.scale*.6);WS.set(t.x,this.bedY(t.x,t.z),t.z),GS.setFromAxisAngle(HS,t.rotY),KS.set(o,s,o),d.setMatrixAt(e,US.compose(WS,GS,KS));let c=n.leanDir+f.range(-.6,.6),l=(xS+SS*(.5*n.lean+.5*f.next()))*(1+wS*i);p[e*4]=Math.cos(c)*l,p[e*4+1]=Math.sin(c)*l,p[e*4+2]=f.range(CS[0],CS[1])*(1+TS*i),p[e*4+3]=f.next(),m[e]=n.hue;let u=Jb(t.x,t.z,this.seed)*.85+(f.next()-.5)*.3;h[e]=Math.min(1,Math.max(0,u))}c.setAttribute(`aBlade`,new No(p,4)),c.setAttribute(`aClumpHue`,new No(m,1)),c.setAttribute(`aDry`,new No(h,1)),d.instanceMatrix.needsUpdate=!0,this.meshes=[d],o.length>0&&this.meshes.push(this.buildFlowerMesh(o))}buildFlowerMesh(e){let t=new Vo(Ix(ES),new Bs({roughness:1,side:2}),Math.max(e.length,1));t.count=e.length,t.receiveShadow=!0;let n=new b(this.seed+15797765);for(let r=0;r<e.length;r++){let i=e[r],a=qb(i.x,i.z,this.seed),o=this.height*a*(.85+n.next()*.35);WS.set(i.x,this.bedY(i.x,i.z),i.z),GS.setFromAxisAngle(HS,i.rotY),KS.set(1,o,1),t.setMatrixAt(r,US.compose(WS,GS,KS));let s=n.next(),c=DS[s<.45?0:s<.75?1:2];t.setColorAt(r,YS.set(c))}return t.instanceMatrix.needsUpdate=!0,t.instanceColor&&(t.instanceColor.needsUpdate=!0),t}buildTuftField(e){let t=Xb({seed:this.seed,areaX:this.area[0]??0,areaZ:this.area[1]??0,count:e,density:this.density*OS}),n=this.coverage>=1?t:t.filter(e=>Yb(e.x,e.z,this.seed)<=this.coverage);this.tuftTime={value:this.time},this.tuftStrength={value:this.sway*MS};let r=new Bs({alphaTest:.5,side:2,roughness:1}),i=Qx(this.seed,this.tuftStyle);i&&(r.map=i,this.tuftTexture=i),cS(r,this.tuftTime,this.tuftStrength);let a=new Vo(BS(),r,Math.max(n.length,1));a.count=n.length,a.castShadow=!1,a.receiveShadow=!0;let o=new b(this.seed+7403357),s=qS.set(this.colorA),c=JS.set(this.colorB);for(let e=0;e<n.length;e++){let t=n[e],r=1+(qb(t.x,t.z,this.seed)-1)*jS,i=this.height*kS*r*o.range(NS[0],NS[1]),l=i*AS*o.range(PS[0],PS[1]);WS.set(t.x,this.bedY(t.x,t.z),t.z),GS.setFromAxisAngle(HS,t.rotY),KS.set(l,i,l),a.setMatrixAt(e,US.compose(WS,GS,KS));let u=Vb(t.x,t.z),d=Math.min(1,Math.max(0,.5+.35*u.hue+(o.next()-.5)*.3)),f=Math.min(1,Math.max(0,Jb(t.x,t.z,this.seed)*.8-.1));YS.copy(s).lerp(c,d).lerp(XS.set(FS),f*.5).multiplyScalar(o.range(.62,1.18)),a.setColorAt(e,YS)}a.instanceMatrix.needsUpdate=!0,a.instanceColor&&(a.instanceColor.needsUpdate=!0),this.meshes=[a]}buildBladesField(e){let t=Xb({seed:this.seed,areaX:this.area[0]??0,areaZ:this.area[1]??0,count:e,density:this.density});this.benderSlots=Array.from({length:4},()=>new ri(0,0,0,0));let n=Fs.merge([Y.fog]);Object.assign(n,{time:{value:0},windStrength:{value:this.sway*hS},topColor:{value:new K(this.colorB)},bottomColor:{value:new K(this.colorA)},grassHeight:{value:this.height},grassScale:{value:1},benders:{value:this.benderSlots}}),this.bladesMaterial=new Rs({vertexShader:Lx,fragmentShader:Rx,uniforms:n,fog:!0,transparent:!0,side:2,depthWrite:!0,depthTest:!0,alphaTest:.1,blending:0});let r=zS(this.height),i=new Vo(r,this.bladesMaterial,Math.max(e,1)),a=new Vo(r,this.bladesMaterial,Math.max(e,1));i.count=e,a.count=e,i.renderOrder=this.renderOrder,a.renderOrder=this.renderOrder;for(let n=0;n<e;n++){let e=t[n];WS.set(e.x,this.bedY(e.x,e.z),e.z),KS.setScalar(e.scale),GS.setFromAxisAngle(HS,e.rotY),i.setMatrixAt(n,US.compose(WS,GS,KS)),GS.setFromAxisAngle(HS,e.rotY+Math.PI/2),a.setMatrixAt(n,US.compose(WS,GS,KS))}i.instanceMatrix.needsUpdate=!0,a.instanceMatrix.needsUpdate=!0,this.meshes=[i,a]}buildSimpleField(e){let t=new Bs({side:2,roughness:1}),n=new Vo(VS(fS[this.kind]??.08),t,Math.max(e,1));n.count=e,n.receiveShadow=!0;let r=new b(this.seed),i=(this.area[0]??0)/2,a=(this.area[1]??0)/2,o=qS.set(this.colorA),s=JS.set(this.colorB);for(let t=0;t<e;t++){let e=r.range(-i,i),c=r.range(-a,a);WS.set(e,this.bedY(e,c),c),GS.setFromAxisAngle(HS,r.range(0,Math.PI*2)),KS.set(1,this.height*r.range(pS[0],pS[1]),1),US.compose(WS,GS,KS),n.setMatrixAt(t,US),n.setColorAt(t,YS.copy(o).lerp(s,r.next()))}n.instanceMatrix.needsUpdate=!0,n.instanceColor&&(n.instanceColor.needsUpdate=!0),this.meshes=[n]}disposeMeshes(){let e=new Set(this.meshes.map(e=>e.geometry)),t=new Set(this.meshes.map(e=>e.material));for(let e of this.meshes)e.removeFromParent(),e.dispose();for(let t of e)t.dispose();for(let e of t)e.dispose?.();this.bladesMaterial?.dispose(),this.bladesMaterial=null,this.benderSlots=null,this.tuftTexture?.dispose(),this.tuftTexture=null,this.tuftTime=null,this.tuftStrength=null,this.meshes=[]}free(){this.disposeMeshes(),this.pivot=null,super.free()}};function zS(e){let t=new Os(1,e,1,1);return t.translate(0,e/2,0),t}function BS(){let e=[],t=[],n=[],r=[];for(let i=0;i<3;i++){let a=i*Math.PI/3,o=Math.cos(a)/2,s=Math.sin(a)/2,c=-Math.sin(a),l=Math.cos(a),u=i*4;e.push(-o,0,-s,o,0,s,-o,1,-s,o,1,s);for(let e=0;e<4;e++)t.push(c,0,l);n.push(0,0,1,0,0,1,1,1),r.push(u,u+1,u+3,u,u+3,u+2)}let i=new Ha;return i.setAttribute(`position`,new q(new Float32Array(e),3)),i.setAttribute(`normal`,new q(new Float32Array(t),3)),i.setAttribute(`uv`,new q(new Float32Array(n),2)),i.setIndex(r),i}function VS(e){let t=e/2,n=new Float32Array([-t,0,0,t,0,0,-t,1,0,t,1,0,0,0,-t,0,0,t,0,1,-t,0,1,t]),r=new Float32Array([0,0,1,0,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,0,1,0,0]),i=new Ha;return i.setAttribute(`position`,new q(n,3)),i.setAttribute(`normal`,new q(r,3)),i.setIndex([0,1,2,2,1,3,4,5,6,6,5,7]),i}var HS=new H(0,1,0),US=new G,WS=new H,GS=new V,KS=new H,qS=new K,JS=new K,YS=new K,XS=new K,ZS=class extends Zv{static typeName=`DirectionalLight3D`;static props={color:{default:`#ffffff`},intensity:{default:1},castShadow:{default:!1},shadowArea:{default:75},shadowMapSize:{default:2048},shadowFollowsCamera:{default:!1}};color=`#ffffff`;intensity=1;castShadow=!1;shadowArea=75;shadowMapSize=2048;shadowFollowsCamera=!1;_createObject3D(){return new Bc}_syncObject3D(){super._syncObject3D();let e=this._ensureObject3D();if(e.color.set(this.color),e.intensity=this.intensity,e.castShadow=this.castShadow,this.castShadow&&`shadow`in e){let t=e.shadow.camera;`left`in t&&(t.left=-this.shadowArea,t.right=this.shadowArea,t.top=this.shadowArea,t.bottom=-this.shadowArea,t.near=.5,t.far=500,t.updateProjectionMatrix()),e.shadow.mapSize.set(this.shadowMapSize,this.shadowMapSize),e.shadow.bias=-2e-4,e.shadow.normalBias=.002}}},QS=class extends Zv{static typeName=`OmniLight3D`;static props={color:{default:`#ffffff`},intensity:{default:1},range:{default:0}};color=`#ffffff`;intensity=1;range=0;_createObject3D(){return new Lc}_syncObject3D(){super._syncObject3D();let e=this._ensureObject3D();e.color.set(this.color),e.intensity=this.intensity,e.distance=this.range}},$S=[`color`,`metalness`,`roughness`,`emissive`,`emissiveIntensity`,`wireframe`,`flatShading`,`opacity`,`map`,`normalMap`,`repeat`],eC=class extends Zv{static typeName=`MeshInstance3D`;static props={mesh:{default:`box`,options:[`box`,`sphere`,`capsule`,`plane`,`cylinder`,`gem`]},size:{default:[1,1,1]},material:{default:{}},castShadow:{default:!1},receiveShadow:{default:!1}};mesh=`box`;size=[1,1,1];material={};castShadow=!1;receiveShadow=!1;static validateJson(e){tC(e.material,e.name)}geometryKey=``;textureKey=``;loadedTextures=[];_createObject3D(){return new ho(new Ss(1,1,1),new Bs)}_syncObject3D(){super._syncObject3D();let e=this._ensureObject3D(),t=`${this.mesh}:${JSON.stringify(this.size)}`;t!==this.geometryKey&&(e.geometry.dispose(),e.geometry=nC(this.mesh,this.size),this.geometryKey=t);let n=e.material;n.color.set(this.material.color??`#ffffff`),n.metalness=this.material.metalness??0,n.roughness=this.material.roughness??1,n.wireframe=this.material.wireframe??!1,n.emissive.set(this.material.emissive??`#000000`),n.emissiveIntensity=this.material.emissiveIntensity??1;let r=this.material.opacity??1;n.opacity=r,n.transparent=r<1;let i=this.material.flatShading??!1;n.flatShading!==i&&(n.flatShading=i,n.needsUpdate=!0),this.syncTextures(n),e.castShadow=this.castShadow,e.receiveShadow=this.receiveShadow}syncTextures(e){let t=JSON.stringify([this.material.map??``,this.material.normalMap??``,this.material.repeat??null]);if(t===this.textureKey)return;this.textureKey=t;for(let e of this.loadedTextures)e.dispose();this.loadedTextures=[];let n=e.map!==null||e.normalMap!==null;e.map=null,e.normalMap=null;let r=typeof document<`u`?new bc:null;r&&this.material.map&&(e.map=this.loadTexture(r,this.material.map,!0)),r&&this.material.normalMap&&(e.normalMap=this.loadTexture(r,this.material.normalMap,!1)),(n||e.map||e.normalMap)&&(e.needsUpdate=!0)}loadTexture(e,t,n){let r=e.load(t);r.wrapS=bt,r.wrapT=bt;let[i,a]=this.material.repeat??[1,1];return r.repeat.set(i,a),r.anisotropy=4,n&&(r.colorSpace=Yn),this.loadedTextures.push(r),r}free(){for(let e of this.loadedTextures)e.dispose();this.loadedTextures=[],super.free()}};function tC(e,t){let n=e=>{throw new y(`BAD_FORMAT`,`MeshInstance3D '${t}' ${e}`,{prop:`material`,validOptions:[...$S]})};(typeof e!=`object`||!e||Array.isArray(e))&&n(`material must be an object, got ${JSON.stringify(e)}.`);for(let t of Object.keys(e))$S.includes(t)||n(`material has unknown key '${t}' — must be one of [${$S.join(`, `)}].`);for(let t of[`map`,`normalMap`]){let r=e[t];r!==void 0&&(typeof r!=`string`||r.length===0)&&n(`material.${t} must be a non-empty texture URL string, got ${JSON.stringify(r)}.`)}if(e.repeat!==void 0){let t=e.repeat;(!Array.isArray(t)||t.length!==2||!t.every(e=>typeof e==`number`&&Number.isFinite(e)&&e>0))&&n(`material.repeat must be [u, v] with positive numbers, got ${JSON.stringify(t)}.`),!e.map&&!e.normalMap&&n(`material.repeat tiles nothing — add a 'map' and/or 'normalMap' texture URL.`)}for(let t of[`metalness`,`roughness`,`opacity`]){let r=e[t];r!==void 0&&!(typeof r==`number`&&r>=0&&r<=1)&&n(`material.${t} must be a number in 0..1, got ${JSON.stringify(r)}.`)}for(let t of[`wireframe`,`flatShading`]){let r=e[t];r!==void 0&&typeof r!=`boolean`&&n(`material.${t} must be a boolean, got ${JSON.stringify(r)}.`)}}function nC(e,t){let n=t[0]??1,r=t[1]??1,i=t[2]??1;switch(e){case`box`:return new Ss(n,r,i);case`sphere`:return new ks(n,32,16);case`gem`:return new Ds(n,0);case`capsule`:return new Cs(n,r,8,16);case`plane`:{let e=new Os(n,i);return e.rotateX(-Math.PI/2),e}case`cylinder`:return new ws(n,n,r,24);default:throw new y(`PROP_TYPE_MISMATCH`,`MeshInstance3D.mesh must be one of box|sphere|capsule|plane|cylinder|gem, got '${e}'.`)}}var rC={Hips:`hips`,Spine:`spine`,Spine1:`chest`,Spine2:`upperChest`,Neck:`neck`,Head:`head`,LeftShoulder:`leftShoulder`,LeftArm:`leftUpperArm`,LeftForeArm:`leftLowerArm`,LeftHand:`leftHand`,RightShoulder:`rightShoulder`,RightArm:`rightUpperArm`,RightForeArm:`rightLowerArm`,RightHand:`rightHand`,LeftUpLeg:`leftUpperLeg`,LeftLeg:`leftLowerLeg`,LeftFoot:`leftFoot`,LeftToeBase:`leftToes`,RightUpLeg:`rightUpperLeg`,RightLeg:`rightLowerLeg`,RightFoot:`rightFoot`,RightToeBase:`rightToes`,LeftHandThumb1:`leftThumbMetacarpal`,LeftHandThumb2:`leftThumbProximal`,LeftHandThumb3:`leftThumbDistal`,LeftHandIndex1:`leftIndexProximal`,LeftHandIndex2:`leftIndexIntermediate`,LeftHandIndex3:`leftIndexDistal`,LeftHandMiddle1:`leftMiddleProximal`,LeftHandMiddle2:`leftMiddleIntermediate`,LeftHandMiddle3:`leftMiddleDistal`,LeftHandRing1:`leftRingProximal`,LeftHandRing2:`leftRingIntermediate`,LeftHandRing3:`leftRingDistal`,LeftHandPinky1:`leftLittleProximal`,LeftHandPinky2:`leftLittleIntermediate`,LeftHandPinky3:`leftLittleDistal`,RightHandThumb1:`rightThumbMetacarpal`,RightHandThumb2:`rightThumbProximal`,RightHandThumb3:`rightThumbDistal`,RightHandIndex1:`rightIndexProximal`,RightHandIndex2:`rightIndexIntermediate`,RightHandIndex3:`rightIndexDistal`,RightHandMiddle1:`rightMiddleProximal`,RightHandMiddle2:`rightMiddleIntermediate`,RightHandMiddle3:`rightMiddleDistal`,RightHandRing1:`rightRingProximal`,RightHandRing2:`rightRingIntermediate`,RightHandRing3:`rightRingDistal`,RightHandPinky1:`rightLittleProximal`,RightHandPinky2:`rightLittleIntermediate`,RightHandPinky3:`rightLittleDistal`},iC={};for(let[e,t]of Object.entries(rC))iC[`mixamorig${e}`]=t,iC[`mixamorig:${e}`]=t;function aC(e,t,n){let r=[],i=new Set,a=new V,o=new V,s=new V,c=n.meta?.metaVersion===`0`,l=t.getObjectByName(`mixamorigHips`)??t.getObjectByName(`mixamorig:Hips`),u=n.humanoid?.getNormalizedBoneNode(`hips`),d=l&&u?Math.abs(u.getWorldPosition(new H).y)/Math.max(1e-6,Math.abs(l.getWorldPosition(new H).y)):1;for(let l of e.tracks){let e=l.name.lastIndexOf(`.`),u=l.name.slice(0,e),f=l.name.slice(e+1),p=iC[u],m=p?n.humanoid?.getNormalizedBoneNode(p)?.name:void 0,h=t.getObjectByName(u);if(!m||!h){i.add(u);continue}if(h.getWorldQuaternion(a).invert(),h.parent?h.parent.getWorldQuaternion(o):o.identity(),l instanceof ic&&f===`quaternion`){let e=Float32Array.from(l.values);for(let t=0;t<e.length;t+=4)s.set(e[t]??0,e[t+1]??0,e[t+2]??0,e[t+3]??1),s.premultiply(o).multiply(a),e[t]=c?-s.x:s.x,e[t+1]=s.y,e[t+2]=c?-s.z:s.z,e[t+3]=s.w;r.push(new ic(`${m}.quaternion`,[...l.times],[...e]))}else if(l instanceof oc&&f===`position`){let e=Float32Array.from(l.values);for(let t=0;t<e.length;t+=3)e[t]=(c?-(e[t]??0):e[t]??0)*d,e[t+1]=(e[t+1]??0)*d,e[t+2]=(c?-(e[t+2]??0):e[t+2]??0)*d;r.push(new oc(`${m}.position`,[...l.times],[...e]))}}return{clip:new sc(`${e.name} (VRM)`,e.duration,r),retargetedTracks:r.length,skippedBones:[...i]}}var oC=.2,sC=class extends Zv{static typeName=`ModelInstance3D`;static signals=[`animationFinished`];static props={...Zv.props,model:{default:``},animation:{default:``},targetHeight:{default:0},castShadow:{default:!1},animationLoop:{default:!0},receiveShadow:{default:!1},tint:{default:``}};model=``;animation=``;targetHeight=0;castShadow=!1;animationLoop=!0;receiveShadow=!1;tint=``;store=null;entry=null;mountedRef=``;fittedHeight=-1;fitGroup=null;mixer=null;playing=``;currentAction=null;actionKeys=new WeakMap;warnedClaim=!1;tintTargets=[];tintedClones=[];appliedTint=``;_createObject3D(){return new Fi}availableAnimations(){let e=this.entry?.animations.map(e=>e.name)??[],t=this.store?.animationRefs()??[];return[...e,...t]}_syncModel(e){this.store=e;let t=this._ensureObject3D();if(this.model===``){this.fitGroup&&this.unmount(t);return}let n=e.getModel(this.model);if(!(n.status!==`ready`||!n.scene)){if(this.mountedRef!==this.model){this.unmount(t);let e;if(n.isVrm){if(n.claimedBy&&n.claimedBy!==this){this.warnedClaim||(this.warnedClaim=!0,console.warn(`incanto: VRM '${this.model}' is already displayed by another node — a VRM asset can back only one ModelInstance3D.`));return}n.claimedBy=this,e=n.scene}else e=V_(n.scene);let r=new Fi;r.add(e),t.add(r),this.fitGroup=r,this.entry=n,this.mountedRef=this.model,this.mixer=new ul(e),this.mixer.addEventListener(`finished`,e=>{let t=e.action,n=(t&&this.actionKeys.get(t))??this.animation;this.emit(`animationFinished`,n)}),this.playing=``,this.currentAction=null,this.fittedHeight=-1,e.traverse(e=>{e.userData.incantoModelShared=!0}),this.tintTargets=[],this.tintedClones=[],this.appliedTint=``,e.traverse(e=>{let t=e;t.isMesh&&t.material&&this.tintTargets.push({mesh:t,original:t.material})})}if(this.fitGroup&&this.fittedHeight!==this.targetHeight)if(this.fittedHeight=this.targetHeight,this.targetHeight>0){let e=this.fitGroup.scale.x;this.fitGroup.scale.setScalar(1);let t=cC(this.fitGroup).getSize(new H),n=t.y>0?this.targetHeight/t.y:e;n<.05||n>20?(this.fitGroup.scale.setScalar(1),console.warn(`[incanto] ModelInstance3D '${this.name}': targetHeight fit measured an implausible scale (${n.toFixed(2)}x) — skinned rigs defeat bounding-box measurement. Rendering at the model's authored scale instead.`)):this.fitGroup.scale.setScalar(n)}else this.fitGroup.scale.setScalar(1);this.fitGroup&&this.fitGroup.traverse(e=>{let t=e;t.isMesh&&(t.castShadow=this.castShadow,t.receiveShadow=this.receiveShadow)}),this.applyTint(),this.syncAnimation(e)}}applyTint(){if(this.tint===this.appliedTint)return;for(let e of this.tintedClones)e.dispose();this.tintedClones=[];let e=this.tint?new K(this.tint):null;for(let t of this.tintTargets){if(!e){t.mesh.material=t.original;continue}let n=t=>{let n=t.clone(),r=n,i=t.color;return r.color&&=i?i.clone().multiply(e):e.clone(),n.userData.incantoTinted=!0,this.tintedClones.push(n),n};t.mesh.material=Array.isArray(t.original)?t.original.map(n):n(t.original)}this.appliedTint=this.tint}syncAnimation(e){if(!this.mixer||!this.entry||this.playing===this.animation)return;if(this.animation===``){this.currentAction&&this.currentAction.fadeOut(oC),this.currentAction=null,this.playing=``;return}let t=this.entry.animations.find(e=>e.name===this.animation)??null;if(!t&&(this.animation.startsWith(`$`)||/\.(glb|gltf)$/i.test(this.animation))){let n=e.getAnimation(this.animation);if(n.status===`loading`)return;if(n.status===`ready`&&n.scene){let e=(n.clip?n.clips.find(e=>e.name===n.clip):void 0)??n.clips[0];if(e)if(this.entry.isVrm&&this.entry.vrm){let r=this.entry.retargeted.get(this.animation);if(r)t=r;else{let r=aC(e,n.scene,this.entry.vrm);r.skippedBones.length>0&&console.warn(`incanto: retarget '${this.animation}' skipped bones: ${r.skippedBones.join(`, `)}`),this.entry.retargeted.set(this.animation,r.clip),t=r.clip}}else t=e}}if(this.playing=this.animation,t){let e=this.mixer.clipAction(t);if(e.reset(),this.animationLoop?e.setLoop(In,1/0):(e.setLoop(Fn,1),e.clampWhenFinished=!0),e.setEffectiveWeight(1),e.play(),this.actionKeys.set(e,this.animation),this.currentAction&&this.currentAction!==e){let n=t.duration>0&&this.currentAction.getClip().duration>0;e.crossFadeFrom(this.currentAction,oC,n)}this.currentAction=e}else console.warn(`incanto: model '${this.model}' has no animation '${this.animation}' — embedded: ${this.entry.animations.map(e=>e.name).join(`, `)||`(none)`}; assets: ${e.animationRefs().join(`, `)||`(none)`}`)}update(e){this.mixer?.update(e),this.entry?.isVrm&&this.entry.claimedBy===this&&this.entry.vrm?.update(e)}onExitTree(){this.unmount(this._ensureObject3D())}unmount(e){this.entry?.claimedBy===this&&(this.entry.claimedBy=null),this.fitGroup&&e.remove(this.fitGroup);for(let e of this.tintedClones)e.dispose();this.tintedClones=[],this.tintTargets=[],this.appliedTint=``,this.fitGroup=null,this.mixer=null,this.currentAction=null,this.entry=null,this.mountedRef=``,this.playing=``,this.fittedHeight=-1}};function cC(e){e.updateWorldMatrix(!0,!0);let t=new aa,n=new aa,r=e.matrixWorld.clone().invert(),i=new G,a=new H;return e.traverse(e=>{let o=e;if(o.isSkinnedMesh&&o.skeleton){for(let e of o.skeleton.boneInverses)i.copy(e).invert().premultiply(o.matrixWorld).premultiply(r),a.setFromMatrixPosition(i),t.expandByPoint(a);return}let s=e;s.isMesh&&s.geometry&&(s.geometry.computeBoundingBox(),s.geometry.boundingBox&&(n.copy(s.geometry.boundingBox).applyMatrix4(s.matrixWorld).applyMatrix4(r),t.union(n)))}),t}var lC=.01,uC=class e extends Zv{static typeName=`Particles3D`;static signals=[`finished`];static props={preset:{default:`custom`,options:[`custom`,...He]},emitting:{default:!0},rate:{default:40},burst:{default:0},lifetime:{default:[.6,1.2]},speed:{default:[40,120]},directionDeg:{default:-90},spreadDeg:{default:30},gravity:{default:[0,0]},drag:{default:0},sizeStart:{default:8},sizeEnd:{default:2},colorStart:{default:`#ffffff`},colorEnd:{default:`#ffffff`},paletteColors:{default:[]},alphaStart:{default:1},alphaEnd:{default:0},shimmer:{default:0},blend:{default:`add`,options:[`add`,`normal`]},depthTest:{default:!0},maxParticles:{default:256}};preset=`custom`;emitting=!0;rate=40;burst=0;lifetime=[.6,1.2];speed=[40,120];directionDeg=-90;spreadDeg=30;gravity=[0,0];drag=0;sizeStart=8;sizeEnd=2;colorStart=`#ffffff`;colorEnd=`#ffffff`;paletteColors=[];alphaStart=1;alphaEnd=0;shimmer=0;blend=`add`;depthTest=!0;maxParticles=256;static validateJson(e){let t=e;if(t.preset!==`custom`&&!He.includes(t.preset))throw new y(`BAD_FORMAT`,`Particles3D '${e.name}' preset must be 'custom' or one of [${He.join(`, `)}], got '${t.preset}'.`,{prop:`preset`,validOptions:He});if(t.blend!==`add`&&t.blend!==`normal`)throw new y(`BAD_FORMAT`,`Particles3D '${e.name}' blend must be 'add' or 'normal', got '${t.blend}'.`,{prop:`blend`,validOptions:[`add`,`normal`]})}sim=null;bursted=!1;finishedEmitted=!1;_ensureSim(){if(!this.sim){Ue(this,this.preset,fe(e.typeName));let t=this.tree?.engine?.rng??new b(4660);this.sim=new Ge({rate:this.emitting?this.rate:0,lifetime:[this.lifetime[0]??1,this.lifetime[1]??1],speed:[(this.speed[0]??0)*lC,(this.speed[1]??0)*lC],directionDeg:this.directionDeg,spreadDeg:this.spreadDeg,gravity:[(this.gravity[0]??0)*lC,(this.gravity[1]??0)*lC,0],drag:this.drag,maxParticles:this.maxParticles,spreadZ:!0},t)}return this.sim}update(e){let t=this._ensureSim();this.burst>0&&!this.bursted&&(this.bursted=!0,t.burst(this.burst)),t.update(e),(!this.emitting||this.rate===0)&&this.bursted&&t.done&&!this.finishedEmitted&&(this.finishedEmitted=!0,this.emit(`finished`))}replay(){this.bursted=!1,this.finishedEmitted=!1}points=null;positions=null;colors=null;paletteCache=[];paletteCacheKey=``;_syncObject3D(){super._syncObject3D(),this.syncParticles()}refreshPalette(){let e=this.paletteColors;if(e.length===0)return null;let t=e.join(`|`);if(t!==this.paletteCacheKey){this.paletteCacheKey=t;for(let t=0;t<e.length;t++){let n=this.paletteCache[t];n||(n=new K,this.paletteCache[t]=n),n.set(e[t])}this.paletteCache.length=e.length}return this.paletteCache}syncParticles(){let e=this._ensureSim();if(!this.points||(this.positions?.length??0)!==this.maxParticles*3){this.points?.removeFromParent(),this.positions=new Float32Array(this.maxParticles*3),this.colors=new Float32Array(this.maxParticles*4);let e=new Ha;e.setAttribute(`position`,new q(this.positions,3)),e.setAttribute(`color`,new q(this.colors,4));let t=new us({size:(this.sizeStart+this.sizeEnd)/2*lC,map:hC(),vertexColors:!0,transparent:!0,depthWrite:!1,depthTest:this.depthTest,alphaTest:.02,sizeAttenuation:!0,blending:this.blend===`add`?2:1});this.points=new hs(e,t),this.points.frustumCulled=!1,this._ensureObject3D().add(this.points)}this.points.renderOrder=this.renderOrder;let t=this.positions,n=this.colors,r=this.refreshPalette(),i=r?null:dC.set(this.colorStart),a=fC.set(this.colorEnd),o=this.shimmer,s=0;e.forEach(e=>{t[s*3]=e.x,t[s*3+1]=-e.y,t[s*3+2]=e.z;let c=r?r[Math.floor(e.seed*r.length)]:i,l=Math.max(0,this.alphaStart+(this.alphaEnd-this.alphaStart)*e.t);o>0&&(l*=.5+.5*Math.sin(2*Math.PI*(e.age*o+e.seed))),pC.copy(c).lerp(a,e.t),n[s*4]=pC.r,n[s*4+1]=pC.g,n[s*4+2]=pC.b,n[s*4+3]=l,s+=1});let c=this.points.geometry;c.setDrawRange(0,s),c.getAttribute(`position`).needsUpdate=!0,c.getAttribute(`color`).needsUpdate=!0}},dC=new K,fC=new K,pC=new K,mC=null;function hC(){if(mC)return mC;let e=63/2,t=new Uint8Array(4096*4);for(let n=0;n<64;n++)for(let r=0;r<64;r++){let i=(r-e)/e,a=(n-e)/e,o=Math.hypot(i,a),s=.35,c;if(o<=s)c=1;else{let e=Math.max(0,1-(o-s)/(1-s));c=e*e}let l=Math.round(c*255),u=(n*64+r)*4;t[u]=255,t[u+1]=255,t[u+2]=255,t[u+3]=l}let n=new ko(t,64,64,Wt);return n.magFilter=Et,n.minFilter=Et,n.generateMipmaps=!1,n.needsUpdate=!0,mC=n,n}function gC(e){let{branch:t,leaves:n,type:r}=e,i=0,a=0,o=[{level:0,sections:t.sections[0],segments:t.segments[0]}];for(;o.length>0;){let e=o.shift();if(i+=e.sections*e.segments*2,e.level>=t.levels){a+=n.count===0?0:n.count+ +(r===`deciduous`);continue}r===`deciduous`&&o.push({level:e.level+1,sections:e.sections,segments:e.segments});let s=t.children[e.level]??0;for(let n=0;n<s;n++)o.push({level:e.level+1,sections:t.sections[e.level+1]??1,segments:t.segments[e.level+1]??3})}let s=n.billboard===`double`?2:1,c=a*s*2;return{branches:i,leaves:c,total:i+c}}var _C={ash:{type:`deciduous`,branch:{levels:3,angle:[0,48,75,60],children:[7,4,3,0],force:{direction:{x:0,y:1,z:0},strength:-.06},gnarliness:[.03,.25,.2,.09],length:[43.47,27.14,9.51,4.6],radius:[2,.63,.76,.7],sections:[12,8,6,4],segments:[12,6,4,3],start:[0,.23,.33,0],taper:[.7,.7,.7,.7],twist:[.09,-.07,0,0]},leaves:{style:`ash`,billboard:`double`,angle:55,count:16,start:0,size:2.67,sizeVariance:.72,alphaTest:.5}},aspen:{type:`deciduous`,branch:{levels:2,angle:[0,75,32,7],children:[10,3,3,0],force:{direction:{x:0,y:1,z:0},strength:.0148},gnarliness:[.05,.12,.12,.02],length:[50,6.07,11.19,1],radius:[.72,.41,.7,.7],sections:[12,10,8,6],segments:[8,6,4,3],start:[0,.59,.35,0],taper:[.37,.13,.7,.7],twist:[0,0,0,0]},leaves:{style:`aspen`,billboard:`double`,angle:30,count:11,start:.124,size:2.5,sizeVariance:.7,alphaTest:.5}},oak:{type:`deciduous`,branch:{levels:3,angle:[0,54,58,32],children:[6,4,3,0],force:{direction:{x:0,y:1,z:0},strength:-.01},gnarliness:[0,-.1,-.15,.09],length:[37.24,11.08,12.39,7.16],radius:[1.41,.9,.69,1.19],sections:[8,6,3,1],segments:[7,5,3,3],start:[0,.49,.06,.12],taper:[.73,.42,.69,.75],twist:[-.23,.42,0,0]},leaves:{style:`oak`,billboard:`double`,angle:42,count:18,start:.16,size:2.5,sizeVariance:.7,alphaTest:.5}},pine:{type:`evergreen`,branch:{levels:1,angle:[0,110,16,60],children:[82,3,5,0],force:{direction:{x:0,y:1,z:0},strength:-.003},gnarliness:[.05,.08,0,0],length:[50,23.87,14.08,1],radius:[1.05,.36,.7,.7],sections:[12,10,8,6],segments:[8,6,4,3],start:[0,.27,.14,.3],taper:[.7,.7,.7,.7],twist:[0,0,0,0]},leaves:{style:`pine`,billboard:`double`,angle:39,count:30,start:.09,size:1.435,sizeVariance:.201,alphaTest:.3}},bush:{type:`deciduous`,branch:{levels:3,angle:[0,21.5,62.6,60],children:[7,3,2,0],force:{direction:{x:0,y:1,z:0},strength:-.02},gnarliness:[.11,.09,.05,.09],length:[.1,15.3,5.59,4.6],radius:[.58,.95,.76,.7],sections:[6,6,4,3],segments:[4,4,4,3],start:[0,.53,.33,0],taper:[.7,.7,.7,.7],twist:[.3,-.07,0,0]},leaves:{style:`ash`,billboard:`double`,angle:55,count:8,start:0,size:2.45,sizeVariance:.717,alphaTest:.5}},"ash-forest":{type:`deciduous`,branch:{levels:2,angle:[0,48,75,60],children:[5,3,0,0],force:{direction:{x:0,y:1,z:0},strength:-.06},gnarliness:[.03,.25,.2,.09],length:[43.47,27.14,9.51,4.6],radius:[2,.63,.76,.7],sections:[6,5,4,4],segments:[6,4,3,3],start:[0,.3,.33,0],taper:[.7,.7,.7,.7],twist:[.09,-.07,0,0]},leaves:{style:`ash`,billboard:`double`,angle:55,count:15,start:0,size:3,sizeVariance:.6,alphaTest:.5}},"oak-forest":{type:`deciduous`,branch:{levels:2,angle:[0,54,58,32],children:[5,3,0,0],force:{direction:{x:0,y:1,z:0},strength:-.01},gnarliness:[0,-.1,-.15,.09],length:[37.24,11.08,12.39,7.16],radius:[1.41,.9,.69,1.19],sections:[6,4,3,1],segments:[6,4,3,3],start:[0,.49,.06,.12],taper:[.73,.42,.69,.75],twist:[-.23,.42,0,0]},leaves:{style:`oak`,billboard:`double`,angle:42,count:16,start:.1,size:2.9,sizeVariance:.7,alphaTest:.5}},"pine-forest":{type:`evergreen`,branch:{levels:1,angle:[0,110,16,60],children:[44,3,5,0],force:{direction:{x:0,y:1,z:0},strength:-.005},gnarliness:[.07,.09,0,0],length:[50,23.87,14.08,1],radius:[1.05,.36,.7,.7],sections:[6,5,8,6],segments:[6,3,4,3],start:[0,.27,.14,.3],taper:[.7,.7,.7,.7],twist:[0,0,0,0]},leaves:{style:`pine`,billboard:`double`,angle:39,count:9,start:.09,size:1.8,sizeVariance:.201,alphaTest:.3}},"bush-forest":{type:`deciduous`,branch:{levels:2,angle:[0,21.5,62.6,60],children:[6,3,0,0],force:{direction:{x:0,y:1,z:0},strength:-.02},gnarliness:[.11,.09,.05,.09],length:[.1,15.3,5.59,4.6],radius:[.58,.95,.76,.7],sections:[5,4,3,3],segments:[4,3,3,3],start:[0,.53,.33,0],taper:[.7,.7,.7,.7],twist:[.3,-.07,0,0]},leaves:{style:`ash`,billboard:`double`,angle:55,count:10,start:0,size:3.2,sizeVariance:.6,alphaTest:.5}}},vC=[`ash`,`aspen`,`oak`,`pine`,`bush`,`ash-forest`,`oak-forest`,`pine-forest`,`bush-forest`];function yC(e){let t=_C[e];if(!t)throw new y(`BAD_FORMAT`,`Unknown eztree preset '${e}' — must be one of [${vC.join(`, `)}].`,{prop:`preset`,validOptions:vC});return structuredClone(t)}var bC={ash:{texture:`oak`,tint:`#cecbbe`,repeat:[.5,1/5]},aspen:{texture:`birch`,tint:`#ffffff`,repeat:[1,1]},oak:{texture:`oak`,tint:`#fff3d1`,repeat:[1,1/10]},pine:{texture:`pine`,tint:`#ffffff`,repeat:[1,1]},bush:{texture:`oak`,tint:`#cecbbe`,repeat:[.5,1/5]}};function xC(e){if(!vC.includes(e))throw new y(`BAD_FORMAT`,`Unknown eztree preset '${e}' — must be one of [${vC.join(`, `)}].`,{prop:`preset`,validOptions:vC});return structuredClone(bC[e.replace(/-forest$/,``)])}var SC=.45,CC=.45,wC=.55,TC=.15,EC=.07,DC=.035;function OC(e,t){let n=Math.imul(e+1,374761393)+Math.imul(t+1,668265263)>>>0;return n=Math.imul(n^n>>>13,1274126177)>>>0,((n^n>>>16)>>>0)/4294967296}function kC(e,t,n){let r=e.length/3,i=new Float32Array(e.length),a=new Float32Array(e.length),o=1/0,s=1/0,c=1/0,l=-1/0,u=-1/0,d=-1/0;for(let t=0;t<r;t++){let n=e[t*3],r=e[t*3+1],i=e[t*3+2];n<o&&(o=n),n>l&&(l=n),r<s&&(s=r),r>u&&(u=r),i<c&&(c=i),i>d&&(d=i)}let f=(o+l)/2,p=(s+u)/2,m=(c+d)/2,h=Math.max((l-o)/2,.001),g=Math.max((u-s)/2,.001),_=Math.max((d-c)/2,.001),v=new Map;for(let n=0;n<r;n++){let r=t[n],i=v.get(r);i||(i={x:0,y:0,z:0,n:0},v.set(r,i)),i.x+=e[n*3],i.y+=e[n*3+1],i.z+=e[n*3+2],i.n++}for(let e of v.values())e.x/=e.n,e.y/=e.n,e.z/=e.n;for(let o=0;o<r;o++){let r=e[o*3],c=e[o*3+1],l=e[o*3+2],d=(r-f)/h,y=(c-p)/g,b=(l-m)/_,x,S,C,w;if(n===`evergreen`){let e=Math.hypot(r-f,l-m);if(e<1e-6)x=0,S=1,C=0;else{let t=1/Math.hypot(1,CC);x=(r-f)/e*t,S=CC*t,C=(l-m)/e*t}let t=(c-s)/Math.max(u-s,.001),n=Math.max(TC,1-t);w=Math.min(1,Math.hypot(d,b)/n)}else{let e=d/h,t=y/g,n=b/_,r=Math.hypot(e,t,n);r<1e-6?(x=0,S=1,C=0):(x=e/r,S=t/r,C=n/r),w=Math.min(1,Math.hypot(d,y,b))}let T=v.get(t[o]);if(T){let e=r-T.x,t=c-T.y,n=l-T.z,i=Math.hypot(e,t,n);if(i>1e-6){x=x*(1-wC)+e/i*wC,S=S*(1-wC)+t/i*wC,C=C*(1-wC)+n/i*wC;let r=Math.hypot(x,S,C);r>1e-6?(x/=r,S/=r,C/=r):(x=0,S=1,C=0)}}i[o*3]=x,i[o*3+1]=S,i[o*3+2]=C;let E=SC+(1-SC)*w,D=t[o],O=E*(1-EC*OC(D,1)),k=(OC(D,2)-.5)*2*DC;a[o*3]=O*(1+k),a[o*3+1]=O,a[o*3+2]=O*(1-k)}return{normals:i,colors:a}}var AC=class e{static MASK=4294967295;mW;mZ;constructor(t){this.mW=123456789+t&e.MASK,this.mZ=987654321-t&e.MASK}random(t=1,n=0){this.mZ=36969*(this.mZ&65535)+(this.mZ>>16)&e.MASK,this.mW=18e3*(this.mW&65535)+(this.mW>>16)&e.MASK;let r=((this.mZ<<16)+(this.mW&65535)>>>0)/4294967296;return(t-n)*r+n}};function jC(e,t,n){let r=new AC(t),i={branchVerts:[],branchNormals:[],branchUvs:[],branchIndices:[],leafVerts:[],leafIndices:[],leafUvs:[],leafAnchors:[],leafClusters:[],leafClusterNext:0},a=[{origin:new H,orientation:new _i,length:e.branch.length[0],radius:e.branch.radius[0],level:0,sectionCount:e.branch.sections[0],segmentCount:e.branch.segments[0]}];for(;a.length>0;)MC(e,r,i,a,a.shift());zC(i,n);let o=new Ha;o.setAttribute(`position`,new q(new Float32Array(i.branchVerts),3)),o.setAttribute(`normal`,new q(new Float32Array(i.branchNormals),3)),o.setAttribute(`uv`,new q(new Float32Array(i.branchUvs),2)),o.setIndex(new q(BC(i.branchIndices,i.branchVerts),1)),o.computeBoundingSphere();let s=null;if(i.leafVerts.length>0){s=new Ha,s.setAttribute(`position`,new q(new Float32Array(i.leafVerts),3)),s.setAttribute(`anchor`,new q(new Float32Array(i.leafAnchors),3)),s.setAttribute(`uv`,new q(new Float32Array(i.leafUvs),2)),s.setIndex(new q(BC(i.leafIndices,i.leafVerts),1));let t=kC(i.leafVerts,i.leafClusters,e.type);s.setAttribute(`normal`,new q(t.normals,3)),s.setAttribute(`color`,new q(t.colors,3)),s.computeBoundingSphere()}return{branches:o,leaves:s}}function MC(e,t,n,r,i){let a=n.branchVerts.length/3,o=i.orientation.clone(),s=i.origin.clone(),c=i.length/i.sectionCount,l=[];for(let r=0;r<=i.sectionCount;r++){let a=i.radius;r===i.sectionCount&&i.level===e.branch.levels?a=.001:e.type===`deciduous`?a*=1-(e.branch.taper[i.level]??0)*(r/i.sectionCount):a*=1-r/i.sectionCount;let u=new H,d=new H,f=0;for(let e=0;e<i.segmentCount;e++){let t=2*Math.PI*e/i.segmentCount,c=new H(Math.cos(t),0,Math.sin(t)).multiplyScalar(a).applyEuler(o).add(s),l=new H(Math.cos(t),0,Math.sin(t)).applyEuler(o).normalize(),p=r%2==0?0:1;n.branchVerts.push(c.x,c.y,c.z),n.branchNormals.push(l.x,l.y,l.z),n.branchUvs.push(e/i.segmentCount,p),e===0&&(u=c,d=l,f=p)}n.branchVerts.push(u.x,u.y,u.z),n.branchNormals.push(d.x,d.y,d.z),n.branchUvs.push(1,f),l.push({origin:s.clone(),orientation:o.clone(),radius:a}),s.add(new H(0,c,0).applyEuler(o));let p=Math.max(1,1/Math.sqrt(a))*(e.branch.gnarliness[i.level]??0);o.x+=t.random(p,-p),o.z+=t.random(p,-p);let m=new V().setFromEuler(o),h=new V().setFromAxisAngle(new H(0,1,0),e.branch.twist[i.level]??0),g=e.branch.force,_=new V().setFromUnitVectors(new H(0,1,0),new H(g.direction.x,g.direction.y,g.direction.z));m.multiply(h),m.rotateTowards(_,g.strength/a),o.setFromQuaternion(m)}RC(n,a,i);let u=i.level===e.branch.levels?n.leafClusterNext++:0;if(e.type===`deciduous`){let a=l[l.length-1];i.level<e.branch.levels?r.push({origin:a.origin,orientation:a.orientation,length:e.branch.length[i.level+1]??0,radius:a.radius,level:i.level+1,sectionCount:i.sectionCount,segmentCount:i.segmentCount}):e.leaves.count>0&&LC(e,t,n,a.origin,a.orientation,u)}i.level===e.branch.levels?IC(e,t,n,l,u):i.level<e.branch.levels&&FC(e,t,r,e.branch.children[i.level]??0,i.level+1,l)}function NC(e,t){let n=Math.floor(t*(e.length-1)),r=e[n],i=n===e.length-1?r:e[n+1],a=(t-n/(e.length-1))/(1/(e.length-1)),o=new H().lerpVectors(r.origin,i.origin,a),s=new V().setFromEuler(r.orientation),c=new V().setFromEuler(i.orientation);return{origin:o,orientation:new _i().setFromQuaternion(c.slerp(s,a)),radius:{a:r.radius,b:i.radius,alpha:a}}}function PC(e,t,n){let r=new V().setFromAxisAngle(new H(1,0,0),t/(180/Math.PI)),i=new V().setFromAxisAngle(new H(0,1,0),n),a=new V().setFromEuler(e);return new _i().setFromQuaternion(a.multiply(i.multiply(r)))}function FC(e,t,n,r,i,a){let o=t.random();for(let s=0;s<r;s++){let c=t.random(1,e.branch.start[i]??0),l=NC(a,c),u=(e.branch.radius[i]??0)*((1-l.radius.alpha)*l.radius.a+l.radius.alpha*l.radius.b),d=2*Math.PI*(o+s/r),f=PC(l.orientation,e.branch.angle[i]??0,d),p=(e.branch.length[i]??0)*(e.type===`evergreen`?1-c:1);n.push({origin:l.origin,orientation:f,length:p,radius:u,level:i,sectionCount:e.branch.sections[i]??1,segmentCount:e.branch.segments[i]??3})}}function IC(e,t,n,r,i){let a=t.random();for(let o=0;o<e.leaves.count;o++){let s=NC(r,t.random(1,e.leaves.start)),c=2*Math.PI*(a+o/e.leaves.count),l=PC(s.orientation,e.leaves.angle,c);LC(e,t,n,s.origin,l,i)}}function LC(e,t,n,r,i,a){let o=n.leafVerts.length/3,s=e.leaves.size*(1+t.random(e.leaves.sizeVariance,-e.leaves.sizeVariance)),c=e=>{let t=s/2,c=[new H(-t,s,0),new H(-t,0,0),new H(t,0,0),new H(t,s,0)];for(let t of c)t.applyEuler(new _i(0,e,0)).applyEuler(i).add(r),n.leafVerts.push(t.x,t.y,t.z),n.leafAnchors.push(r.x,r.y,r.z),n.leafClusters.push(a);n.leafUvs.push(0,1,0,0,1,0,1,1),n.leafIndices.push(o,o+1,o+2,o,o+2,o+3),o+=4};c(0),e.leaves.billboard===`double`&&c(Math.PI/2)}function RC(e,t,n){let r=n.segmentCount+1;for(let i=0;i<n.sectionCount;i++)for(let a=0;a<n.segmentCount;a++){let n=t+i*r+a,o=t+i*r+(a+1),s=n+r,c=o+r;e.branchIndices.push(n,s,o,o,s,c)}}function zC(e,t){let n=0;for(let t=1;t<e.branchVerts.length;t+=3){let r=e.branchVerts[t];r>n&&(n=r)}for(let t=1;t<e.leafVerts.length;t+=3){let r=e.leafVerts[t];r>n&&(n=r)}if(n<=0)return;let r=t/n;for(let t=0;t<e.branchVerts.length;t++)e.branchVerts[t]=e.branchVerts[t]*r;for(let t=0;t<e.leafVerts.length;t++)e.leafVerts[t]=e.leafVerts[t]*r;for(let t=0;t<e.leafAnchors.length;t++)e.leafAnchors[t]=e.leafAnchors[t]*r}function BC(e,t){return t.length/3>65535?new Uint32Array(e):new Uint16Array(e)}var VC=[`simple`,`medium`,`high`],HC=[`conifer`,`broadleaf`,`dead`,`bush`];function UC(e,t,n){return new y(`BAD_FORMAT`,`Tree3D ${e} must be one of [${n.join(`, `)}], got '${t}'.`,{prop:e,validOptions:n})}var WC={simple:0,medium:1,high:2},GC=[5,7,10],KC=[2,3,4],qC=[6,8,12],JC=[2,3,3],YC=[0,1,2],XC=[3,4,5],ZC=3;function QC(e,t){if(!VC.includes(e))throw UC(`tier`,e,VC);if(!HC.includes(t))throw UC(`type`,t,HC);let n=e,r=t,i=WC[n];return{tier:n,type:r,trunkRadialSegments:GC[i],canopyLayers:r===`conifer`?KC[i]:0,canopySegments:qC[i],blobCount:r===`broadleaf`||r===`bush`?JC[i]:0,blobDetail:YC[i],branchCount:r===`dead`?XC[i]:r===`conifer`&&n===`high`?ZC:0,hasCanopy:r!==`dead`,canopySway:n!==`simple`}}var $C=.02,ew={conifer:{trunkHeight:.4,trunkRadiusBottom:.05,trunkRadiusTop:.035},broadleaf:{trunkHeight:.42,trunkRadiusBottom:.05,trunkRadiusTop:.032},dead:{trunkHeight:.72,trunkRadiusBottom:.045,trunkRadiusTop:.018},bush:{trunkHeight:.14,trunkRadiusBottom:.045,trunkRadiusTop:.035}};function tw(e,t,n){let{trunkHeight:r,trunkRadiusBottom:i,trunkRadiusTop:a}=ew[e.type],o=[],s=new ws(a,i,r,e.trunkRadialSegments).toNonIndexed();if(s.translate(0,r/2,0),o.push(s),e.type===`dead`)for(let n=0;n<e.branchCount;n++){let r=.22+t.next()*.14,a=new ws(.006,.02,r,5).toNonIndexed();a.translate(0,r/2,0);let s=.7+t.next()*.6,c=n/e.branchCount*Math.PI*2+t.next()*.8,l=.34+(n+t.next())/e.branchCount*.34;a.applyMatrix4(new G().makeRotationY(c).multiply(new G().makeRotationX(s)).setPosition(Math.sin(c)*i*.6,l,Math.cos(c)*i*.6)),o.push(a)}let c=rw(o);return aw(c,new K(n),.7,1.05,t),c}function nw(e,t,n){if(!e.hasCanopy)return null;let r=[];if(e.type===`conifer`){let n=.74;for(let i=0;i<e.canopyLayers;i++){let a=i/e.canopyLayers,o=.3*(1-a*.62)*(.92+t.next()*.16),s=n/e.canopyLayers*1.7,c=new Ts(o,s,e.canopySegments).toNonIndexed();iw(c,t,$C),c.translate(0,.26+n*a+s/2,0),r.push(c)}for(let n=0;n<e.branchCount;n++){let i=new Ts(.06,.2,5).toNonIndexed(),a=n/Math.max(e.branchCount,1)*Math.PI*2+t.next(),o=1.1+t.next()*.4;i.applyMatrix4(new G().makeRotationY(a).multiply(new G().makeRotationX(o)).setPosition(Math.sin(a)*.22,.34+t.next()*.18,Math.cos(a)*.22)),r.push(i)}}else{let{trunkHeight:n}=ew[e.type];for(let i=0;i<e.blobCount;i++){let a=new Ds((i===0?.3:.24-i*.02)*(.92+t.next()*.16),e.blobDetail).toNonIndexed();a.scale(1,.72,1),iw(a,t,$C);let o=i===0?0:.13,s=t.next()*Math.PI*2;a.translate(Math.cos(s)*o,n+.16+i*.1+t.next()*.05,Math.sin(s)*o),r.push(a)}}let i=rw(r);return aw(i,new K(n),.62,1.18,t),i}function rw(e){let t=0;for(let n of e)t+=n.getAttribute(`position`).count;let n=new Float32Array(t*3),r=new Float32Array(t*3),i=0;for(let t of e)n.set(t.getAttribute(`position`).array,i),r.set(t.getAttribute(`normal`).array,i),i+=t.getAttribute(`position`).count*3,t.dispose();let a=new Ha;return a.setAttribute(`position`,new q(n,3)),a.setAttribute(`normal`,new q(r,3)),a}function iw(e,t,n){let r=e.getAttribute(`position`);for(let e=0;e<r.count;e++)r.setXYZ(e,r.getX(e)+t.range(-n,n),r.getY(e)+t.range(-n,n),r.getZ(e)+t.range(-n,n))}function aw(e,t,n,r,i){let a=e.getAttribute(`position`),o=new Float32Array(a.count*3),s=1/0,c=-1/0;for(let e=0;e<a.count;e++){let t=a.getY(e);t<s&&(s=t),t>c&&(c=t)}let l=Math.max(c-s,1e-6),u=new K;for(let e=0;e<a.count;e++){let c=(a.getY(e)-s)/l,d=(n+(r-n)*c)*(.96+i.next()*.08);u.copy(t).multiplyScalar(d),o[e*3]=u.r,o[e*3+1]=u.g,o[e*3+2]=u.b}e.setAttribute(`color`,new q(o,3))}var ow=500,sw=15e5,cw=[.8,1.2],lw=.28,uw=.12,dw=.055,fw=.11,pw=3,mw=101,hw=[.78,.7,.45],gw=.45,_w=.55,vw=`https://agent8-games.verse8.io/assets/3D/default/textures/vegetation`,yw=`https://agent8-games.verse8.io/assets/3D/default/textures/vegetation/bark`,bw={high:{conifer:[`pine`,`pine`,`pine`],broadleaf:[`oak`,`ash`,`oak`],dead:[`ash`,`ash`,`ash`],bush:[`bush`,`bush`,`bush`]},medium:{conifer:[`pine-forest`,`pine-forest`,`pine-forest`],broadleaf:[`oak-forest`,`ash-forest`,`oak-forest`],dead:[`ash-forest`,`ash-forest`,`ash-forest`],bush:[`bush-forest`,`bush-forest`,`bush-forest`]}},xw=class e extends Zv{static typeName=`Tree3D`;static props={tier:{default:`medium`,options:VC},type:{default:`conifer`,options:HC},seed:{default:1},height:{default:6},count:{default:1},area:{default:[10,10]},trunkColor:{default:`#7a5a3a`},canopyColor:{default:`#4a7c3f`},leafTexture:{default:``},leafFadeStart:{default:0},leafFadeEnd:{default:0},leafShadows:{default:!0},drape:{default:!1},terrain:{default:``}};tier=`medium`;type=`conifer`;seed=1;height=6;count=1;area=[10,10];trunkColor=`#7a5a3a`;canopyColor=`#4a7c3f`;leafTexture=``;leafFadeStart=0;leafFadeEnd=0;leafShadows=!0;drape=!1;terrain=``;drapeTerrain=null;drapeWorld=[0,0,0];static validateJson(e){let t=e;if(QC(t.tier,t.type),!(t.height>0))throw new y(`BAD_FORMAT`,`Tree3D '${e.name}' height must be > 0 meters, got ${t.height}.`,{prop:`height`});if(!Number.isInteger(t.count)||t.count<1||t.count>ow)throw new y(`BAD_FORMAT`,`Tree3D '${e.name}' count must be an integer in 1..${ow}, got ${t.count}.`,{prop:`count`});if(!((t.area[0]??0)>0)||!((t.area[1]??0)>0))throw new y(`BAD_FORMAT`,`Tree3D '${e.name}' area must be positive [x, z] meters, got ${JSON.stringify(t.area)}.`,{prop:`area`});if(t.leafFadeEnd!==0&&!(t.leafFadeEnd>t.leafFadeStart&&t.leafFadeStart>=0))throw new y(`BAD_FORMAT`,`Tree3D '${e.name}' needs leafFadeEnd > leafFadeStart >= 0 (the leaf LOD window) or 0/0 to disable, got start ${t.leafFadeStart}, end ${t.leafFadeEnd}.`,{prop:`leafFadeStart`});if(typeof t.leafShadows!=`boolean`)throw new y(`BAD_FORMAT`,`Tree3D '${e.name}' leafShadows must be a boolean, got ${JSON.stringify(t.leafShadows)}.`,{prop:`leafShadows`});if(typeof t.drape!=`boolean`)throw new y(`BAD_FORMAT`,`Tree3D '${e.name}' drape must be a boolean, got ${JSON.stringify(t.drape)}.`,{prop:`drape`});if(typeof t.terrain!=`string`)throw new y(`BAD_FORMAT`,`Tree3D '${e.name}' terrain must be a node-path string (empty = auto-find), got ${JSON.stringify(t.terrain)}.`,{prop:`terrain`});if(t.tier===`simple`)return;let n=ww(t.tier,t.type),r=n*t.count;if(r>sw)throw new y(`BAD_FORMAT`,`Tree3D '${e.name}' blows the grove triangle budget: count ${t.count} × ${n} tris/tree (tier '${t.tier}', type '${t.type}') = ${r} > ${sw}. Lower count (max ${Math.floor(sw/n)} at this tier), drop tier to 'medium'/'simple', or split the grove into multiple nodes.`,{prop:`count`})}time=0;grove=null;variants=[];simpleTrunk=null;simpleCanopy=null;swayUniform=null;leafFadeUniform=null;loadedTextures=[];groveKey=``;_createObject3D(){return new Fi}_trunk(){return this.rebuildIfNeeded(),this.simpleTrunk??this.variants[0]?.branches}_canopy(){return this.rebuildIfNeeded(),this.simpleTrunk?this.simpleCanopy:this.variants[0]?.leaves??null}_variantMeshes(){return this.rebuildIfNeeded(),this.variants}update(e){this.time+=e,this.swayUniform&&(this.swayUniform.value=this.time),this.leafFadeUniform&&(this.leafFadeUniform.start.value=this.leafFadeStart,this.leafFadeUniform.end.value=this.leafFadeEnd)}_syncObject3D(){super._syncObject3D(),this.rebuildIfNeeded()}rebuildIfNeeded(){this.grove||(this.grove=new Fi,this._ensureObject3D().add(this.grove));let e=JSON.stringify([this.tier,this.type,this.seed,this.height,this.count,this.area,this.trunkColor,this.canopyColor,this.leafTexture,this.leafShadows,this.leafFadeEnd>this.leafFadeStart,this.drape,this.terrain]);e===this.groveKey&&(this.simpleTrunk||this.variants.length>0)||(this.groveKey=e,this.disposeMeshes(),this.resolveDrape(),this.tier===`simple`?this.buildSimpleGrove():this.buildEzGrove())}resolveDrape(){if(this.drapeTerrain=null,!this.drape)return;let e=_x(this,this.terrain);if(e)this.drapeTerrain=e,this.drapeWorld=gx(this);else if(this.terrain!==``)throw new y(`NODE_NOT_FOUND`,`Tree3D '${this.name}' drape terrain '${this.terrain}' is not a Terrain3D in the tree.`,{prop:`terrain`})}bedY(e,t){let n=this.drapeTerrain;return n?n.heightAt(this.drapeWorld[0]+e,this.drapeWorld[2]+t)-this.drapeWorld[1]:0}buildSimpleGrove(){let e=QC(this.tier,this.type),t=new b(this.seed),n=tw(e,t,this.trunkColor),r=nw(e,t,this.canopyColor),i=new Vo(n,Aw(),this.count);Mw(i);let a=null;r&&(a=new Vo(r,Aw(),this.count),Mw(a)),this.scatter(t,(e,t,n)=>{i.setMatrixAt(e,t),i.setColorAt(e,n),a?.setMatrixAt(e,t),a?.setColorAt(e,n)}),jw(i),a&&jw(a),this.simpleTrunk=i,this.simpleCanopy=a,this.grove.add(i),a&&this.grove.add(a)}buildEzGrove(){let e=bw[this.tier][this.type],t=Math.min(pw,this.count),n=Array.from({length:t},(e,n)=>Math.floor(this.count/t)+ +(n<this.count%t));this.swayUniform={value:this.time},this.leafFadeUniform=this.leafFadeEnd>this.leafFadeStart?{start:{value:this.leafFadeStart},end:{value:this.leafFadeEnd}}:null;let r=Dw(this.canopyColor),i=null;typeof document<`u`&&typeof createImageBitmap==`function`&&(i=new Wc,i.setOptions({imageOrientation:`flipY`}));for(let a=0;a<t;a++){let t=n[a],o=e[a],s=yC(o);this.type===`dead`&&(s.leaves.count=0);let c=jC(s,this.seed+a*mw,1),l=this.buildBarkMaterial(o,i),u=new Vo(c.branches,l,t);Mw(u);let d=null;if(c.leaves){let e=new Bs({color:r.clone().multiply(new K(hw[0],hw[1],hw[2])),roughness:1,side:2,vertexColors:!0,alphaTest:s.leaves.alphaTest}),n=this.leafTexture||Tw(s.leaves.style);i&&(e.map=Cw(i,n,e=>{e.colorSpace=Yn,e.flipY=!1},e=>Ew(e))),aS(e,this.swayUniform,gw,this.leafFadeUniform??void 0),d=new Vo(c.leaves,e,t),Mw(d),this.leafShadows||(d.castShadow=!1)}this.variants.push({branches:u,leaves:d}),this.grove.add(u),d&&this.grove.add(d)}let a=Array(t).fill(0),o=new b(this.seed);this.scatter(o,(e,n,r)=>{let i=e%t,o=this.variants[i],s=a[i];a[i]=s+1,o.branches.setMatrixAt(s,n),o.branches.setColorAt(s,r),o.leaves?.setMatrixAt(s,n),o.leaves?.setColorAt(s,r)});for(let e of this.variants)jw(e.branches),e.leaves&&jw(e.leaves)}buildBarkMaterial(t,n){if(this.type===`dead`||!n)return new Bs({color:new K(this.trunkColor),roughness:1});let r=xC(t),i=new Bs({color:this.trunkColor===e.props.trunkColor?.default?new K(r.tint):kw(this.trunkColor),roughness:1});return iS(i,_w),i.map=this.loadBarkTexture(n,r,`color`,!0),i.normalMap=this.loadBarkTexture(n,r,`normal`,!1),i.roughnessMap=this.loadBarkTexture(n,r,`roughness`,!1),i}loadBarkTexture(e,t,n,r){return Cw(e,`${yw}/${t.texture}_${n}_1k.jpg`,e=>{e.wrapS=bt,e.wrapT=bt,e.repeat.set(t.repeat[0],t.repeat[1]),e.anisotropy=4,e.flipY=!1,r&&(e.colorSpace=Yn)})}scatter(e,t){let n=(this.area[0]??0)/2,r=(this.area[1]??0)/2;for(let i=0;i<this.count;i++){let a=this.count===1?0:e.range(-n,n),o=this.count===1?0:e.range(-r,r);Fw.set(a,this.drape?this.bedY(a,o):0,o),Iw.setFromAxisAngle(Nw,e.range(0,Math.PI*2));let s=this.height*e.range(cw[0],cw[1]),c=s*e.range(.9,1.1);Lw.set(c,s,c),Pw.compose(Fw,Iw,Lw),Rw.setHSL(lw,uw,.92).offsetHSL(e.range(-.055,dw),0,e.range(-.11,fw)),t(i,Pw,Rw)}}disposeMeshes(){let e=[this.simpleTrunk,this.simpleCanopy];for(let t of this.variants)e.push(t.branches,t.leaves);let t=new Set;for(let n of e)n&&(n.removeFromParent(),n.geometry.dispose(),t.add(n.material),n.dispose());for(let e of t)e.dispose();for(let e of this.loadedTextures)e.dispose();this.loadedTextures=[],this.simpleTrunk=null,this.simpleCanopy=null,this.variants=[],this.swayUniform=null,this.leafFadeUniform=null}free(){this.disposeMeshes(),this.grove=null,super.free()}},Sw=new Map;function Cw(e,t,n,r){let i=Sw.get(t);if(i)return i;let a=new ni;return n(a),e.load(t,e=>{a.image=e,r?r(a):a.needsUpdate=!0}),Sw.set(t,a),a}function ww(e,t){let n=0;for(let r of new Set(bw[e][t]??[])){let e=yC(r);t===`dead`&&(e.leaves.count=0),n=Math.max(n,gC(e).total)}return n}function Tw(e){return`${vw}/${e}_color.png`}function Ew(e){let t=e.image;if(!t||typeof document>`u`)return;let n=document.createElement(`canvas`);n.width=t.naturalWidth||t.width,n.height=t.naturalHeight||t.height;let r=n.getContext(`2d`,{willReadFrequently:!0});if(!r)return;r.drawImage(t,0,0);let i;try{i=r.getImageData(0,0,n.width,n.height)}catch{return}let a=i.data,o=0,s=0,c=0,l=0;for(let e=0;e<a.length;e+=4)a[e+3]>240&&(o+=a[e],s+=a[e+1],c+=a[e+2],l++);if(l!==0){o=Math.round(o/l),s=Math.round(s/l),c=Math.round(c/l);for(let e=0;e<a.length;e+=4)a[e+3]<=240&&(a[e]=o,a[e+1]=s,a[e+2]=c);r.putImageData(i,0,0),e.image=n,e.needsUpdate=!0}}function Dw(e){let t=new K(e),n=Math.max(t.r,t.g,t.b);return n>0?t.multiplyScalar(1/n):t.setRGB(1,1,1),t.lerp(Ow,.18)}var Ow=new K(1,1,1);function kw(e){let t=new K(e),n=Math.max(t.r,t.g,t.b);return n>0?t.multiplyScalar(1/n):t.setRGB(1,1,1),t}function Aw(){return new Bs({vertexColors:!0,roughness:1,flatShading:!0})}function jw(e){e.instanceMatrix.needsUpdate=!0,e.instanceColor&&(e.instanceColor.needsUpdate=!0),hx(e)}function Mw(e){e.castShadow=!0,e.receiveShadow=!0}var Nw=new H(0,1,0),Pw=new G,Fw=new H,Iw=new V,Lw=new H,Rw=new K,zw=[[[.4,.4,.4],[.4,.4,.4],[.4,.4,.4],[.4,.4,.4],[.4,.4,.4],[.4,.4,.4]],[[.6,.3,0],[.6,.3,0],[.6,.3,0],[.6,.3,0],[0,.8,0],[.6,.3,0]],[[.6,.3,0],[.6,.3,0],[.6,.3,0],[.6,.3,0],[.6,.3,0],[.6,.3,0]],[[.6,.55,.5],[.55,.5,.45],[.5,.45,.4],[.55,.5,.45],[.65,.6,.55],[.5,.45,.4]],[[.45,.45,.4],[.4,.4,.35],[.35,.35,.3],[.4,.4,.35],[.5,.5,.45],[.35,.35,.3]],[[.2,.2,.2],[.2,.2,.2],[.2,.2,.2],[.2,.2,.2],[.2,.2,.2],[.2,.2,.2]],[[.8,.5,.2],[.75,.45,.15],[.7,.4,.1],[.75,.45,.15],[.85,.55,.25],[.7,.4,.1]],[[0,.7,.1],[0,.65,.1],[0,.6,.1],[0,.65,.1],[0,.75,.1],[0,.6,.1]],[[.3,.3,.3],[.3,.3,.3],[.3,.3,.3],[.3,.3,.3],[.3,.3,.3],[.3,.3,.3]],[[0,0,.7],[0,0,.7],[0,0,.7],[0,0,.7],[0,.2,.9],[0,0,.6]],[[.9,.3,0],[.9,.3,0],[.9,.3,0],[.9,.3,0],[1,.4,0],[.7,.2,0]],[[.9,.8,.2],[.85,.75,.18],[.85,.75,.18],[.85,.75,.18],[.95,.85,.25],[.85,.75,.18]],[[.6,.6,.6],[.55,.55,.55],[.5,.5,.5],[.55,.55,.55],[.65,.65,.65],[.5,.5,.5]],[[.7,.7,.4],[.65,.65,.35],[.6,.6,.3],[.65,.65,.35],[.75,.75,.45],[.6,.6,.3]],[[.7,.5,.5],[.65,.45,.45],[.6,.4,.4],[.65,.45,.45],[.75,.55,.55],[.6,.4,.4]],[[.35,.3,.25],[.3,.25,.2],[.25,.2,.15],[.3,.25,.2],[.4,.35,.3],[.25,.2,.15]],[[.5,.3,0],[.5,.3,0],[.5,.3,0],[.5,.3,0],[.7,.4,.1],[.7,.4,.1]],[[0,.5,0],[0,.45,0],[0,.45,0],[0,.45,0],[0,.6,0],[0,.4,0]],[[.2,.8,.8],[.15,.7,.7],[.1,.6,.6],[.15,.7,.7],[.3,.9,.9],[.1,.6,.6]],[[1,.5,.8],[.9,.4,.7],[.8,.3,.6],[.9,.4,.7],[1,.6,.9],[.8,.3,.6]],[[1,.9,.3],[.95,.85,.25],[.9,.8,.2],[.95,.85,.25],[1,1,.4],[.9,.8,.2]],[[1,0,0],[.9,0,0],[.8,0,0],[.9,0,0],[1,.2,.2],[.8,0,0]],[[.6,.4,.2],[.5,.3,.15],[.4,.25,.1],[.5,.3,.15],[.7,.45,.25],[.4,.25,.1]],[[.6,.2,.8],[.5,.15,.7],[.4,.1,.6],[.5,.15,.7],[.7,.25,.9],[.4,.1,.6]],[[1,.8,0],[.95,.75,0],[.95,.75,0],[.95,.75,0],[1,.9,0],[.9,.7,0]],[[.6,.65,.7],[.55,.6,.65],[.5,.55,.6],[.55,.6,.65],[.65,.7,.75],[.5,.55,.6]]],Bw=2e5,Vw=class extends Zv{static typeName=`VoxelGrid3D`;static signals=[`blocksChanged`];static props={voxels:{default:[]}};voxels=[];appliedVoxels=null;map=new Map;mesh=null;dirty=!1;blockCount(){return this.map.size}tileAt(e,t,n){return this.map.get(`${e},${t},${n}`)}setBlocks(e){this.map.clear();for(let t of e)this.map.set(`${t.x},${t.y},${t.z}`,t.tile);this.dirty=!0,this.emit(`blocksChanged`)}addBlock(e){this.map.set(`${e.x},${e.y},${e.z}`,e.tile),this.dirty=!0,this.emit(`blocksChanged`)}removeBlockAt(e,t,n){let r=this.map.delete(`${e},${t},${n}`);return r&&(this.dirty=!0,this.emit(`blocksChanged`)),r}blocks(){let e=[];for(let[t,n]of this.map){let[r,i,a]=t.split(`,`).map(Number);e.push({x:r,y:i,z:a,tile:n})}return e}_createObject3D(){let e=new Ss(1,1,1),t=new Rs({vertexShader:Uw,fragmentShader:Ww});this.mesh=new Vo(e,t,Bw),this.mesh.count=0,this.mesh.frustumCulled=!1;for(let e of Hw)this.mesh.geometry.setAttribute(e,new No(new Float32Array(Bw*3),3));return this.dirty=!0,this.mesh}update(){if(this.voxels.length>0&&this.voxels!==this.appliedVoxels&&(this.appliedVoxels=this.voxels,this.setBlocks(this.voxels.map(e=>({x:e[0]??0,y:e[1]??0,z:e[2]??0,tile:e[3]??0})))),!this.dirty||!this.mesh)return;this.dirty=!1;let e=new G,t=0;for(let[n,r]of this.map){if(t>=Bw)break;let[i,a,o]=n.split(`,`).map(Number);e.setPosition(i,a,o),this.mesh.setMatrixAt(t,e);let s=zw[r]??zw[0];for(let e=0;e<6;e++){let n=this.mesh.geometry.getAttribute(Hw[e]),r=s[e];n.setXYZ(t,r[0],r[1],r[2]),n.needsUpdate=!0}t++}this.mesh.count=t,this.mesh.instanceMatrix.needsUpdate=!0}},Hw=[`colorFront`,`colorRight`,`colorBack`,`colorLeft`,`colorTop`,`colorBottom`],Uw=`
|
|
5825
5825
|
attribute vec3 colorFront;
|
|
5826
5826
|
attribute vec3 colorRight;
|
|
5827
5827
|
attribute vec3 colorBack;
|
|
@@ -5842,7 +5842,7 @@ void main() {
|
|
|
5842
5842
|
if (abs(n.x) > 0.5) vUv2 = p.zy; else if (abs(n.y) > 0.5) vUv2 = p.xz; else vUv2 = p.xy;
|
|
5843
5843
|
gl_Position = projectionMatrix * modelViewMatrix * instanceMatrix * vec4(position, 1.0);
|
|
5844
5844
|
}
|
|
5845
|
-
`,
|
|
5845
|
+
`,Ww=`
|
|
5846
5846
|
varying vec3 vColor;
|
|
5847
5847
|
varying vec3 vNormal;
|
|
5848
5848
|
varying vec2 vUv2;
|
|
@@ -5853,7 +5853,7 @@ void main() {
|
|
|
5853
5853
|
if (vUv2.x < edge || vUv2.x > 1.0 - edge || vUv2.y < edge || vUv2.y > 1.0 - edge) color *= 0.75;
|
|
5854
5854
|
gl_FragColor = vec4(color, 1.0);
|
|
5855
5855
|
}
|
|
5856
|
-
`,
|
|
5856
|
+
`,Gw=new Yo,Kw=new G,qw=new Pa;function Jw(e,t,n){return Kw.multiplyMatrices(e.projectionMatrix,e.matrixWorldInverse),Gw.setFromProjectionMatrix(Kw),qw.center.set(t.x,t.y,t.z),qw.radius=n,Gw.intersectsSphere(qw)}function Yw(e){if(!e||typeof e!=`object`)return 0;switch(e.shape){case`capsule`:return(e.height??0)/2+(e.radius??0);case`sphere`:return e.radius??0;case`box`:return(e.size?.[1]??0)/2;default:return 0}}function Xw(e){return .55+Math.min(Math.max(e,0),12)/12*.45}function Zw(e,t,n=0){let r=[],i=[],a=new Set;for(let o of t){let t=e.has(o.id);(t?o.y<=o.waterline+n:o.y<o.waterline)?(a.add(o.id),t||r.push(o)):t&&i.push(o)}return{entered:r,exited:i,inWater:a}}function Qw(e,t,n,r,i=0,a=8){if(e.length>=a){let t=0;for(let n=1;n<e.length;n++)e[n].age>e[t].age&&(t=n);e.splice(t,1)}e.push({x:t,z:n,age:0,amp:r,foam:i})}function $w(e,t,n=3){for(let r=e.length-1;r>=0;r--){let i=e[r];i.age+=t,i.age>=n&&e.splice(r,1)}}function eT(e,t,n){let r=e-n.x,i=t-n.z,a=Math.sqrt(r*r+i*i);return n.amp*Math.sin(6*a-8*n.age)*Math.exp(-1.1*n.age)*Math.exp(-.15*a*a)}function tT(e,t,n){let r=0;for(let i of n)r+=eT(e,t,i);return r}function nT(e,t,n,r){let i=[];for(let n of e.keys())t.has(n)||e.delete(n);for(let a of t){let t=(e.get(a)??0)+n;t>=r?(i.push(a),e.set(a,t-r)):e.set(a,t)}return i}var rT=`
|
|
5857
5857
|
precision highp float;
|
|
5858
5858
|
|
|
5859
5859
|
// atmosphere: three's linear fog rides the standard chunks (compiled out
|
|
@@ -5877,7 +5877,7 @@ void main() {
|
|
|
5877
5877
|
// atmosphere: fog depth from the displaced surface position
|
|
5878
5878
|
#include <fog_vertex>
|
|
5879
5879
|
}
|
|
5880
|
-
`,
|
|
5880
|
+
`,iT=`
|
|
5881
5881
|
precision highp float;
|
|
5882
5882
|
|
|
5883
5883
|
// atmosphere: standard fog chunk (compiled out without scene fog)
|
|
@@ -6072,7 +6072,7 @@ void main() {
|
|
|
6072
6072
|
#include <colorspace_fragment>
|
|
6073
6073
|
#include <fog_fragment>
|
|
6074
6074
|
}
|
|
6075
|
-
`,
|
|
6075
|
+
`,aT=`
|
|
6076
6076
|
precision highp float;
|
|
6077
6077
|
|
|
6078
6078
|
// incanto atmosphere: three's linear fog rides the standard chunks (compiled
|
|
@@ -6224,7 +6224,7 @@ void main() {
|
|
|
6224
6224
|
vec4 mvPosition = viewMatrix * modelPosition;
|
|
6225
6225
|
#include <fog_vertex>
|
|
6226
6226
|
}
|
|
6227
|
-
`,
|
|
6227
|
+
`,oT=`
|
|
6228
6228
|
precision highp float;
|
|
6229
6229
|
|
|
6230
6230
|
// incanto atmosphere: standard fog chunk (compiled out without scene fog)
|
|
@@ -6871,13 +6871,13 @@ void main() {
|
|
|
6871
6871
|
#include <colorspace_fragment>
|
|
6872
6872
|
#include <fog_fragment>
|
|
6873
6873
|
}
|
|
6874
|
-
`;function rT(e,t,n,r,i=0){return t>=r.centerY?!1:Math.abs(e-r.centerX)<=r.halfW+i&&Math.abs(n-r.centerZ)<=r.halfD+i}var iT=22,aT={enabled:!0,color:`#cdeeff`,intensity:.55,scale:.32,speed:1};function oT(e,t){if(e===!1)return{enabled:!1,color:cT(t),visibility:iT,caustics:aT};let n=e&&typeof e==`object`?e:{};return{enabled:!0,color:n.color??cT(t),visibility:n.visibility??iT,caustics:sT(n.caustics)}}function sT(e){if(e===!1)return{...aT,enabled:!1};let t=e&&typeof e==`object`?e:{};return{enabled:!0,color:typeof t.color==`string`?t.color:aT.color,intensity:typeof t.intensity==`number`?t.intensity:aT.intensity,scale:typeof t.scale==`number`?t.scale:aT.scale,speed:typeof t.speed==`number`?t.speed:aT.speed}}function cT(e){let{r:t,g:n,b:r}=lT(e),i=(e,t,n)=>Math.round(e*(1-n)+t*n);return uT(Math.round(i(t,6,.45)*.7),Math.round(i(n,40,.4)*.78),Math.round(i(r,70,.35)*.82))}function lT(e){let t=e.replace(`#`,``),n=t.length===3?t.split(``).map(e=>e+e).join(``):t;return{r:Number.parseInt(n.slice(0,2),16)||0,g:Number.parseInt(n.slice(2,4),16)||0,b:Number.parseInt(n.slice(4,6),16)||0}}function uT(e,t,n){let r=e=>Math.max(0,Math.min(255,e)).toString(16).padStart(2,`0`);return`#${r(e)}${r(t)}${r(n)}`}var dT={frequency:6.7,persistence:.3,lacunarity:2.18,iterations:9,speed:.5},fT=.525,pT=.08,mT={freqX:.7,freqZ:.5,cross:.6};function hT(e,t){return e-Math.floor(e/t)*t}function gT(e){return e-Math.floor(e)}function _T(e){return hT((e*34+1)*e,289)}var vT=.211324865405187,yT=.366025403784439,bT=-.577350269189626,xT=.024390243902439;function ST(e,t){let n=Math.floor(e+(e+t)*yT),r=Math.floor(t+(e+t)*yT),i=e-n+(n+r)*vT,a=t-r+(n+r)*vT,o=+(i>a),s=i>a?0:1,c=i+vT-o,l=a+vT-s,u=i+bT,d=a+bT;n=hT(n,289),r=hT(r,289);let f=_T(_T(r)+n),p=_T(_T(r+s)+n+o),m=_T(_T(r+1)+n+1),h=Math.max(.5-(i*i+a*a),0),g=Math.max(.5-(c*c+l*l),0),_=Math.max(.5-(u*u+d*d),0);h*=h,h*=h,g*=g,g*=g,_*=_,_*=_;let v=2*gT(f*xT)-1,y=2*gT(p*xT)-1,b=2*gT(m*xT)-1,x=Math.abs(v)-.5,S=Math.abs(y)-.5,C=Math.abs(b)-.5,w=v-Math.floor(v+.5),T=y-Math.floor(y+.5),E=b-Math.floor(b+.5);h*=1.79284291400159-.85373472095314*(w*w+x*x),g*=1.79284291400159-.85373472095314*(T*T+S*S),_*=1.79284291400159-.85373472095314*(E*E+C*C);let D=w*i+x*a,O=T*c+S*l,k=E*u+C*d;return 130*(h*D+g*O+_*k)}function CT(e,t,n,r){let i=Math.max(r.scaleNorm,1e-6),a=e/i,o=t/i,s=0,c=1,l=r.frequency,u=n*r.speed;for(let e=0;e<r.iterations;e++)s+=c*ST(a*l+u,o*l+u),c*=r.persistence,l*=r.lacunarity;return s*r.amplitude}function wT(e,t,n,r){return r*(Math.sin(e*mT.freqX+n)+mT.cross*Math.sin(t*mT.freqZ+n*1.3))}var TT=[`fancy`,`simple`],ET=[`trough`,`surface`,`peak`],DT={trough:`#135a72`,surface:`#2a86a0`,peak:`#a5ddd6`},OT={peakThreshold:.08,peakTransition:.05,troughThreshold:-.01,troughTransition:.15,fresnelScale:1,fresnelPower:5},kT={color:`#dcebe8`,intensity:.32,rangeMin:.45,rangeMax:1.4,scale:.35,edgeColor:`#ffffff`,edgeIntensity:.6,edgeWidth:.5};function AT(e,t){return Math.min(kT.rangeMax,Math.max(kT.rangeMin,Math.min(e,t)*.012))}var jT=128,MT=fT/pT,NT=256,PT=1024,FT=.02,IT=4,LT=32,RT=256,zT=32,BT=.05,VT=.08,HT=.02,UT=.18,WT=.04,GT=1.4,KT=.04,qT=.45,JT=.08,YT=.4,XT=.22,ZT=.09,QT=.85,$T=class e extends Zv{static typeName=`Water3D`;static signals=[`entered`,`exited`];static props={size:{default:[40,40]},color:{default:`#2a6fbe`},opacity:{default:.8},renderOrder:{default:1},waveHeight:{default:.04},waveSpeed:{default:1},quality:{default:`fancy`,options:TT},colors:{default:{}},reflection:{default:!0},reflectionInterval:{default:1e3},foam:{default:!0},interaction:{default:!0},splash:{default:!0},sunDirection:{default:[.5,.8,.3]},sunColor:{default:`#fff5d6`},sunIntensity:{default:1},detailStrength:{default:.26},absorption:{default:.15},refraction:{default:!0},underwater:{default:!0}};size=[40,40];color=`#2a6fbe`;opacity=.8;renderOrder=1;waveHeight=.04;waveSpeed=1;quality=`fancy`;colors={};reflection=!0;reflectionInterval=1e3;foam=!0;interaction=!0;splash=!0;sunDirection=[.5,.8,.3];sunColor=`#fff5d6`;sunIntensity=1;detailStrength=.26;absorption=.15;refraction=!0;underwater=!0;static validateJson(e){let t=e;if(!((t.size[0]??0)>0)||!((t.size[1]??0)>0))throw new y(`BAD_FORMAT`,`Water3D '${e.name}' size must be positive [width, depth] meters, got ${JSON.stringify(t.size)}.`,{prop:`size`});if(!TT.includes(t.quality))throw new y(`BAD_FORMAT`,`Water3D '${e.name}' quality must be one of [${TT.join(`, `)}], got '${t.quality}'.`,{prop:`quality`,validOptions:TT});for(let[n,r]of Object.entries(t.colors)){if(!ET.includes(n))throw new y(`BAD_FORMAT`,`Water3D '${e.name}' colors has unknown key '${n}'. Valid keys: [${ET.join(`, `)}].`,{prop:`colors`,validOptions:ET});if(typeof r!=`string`)throw new y(`BAD_FORMAT`,`Water3D '${e.name}' colors.${n} must be a hex color string, got ${JSON.stringify(r)}.`,{prop:`colors`})}if(!(t.reflectionInterval>0))throw new y(`BAD_FORMAT`,`Water3D '${e.name}' reflectionInterval must be > 0 ms, got ${t.reflectionInterval}.`,{prop:`reflectionInterval`});let n=t.sunDirection;if(!Array.isArray(n)||n.length!==3||!n.every(e=>Number.isFinite(e))||Math.hypot(n[0]??0,n[1]??0,n[2]??0)===0)throw new y(`BAD_FORMAT`,`Water3D '${e.name}' sunDirection must be a non-zero [x, y, z] vector, got ${JSON.stringify(n)}.`,{prop:`sunDirection`});if(!(t.sunIntensity>=0))throw new y(`BAD_FORMAT`,`Water3D '${e.name}' sunIntensity must be >= 0, got ${t.sunIntensity}.`,{prop:`sunIntensity`});if(!(t.detailStrength>=0))throw new y(`BAD_FORMAT`,`Water3D '${e.name}' detailStrength must be >= 0, got ${t.detailStrength}.`,{prop:`detailStrength`});if(!(t.absorption>=0))throw new y(`BAD_FORMAT`,`Water3D '${e.name}' absorption must be >= 0 (per-meter Beer's-law constant), got ${t.absorption}.`,{prop:`absorption`});let r=t.underwater;if(typeof r!=`boolean`){if(typeof r!=`object`||!r||Array.isArray(r))throw new y(`BAD_FORMAT`,`Water3D '${e.name}' underwater must be true, false or an object ({ color?, visibility? }), got ${JSON.stringify(r)}.`,{prop:`underwater`});let t=r;if(t.color!==void 0&&typeof t.color!=`string`)throw new y(`BAD_FORMAT`,`Water3D '${e.name}' underwater.color must be a hex color string, got ${JSON.stringify(t.color)}.`,{prop:`underwater`});if(t.visibility!==void 0&&!(t.visibility>0))throw new y(`BAD_FORMAT`,`Water3D '${e.name}' underwater.visibility must be > 0 meters, got ${JSON.stringify(t.visibility)}.`,{prop:`underwater`})}}time=0;surfaceKey=``;_ripples=[];inWater=new Set;bobTimers=new Map;wadeAnchors=new Map;_scratchA={x:0,y:0,z:0};_scratchB={x:0,y:0,z:0};_bodies=[];cubeTarget=null;cubeCamera=null;reflectionNear=0;lastReflectionAt=0;scenePassTarget=null;_createObject3D(){return new ho}update(e){this.time+=e*this.waveSpeed,Yw(this._ripples,e),this.interaction&&this.stepInteraction(e)}_syncObject3D(){super._syncObject3D();let e=this._ensureObject3D();this.rebuildSurfaceIfNeeded(e),this.quality===`simple`?this.syncSimple(e):this.syncFancy(e)}_onRender3D(e){let t=this._ensureObject3D(),n=t.material;if(e.camera.updateWorldMatrix(!0,!1),e.camera.getWorldPosition(uE),this.underwaterAt(uE.x,uE.y,uE.z)||this.quality!==`fancy`||!n.isShaderMaterial)return;let r=Math.hypot((this.size[0]??1)/2,(this.size[1]??1)/2);if(!Ww(e.camera,tE(this),r+1))return;let i=n.uniforms,a=Date.now();if(this.reflection){this.cubeTarget||=new Xl(NT,{generateMipmaps:!0,minFilter:Ot,type:It});let n=Math.max((this.size[0]??1)/2,(this.size[1]??1)/2);if((!this.cubeCamera||this.reflectionNear!==n)&&(this.reflectionNear=n,this.cubeCamera=new qc(n,1e3,this.cubeTarget),this.lastReflectionAt=0),this.lastReflectionAt===0||a-this.lastReflectionAt>=this.reflectionInterval){this.lastReflectionAt=a;let n=tE(this),r=this.cubeCamera;r.position.set(n.x,n.y+.1,n.z);let i=t.visible;t.visible=!1,r.update(e.gl,e.scene),t.visible=i}i.uEnvironmentMap&&i.uUseEnvironmentMap&&(i.uEnvironmentMap.value=this.cubeTarget.texture,i.uUseEnvironmentMap.value=1)}e.gl.getDrawingBufferSize(lE);let o=Math.max(1,Math.min(Math.round(lE.x/2),PT)),s=Math.max(1,Math.min(Math.round(lE.y/2),PT));if(this.scenePassTarget&&(this.scenePassTarget.width!==o||this.scenePassTarget.height!==s)&&(this.scenePassTarget.depthTexture?.dispose(),this.scenePassTarget.dispose(),this.scenePassTarget=null),!this.scenePassTarget){let e=new ys(o,s);e.type=Ft,this.scenePassTarget=new ai(o,s,{depthTexture:e,depthBuffer:!0,stencilBuffer:!1,generateMipmaps:!0,minFilter:Ot})}let c=e.gl.getRenderTarget(),l=e.gl.autoClear,u=e.gl.clippingPlanes,d=t.visible;t.visible=!1;let f=tE(this).y;fE.constant=f+FT,e.gl.clippingPlanes=pE,e.gl.setRenderTarget(this.scenePassTarget),e.gl.autoClear=!0,e.gl.clear(!0,!0,!1),e.gl.render(e.scene,e.camera),mE.constant=-(f-FT),e.gl.clippingPlanes=hE;let p=e.scene.overrideMaterial,m=e.scene.background;e.scene.overrideMaterial=gE,e.scene.background=null,e.gl.autoClear=!1,e.gl.render(e.scene,e.camera),e.scene.overrideMaterial=p,e.scene.background=m,e.gl.setRenderTarget(c),e.gl.autoClear=l,e.gl.clippingPlanes=u,t.visible=d,i.uDepthTexture&&i.uSceneColor&&i.uCameraNear&&i.uCameraFar&&i.uUseSceneDepth&&(i.uDepthTexture.value=this.scenePassTarget.depthTexture,i.uSceneColor.value=this.scenePassTarget.texture,i.uCameraNear.value=e.camera.near,i.uCameraFar.value=e.camera.far,i.uUseSceneDepth.value=1,i.uUseRefraction&&(i.uUseRefraction.value=+!!this.refraction),i.uEnableFoam&&(i.uEnableFoam.value=+!!this.foam))}underwaterAt(e,t,n){if(this.underwater===!1)return null;let r=tE(this,this._scratchA);if(!rT(e,t,n,{centerX:r.x,centerY:r.y,centerZ:r.z,halfW:(this.size[0]??1)/2,halfD:(this.size[1]??1)/2},dE))return null;let i=oT(this.underwater,this.color);return i.enabled?{...i,surfaceY:r.y}:null}free(){let e=this._ensureObject3D();e.geometry?.dispose(),e.material?.dispose?.(),this.cubeTarget?.dispose(),this.cubeTarget=null,this.cubeCamera=null,this.scenePassTarget?.depthTexture?.dispose(),this.scenePassTarget?.dispose(),this.scenePassTarget=null,super.free()}rebuildSurfaceIfNeeded(e){let t=JSON.stringify([this.size,this.quality]);if(t===this.surfaceKey&&e.geometry.getAttribute(`position`))return;this.surfaceKey=t,e.geometry?.dispose(),e.material?.dispose?.();let n=this.size[0]??1,r=this.size[1]??1;this.quality===`simple`?(e.geometry=iE(n,r,zT),e.material=this.buildLakeMaterial()):(e.geometry=iE(n,r,Math.min(Math.max(Math.round(Math.max(n,r)*IT),LT),RT)),e.material=this.buildFancyMaterial())}buildFancyMaterial(){return new Rs({vertexShader:tT,fragmentShader:nT,fog:!0,uniforms:{fogColor:{value:new K(`#ffffff`)},fogNear:{value:1},fogFar:{value:1e3},fogDensity:{value:25e-5},uTime:{value:0},uOpacity:{value:this.opacity},uEnvironmentMap:{value:null},uUseEnvironmentMap:{value:0},uWavesAmplitude:{value:this.waveHeight*MT},uWavesSpeed:{value:dT.speed},uWavesFrequency:{value:dT.frequency},uWavesPersistence:{value:dT.persistence},uWavesLacunarity:{value:dT.lacunarity},uWavesIterations:{value:dT.iterations},uTroughColor:{value:new K(DT.trough)},uSurfaceColor:{value:new K(DT.surface)},uPeakColor:{value:new K(DT.peak)},uPeakThreshold:{value:OT.peakThreshold},uPeakTransition:{value:OT.peakTransition},uTroughThreshold:{value:OT.troughThreshold},uTroughTransition:{value:OT.troughTransition},uFresnelScale:{value:OT.fresnelScale},uFresnelPower:{value:OT.fresnelPower},uScale:{value:new H(1,1,1)},uWaterCenter:{value:new H},uRipples:{value:Array.from({length:8},()=>new ri)},uRippleFoam:{value:Array.from({length:8},()=>0)},uSplashFoam:{value:+!!this.splash},uSunDirection:{value:new H(.5,.8,.3)},uSunColor:{value:new K(this.sunColor)},uSunIntensity:{value:this.sunIntensity},uDetailStrength:{value:this.detailStrength},uAbsorption:{value:this.absorption},uUseSceneDepth:{value:0},uUseRefraction:{value:0},uSceneColor:{value:null},uEnableFoam:{value:0},uFoamColor:{value:new K(kT.color)},uFoamIntensity:{value:kT.intensity},uFoamRange:{value:AT(this.size[0]??1,this.size[1]??1)},uFoamScale:{value:kT.scale},uDepthTexture:{value:null},uCameraNear:{value:.1},uCameraFar:{value:1e3},uEdgeColor:{value:new K(kT.edgeColor)},uEdgeIntensity:{value:kT.edgeIntensity},uEdgeWidth:{value:kT.edgeWidth}},transparent:!0,depthTest:!0,depthWrite:!0,side:2})}buildLakeMaterial(){return new Rs({vertexShader:$w,fragmentShader:eT,fog:!0,uniforms:{fogColor:{value:new K(`#ffffff`)},fogNear:{value:1},fogFar:{value:1e3},fogDensity:{value:25e-5},uTime:{value:0},uOpacity:{value:this.opacity},uColor:{value:new K(this.color)},uSurfaceColor:{value:new K(this.color)},uSkyColor:{value:new K(this.color)},uSunDirection:{value:new H(.5,.8,.3).normalize()},uSunColor:{value:new K(this.sunColor)},uSunIntensity:{value:this.sunIntensity},uDetailStrength:{value:this.detailStrength},uFresnelPower:{value:OT.fresnelPower},uScale:{value:new H(1,1,1)},uWaterCenter:{value:new H},uRipples:{value:Array.from({length:8},()=>new ri)},uRippleFoam:{value:Array.from({length:8},()=>0)},uSplashFoam:{value:+!!this.splash},uFoamColor:{value:new K(kT.color)}},transparent:!0,depthTest:!0,depthWrite:!1,side:2})}syncFancy(e){let t=e.material.uniforms,n=this.size[0]??1,r=this.size[1]??1,i=tE(this);t.uTime&&(t.uTime.value=this.time),t.uOpacity&&(t.uOpacity.value=this.opacity),t.uWavesAmplitude&&(t.uWavesAmplitude.value=this.waveHeight*MT),(t.uScale?.value)?.set(n/2,r/2,r/2),(t.uWaterCenter?.value)?.set(i.x,i.y,i.z);let a=this.resolvePalette();(t.uTroughColor?.value)?.copy(a.trough),(t.uSurfaceColor?.value)?.copy(a.surface),(t.uPeakColor?.value)?.copy(a.peak),(t.uSunDirection?.value)?.set(this.sunDirection[0]??0,this.sunDirection[1]??1,this.sunDirection[2]??0).normalize(),(t.uSunColor?.value)?.set(this.sunColor),t.uSunIntensity&&(t.uSunIntensity.value=this.sunIntensity),t.uDetailStrength&&(t.uDetailStrength.value=this.detailStrength),t.uAbsorption&&(t.uAbsorption.value=this.absorption),!this.reflection&&t.uUseEnvironmentMap&&(t.uUseEnvironmentMap.value=0),!this.foam&&t.uEnableFoam&&(t.uEnableFoam.value=0),!this.refraction&&t.uUseRefraction&&(t.uUseRefraction.value=0),t.uSplashFoam&&(t.uSplashFoam.value=+!!this.splash);let o=t.uRipples?.value,s=t.uRippleFoam?.value;if(o)for(let e=0;e<o.length;e++){let t=this._ripples[e];t?o[e].set(t.x,t.z,t.age,t.amp):o[e].set(0,0,0,0),s&&(s[e]=t?t.foam??0:0)}}_applySunDirection(t){let n=e.props.sunDirection?.default;this.sunDirection.every((e,t)=>e===n[t])&&(this._ensureObject3D().material.uniforms?.uSunDirection?.value)?.set(t[0],t[1],t[2])}resolvePalette(){let t=this.colors,n=this.color===e.props.color?.default?null:this.color,r=t.surface??n??DT.surface;return cE.surface.set(r),t.trough?cE.trough.set(t.trough):n?cE.trough.set(n).lerp(aE,.45):cE.trough.set(DT.trough),t.peak?cE.peak.set(t.peak):n?cE.peak.set(n).lerp(oE,.3):cE.peak.set(DT.peak),cE}syncSimple(e){let t=e.material;t.opacity=this.opacity;let n=t.uniforms,r=this.size[0]??1,i=this.size[1]??1,a=tE(this,this._scratchA);n.uTime&&(n.uTime.value=this.time),n.uOpacity&&(n.uOpacity.value=this.opacity),(n.uSurfaceColor?.value)?.set(this.color).lerp(oE,.12),(n.uColor?.value)?.set(this.color).lerp(aE,.4),(n.uSkyColor?.value)?.set(this.color).lerp(sE,.82),(n.uScale?.value)?.set(r/2,i/2,i/2),(n.uWaterCenter?.value)?.set(a.x,a.y,a.z),(n.uSunDirection?.value)?.set(this.sunDirection[0]??0,this.sunDirection[1]??1,this.sunDirection[2]??0).normalize(),(n.uSunColor?.value)?.set(this.sunColor),n.uSunIntensity&&(n.uSunIntensity.value=this.sunIntensity),n.uDetailStrength&&(n.uDetailStrength.value=this.detailStrength),n.uSplashFoam&&(n.uSplashFoam.value=+!!this.splash);let o=n.uRipples?.value,s=n.uRippleFoam?.value;if(o)for(let e=0;e<o.length;e++){let t=this._ripples[e];t?o[e].set(t.x,t.z,t.age,t.amp):o[e].set(0,0,0,0),s&&(s[e]=t?t.foam??0:0)}let c=e.geometry,l=c.getAttribute(`position`),u=c.getAttribute(`normal`),d=this.time,f=this.waveHeight,p=a.x,m=a.z,h=this._ripples,g=.5;for(let e=0;e<l.count;e++){let t=l.getX(e),n=l.getZ(e),r=wT(t,n,d,f)+Zw(t+p,n+m,h);l.setY(e,r);let i=wT(t+g,n,d,f)+Zw(t+p+g,n+m,h),a=wT(t,n+g,d,f)+Zw(t+p,n+m+g,h),o=(r-i)/g,s=(r-a)/g,c=1/Math.hypot(o,1,s);u.setXYZ(e,o*c,c,s*c)}l.needsUpdate=!0,u.needsUpdate=!0}stepInteraction(e){let t=this;for(;t.parent;)t=t.parent;let n=this._bodies;n.length=0,eE(t,n);let r=tE(this,this._scratchA),i=(this.size[0]??0)/2,a=(this.size[1]??0)/2,o=[];for(let e of n){let t=tE(e,this._scratchB);if(Math.abs(t.x-r.x)>i||Math.abs(t.z-r.z)>a)continue;let n=Gw(e.collider);o.push({id:e,x:t.x,y:t.y,z:t.z,waterline:r.y+this.waveOffsetAt(t.x,t.z,r)+n})}let{entered:s,exited:c,inWater:l}=qw(this.inWater,o,BT);this.inWater=l;let u=this.splash;for(let e of s){let t=Math.abs(nE(e.id)?.[1]??0);Jw(this._ripples,e.x,e.z,rE(e.id),u?Kw(t):0),this.emit(`entered`,e.id)}for(let e of c)Jw(this._ripples,e.x,e.z,WT,u?YT:0),this.emit(`exited`,e.id);for(let t of Qw(this.bobTimers,l,e,GT)){let e=tE(t,this._scratchB);Jw(this._ripples,e.x,e.z,KT)}for(let e of l){let t=tE(e,this._scratchB),n=this.wadeAnchors.get(e);if(!n){this.wadeAnchors.set(e,{x:t.x,z:t.z});continue}let r=t.x-n.x,i=t.z-n.z;if(r*r+i*i>=qT*qT){let r=nE(e),i=Math.hypot(r?.[0]??0,r?.[2]??0),a=u?Math.min(XT+ZT*i,QT):0;Jw(this._ripples,t.x,t.z,JT,a),n.x=t.x,n.z=t.z}}for(let e of this.wadeAnchors.keys())l.has(e)||this.wadeAnchors.delete(e)}waveOffsetAt(e,t,n){return this.quality===`simple`?wT(e-n.x,t-n.z,this.time,this.waveHeight):CT(e,t,this.time,{amplitude:this.waveHeight*MT,frequency:dT.frequency,persistence:dT.persistence,lacunarity:dT.lacunarity,iterations:dT.iterations,speed:dT.speed,scaleNorm:jT})}};function eE(e,t){(e instanceof ny||e instanceof ty)&&t.push(e);for(let n of e.children)eE(n,t)}function tE(e,t={x:0,y:0,z:0}){let n=0,r=0,i=0,a=e;for(;a;)a instanceof Zv&&(n+=a.position[0]??0,r+=a.position[1]??0,i+=a.position[2]??0),a=a.parent;return t.x=n,t.y=r,t.z=i,t}function nE(e){return e.velocity??e.linearVelocity}function rE(e){let t=Math.abs(nE(e)?.[1]??0);return Math.min(VT+HT*t,UT)}function iE(e,t,n){let r=new Os(e,t,n,n);return r.rotateX(-Math.PI/2),r}var aE=new K(`#000000`),oE=new K(`#ffffff`),sE=new K(`#cfe3f0`),cE={trough:new K,surface:new K,peak:new K},lE=new B,uE=new H,dE=3,fE=new Go(new H(0,-1,0),0),pE=[fE],mE=new Go(new H(0,1,0),0),hE=[mE],gE=new Hs;gE.colorWrite=!1;function _E(){Ke(),M(Zv),M(oC),M(XS),M(nb),M(rb),M(wb),M(Lw),M(Fy),M($T),M(PS),M(xx),M(_w),M(rC),M(ab),M(qS),M(JS),M($v),M(ty),M(ey),M(ny)}var vE=`
|
|
6874
|
+
`;function sT(e,t,n,r,i=0){return t>=r.centerY?!1:Math.abs(e-r.centerX)<=r.halfW+i&&Math.abs(n-r.centerZ)<=r.halfD+i}var cT=22,lT={enabled:!0,color:`#cdeeff`,intensity:.55,scale:.32,speed:1};function uT(e,t){if(e===!1)return{enabled:!1,color:fT(t),visibility:cT,caustics:lT};let n=e&&typeof e==`object`?e:{};return{enabled:!0,color:n.color??fT(t),visibility:n.visibility??cT,caustics:dT(n.caustics)}}function dT(e){if(e===!1)return{...lT,enabled:!1};let t=e&&typeof e==`object`?e:{};return{enabled:!0,color:typeof t.color==`string`?t.color:lT.color,intensity:typeof t.intensity==`number`?t.intensity:lT.intensity,scale:typeof t.scale==`number`?t.scale:lT.scale,speed:typeof t.speed==`number`?t.speed:lT.speed}}function fT(e){let{r:t,g:n,b:r}=pT(e),i=(e,t,n)=>Math.round(e*(1-n)+t*n);return mT(Math.round(i(t,6,.45)*.7),Math.round(i(n,40,.4)*.78),Math.round(i(r,70,.35)*.82))}function pT(e){let t=e.replace(`#`,``),n=t.length===3?t.split(``).map(e=>e+e).join(``):t;return{r:Number.parseInt(n.slice(0,2),16)||0,g:Number.parseInt(n.slice(2,4),16)||0,b:Number.parseInt(n.slice(4,6),16)||0}}function mT(e,t,n){let r=e=>Math.max(0,Math.min(255,e)).toString(16).padStart(2,`0`);return`#${r(e)}${r(t)}${r(n)}`}var hT={frequency:6.7,persistence:.3,lacunarity:2.18,iterations:9,speed:.5},gT=.525,_T=.08,vT={freqX:.7,freqZ:.5,cross:.6};function yT(e,t){return e-Math.floor(e/t)*t}function bT(e){return e-Math.floor(e)}function xT(e){return yT((e*34+1)*e,289)}var ST=.211324865405187,CT=.366025403784439,wT=-.577350269189626,TT=.024390243902439;function ET(e,t){let n=Math.floor(e+(e+t)*CT),r=Math.floor(t+(e+t)*CT),i=e-n+(n+r)*ST,a=t-r+(n+r)*ST,o=+(i>a),s=i>a?0:1,c=i+ST-o,l=a+ST-s,u=i+wT,d=a+wT;n=yT(n,289),r=yT(r,289);let f=xT(xT(r)+n),p=xT(xT(r+s)+n+o),m=xT(xT(r+1)+n+1),h=Math.max(.5-(i*i+a*a),0),g=Math.max(.5-(c*c+l*l),0),_=Math.max(.5-(u*u+d*d),0);h*=h,h*=h,g*=g,g*=g,_*=_,_*=_;let v=2*bT(f*TT)-1,y=2*bT(p*TT)-1,b=2*bT(m*TT)-1,x=Math.abs(v)-.5,S=Math.abs(y)-.5,C=Math.abs(b)-.5,w=v-Math.floor(v+.5),T=y-Math.floor(y+.5),E=b-Math.floor(b+.5);h*=1.79284291400159-.85373472095314*(w*w+x*x),g*=1.79284291400159-.85373472095314*(T*T+S*S),_*=1.79284291400159-.85373472095314*(E*E+C*C);let D=w*i+x*a,O=T*c+S*l,k=E*u+C*d;return 130*(h*D+g*O+_*k)}function DT(e,t,n,r){let i=Math.max(r.scaleNorm,1e-6),a=e/i,o=t/i,s=0,c=1,l=r.frequency,u=n*r.speed;for(let e=0;e<r.iterations;e++)s+=c*ET(a*l+u,o*l+u),c*=r.persistence,l*=r.lacunarity;return s*r.amplitude}function OT(e,t,n,r){return r*(Math.sin(e*vT.freqX+n)+vT.cross*Math.sin(t*vT.freqZ+n*1.3))}var kT=[`fancy`,`simple`],AT=[`trough`,`surface`,`peak`],jT={trough:`#135a72`,surface:`#2a86a0`,peak:`#a5ddd6`},MT={peakThreshold:.08,peakTransition:.05,troughThreshold:-.01,troughTransition:.15,fresnelScale:1,fresnelPower:5},NT={color:`#dcebe8`,intensity:.32,rangeMin:.45,rangeMax:1.4,scale:.35,edgeColor:`#ffffff`,edgeIntensity:.6,edgeWidth:.5};function PT(e,t){return Math.min(NT.rangeMax,Math.max(NT.rangeMin,Math.min(e,t)*.012))}var FT=128,IT=gT/_T,LT=256,RT=1024,zT=.02,BT=4,VT=32,HT=256,UT=32,WT=.05,GT=.08,KT=.02,qT=.18,JT=.04,YT=1.4,XT=.04,ZT=.45,QT=.08,$T=.4,eE=.22,tE=.09,nE=.85,rE=class e extends Zv{static typeName=`Water3D`;static signals=[`entered`,`exited`];static props={size:{default:[40,40]},color:{default:`#2a6fbe`},opacity:{default:.8},renderOrder:{default:1},waveHeight:{default:.04},waveSpeed:{default:1},quality:{default:`fancy`,options:kT},colors:{default:{}},reflection:{default:!0},reflectionInterval:{default:1e3},foam:{default:!0},interaction:{default:!0},splash:{default:!0},sunDirection:{default:[.5,.8,.3]},sunColor:{default:`#fff5d6`},sunIntensity:{default:1},detailStrength:{default:.26},absorption:{default:.15},refraction:{default:!0},underwater:{default:!0}};size=[40,40];color=`#2a6fbe`;opacity=.8;renderOrder=1;waveHeight=.04;waveSpeed=1;quality=`fancy`;colors={};reflection=!0;reflectionInterval=1e3;foam=!0;interaction=!0;splash=!0;sunDirection=[.5,.8,.3];sunColor=`#fff5d6`;sunIntensity=1;detailStrength=.26;absorption=.15;refraction=!0;underwater=!0;static validateJson(e){let t=e;if(!((t.size[0]??0)>0)||!((t.size[1]??0)>0))throw new y(`BAD_FORMAT`,`Water3D '${e.name}' size must be positive [width, depth] meters, got ${JSON.stringify(t.size)}.`,{prop:`size`});if(!kT.includes(t.quality))throw new y(`BAD_FORMAT`,`Water3D '${e.name}' quality must be one of [${kT.join(`, `)}], got '${t.quality}'.`,{prop:`quality`,validOptions:kT});for(let[n,r]of Object.entries(t.colors)){if(!AT.includes(n))throw new y(`BAD_FORMAT`,`Water3D '${e.name}' colors has unknown key '${n}'. Valid keys: [${AT.join(`, `)}].`,{prop:`colors`,validOptions:AT});if(typeof r!=`string`)throw new y(`BAD_FORMAT`,`Water3D '${e.name}' colors.${n} must be a hex color string, got ${JSON.stringify(r)}.`,{prop:`colors`})}if(!(t.reflectionInterval>0))throw new y(`BAD_FORMAT`,`Water3D '${e.name}' reflectionInterval must be > 0 ms, got ${t.reflectionInterval}.`,{prop:`reflectionInterval`});let n=t.sunDirection;if(!Array.isArray(n)||n.length!==3||!n.every(e=>Number.isFinite(e))||Math.hypot(n[0]??0,n[1]??0,n[2]??0)===0)throw new y(`BAD_FORMAT`,`Water3D '${e.name}' sunDirection must be a non-zero [x, y, z] vector, got ${JSON.stringify(n)}.`,{prop:`sunDirection`});if(!(t.sunIntensity>=0))throw new y(`BAD_FORMAT`,`Water3D '${e.name}' sunIntensity must be >= 0, got ${t.sunIntensity}.`,{prop:`sunIntensity`});if(!(t.detailStrength>=0))throw new y(`BAD_FORMAT`,`Water3D '${e.name}' detailStrength must be >= 0, got ${t.detailStrength}.`,{prop:`detailStrength`});if(!(t.absorption>=0))throw new y(`BAD_FORMAT`,`Water3D '${e.name}' absorption must be >= 0 (per-meter Beer's-law constant), got ${t.absorption}.`,{prop:`absorption`});let r=t.underwater;if(typeof r!=`boolean`){if(typeof r!=`object`||!r||Array.isArray(r))throw new y(`BAD_FORMAT`,`Water3D '${e.name}' underwater must be true, false or an object ({ color?, visibility? }), got ${JSON.stringify(r)}.`,{prop:`underwater`});let t=r;if(t.color!==void 0&&typeof t.color!=`string`)throw new y(`BAD_FORMAT`,`Water3D '${e.name}' underwater.color must be a hex color string, got ${JSON.stringify(t.color)}.`,{prop:`underwater`});if(t.visibility!==void 0&&!(t.visibility>0))throw new y(`BAD_FORMAT`,`Water3D '${e.name}' underwater.visibility must be > 0 meters, got ${JSON.stringify(t.visibility)}.`,{prop:`underwater`})}}time=0;surfaceKey=``;_ripples=[];inWater=new Set;bobTimers=new Map;wadeAnchors=new Map;_scratchA={x:0,y:0,z:0};_scratchB={x:0,y:0,z:0};_bodies=[];cubeTarget=null;cubeCamera=null;reflectionNear=0;lastReflectionAt=0;scenePassTarget=null;_createObject3D(){return new ho}update(e){this.time+=e*this.waveSpeed,$w(this._ripples,e),this.interaction&&this.stepInteraction(e)}_syncObject3D(){super._syncObject3D();let e=this._ensureObject3D();this.rebuildSurfaceIfNeeded(e),this.quality===`simple`?this.syncSimple(e):this.syncFancy(e)}_onRender3D(e){let t=this._ensureObject3D(),n=t.material;if(e.camera.updateWorldMatrix(!0,!1),e.camera.getWorldPosition(mE),this.underwaterAt(mE.x,mE.y,mE.z)||this.quality!==`fancy`||!n.isShaderMaterial)return;let r=Math.hypot((this.size[0]??1)/2,(this.size[1]??1)/2);if(!Jw(e.camera,aE(this),r+1))return;let i=n.uniforms,a=Date.now();if(this.reflection){this.cubeTarget||=new Xl(LT,{generateMipmaps:!0,minFilter:Ot,type:It});let n=Math.max((this.size[0]??1)/2,(this.size[1]??1)/2);if((!this.cubeCamera||this.reflectionNear!==n)&&(this.reflectionNear=n,this.cubeCamera=new qc(n,1e3,this.cubeTarget),this.lastReflectionAt=0),this.lastReflectionAt===0||a-this.lastReflectionAt>=this.reflectionInterval){this.lastReflectionAt=a;let n=aE(this),r=this.cubeCamera;r.position.set(n.x,n.y+.1,n.z);let i=t.visible;t.visible=!1,r.update(e.gl,e.scene),t.visible=i}i.uEnvironmentMap&&i.uUseEnvironmentMap&&(i.uEnvironmentMap.value=this.cubeTarget.texture,i.uUseEnvironmentMap.value=1)}e.gl.getDrawingBufferSize(pE);let o=Math.max(1,Math.min(Math.round(pE.x/2),RT)),s=Math.max(1,Math.min(Math.round(pE.y/2),RT));if(this.scenePassTarget&&(this.scenePassTarget.width!==o||this.scenePassTarget.height!==s)&&(this.scenePassTarget.depthTexture?.dispose(),this.scenePassTarget.dispose(),this.scenePassTarget=null),!this.scenePassTarget){let e=new ys(o,s);e.type=Ft,this.scenePassTarget=new ai(o,s,{depthTexture:e,depthBuffer:!0,stencilBuffer:!1,generateMipmaps:!0,minFilter:Ot})}let c=e.gl.getRenderTarget(),l=e.gl.autoClear,u=e.gl.clippingPlanes,d=t.visible;t.visible=!1;let f=aE(this).y;gE.constant=f+zT,e.gl.clippingPlanes=_E,e.gl.setRenderTarget(this.scenePassTarget),e.gl.autoClear=!0,e.gl.clear(!0,!0,!1),e.gl.render(e.scene,e.camera),vE.constant=-(f-zT),e.gl.clippingPlanes=yE;let p=e.scene.overrideMaterial,m=e.scene.background;e.scene.overrideMaterial=bE,e.scene.background=null,e.gl.autoClear=!1,e.gl.render(e.scene,e.camera),e.scene.overrideMaterial=p,e.scene.background=m,e.gl.setRenderTarget(c),e.gl.autoClear=l,e.gl.clippingPlanes=u,t.visible=d,i.uDepthTexture&&i.uSceneColor&&i.uCameraNear&&i.uCameraFar&&i.uUseSceneDepth&&(i.uDepthTexture.value=this.scenePassTarget.depthTexture,i.uSceneColor.value=this.scenePassTarget.texture,i.uCameraNear.value=e.camera.near,i.uCameraFar.value=e.camera.far,i.uUseSceneDepth.value=1,i.uUseRefraction&&(i.uUseRefraction.value=+!!this.refraction),i.uEnableFoam&&(i.uEnableFoam.value=+!!this.foam))}underwaterAt(e,t,n){if(this.underwater===!1)return null;let r=aE(this,this._scratchA);if(!sT(e,t,n,{centerX:r.x,centerY:r.y,centerZ:r.z,halfW:(this.size[0]??1)/2,halfD:(this.size[1]??1)/2},hE))return null;let i=uT(this.underwater,this.color);return i.enabled?{...i,surfaceY:r.y}:null}free(){let e=this._ensureObject3D();e.geometry?.dispose(),e.material?.dispose?.(),this.cubeTarget?.dispose(),this.cubeTarget=null,this.cubeCamera=null,this.scenePassTarget?.depthTexture?.dispose(),this.scenePassTarget?.dispose(),this.scenePassTarget=null,super.free()}rebuildSurfaceIfNeeded(e){let t=JSON.stringify([this.size,this.quality]);if(t===this.surfaceKey&&e.geometry.getAttribute(`position`))return;this.surfaceKey=t,e.geometry?.dispose(),e.material?.dispose?.();let n=this.size[0]??1,r=this.size[1]??1;this.quality===`simple`?(e.geometry=cE(n,r,UT),e.material=this.buildLakeMaterial()):(e.geometry=cE(n,r,Math.min(Math.max(Math.round(Math.max(n,r)*BT),VT),HT)),e.material=this.buildFancyMaterial())}buildFancyMaterial(){return new Rs({vertexShader:aT,fragmentShader:oT,fog:!0,uniforms:{fogColor:{value:new K(`#ffffff`)},fogNear:{value:1},fogFar:{value:1e3},fogDensity:{value:25e-5},uTime:{value:0},uOpacity:{value:this.opacity},uEnvironmentMap:{value:null},uUseEnvironmentMap:{value:0},uWavesAmplitude:{value:this.waveHeight*IT},uWavesSpeed:{value:hT.speed},uWavesFrequency:{value:hT.frequency},uWavesPersistence:{value:hT.persistence},uWavesLacunarity:{value:hT.lacunarity},uWavesIterations:{value:hT.iterations},uTroughColor:{value:new K(jT.trough)},uSurfaceColor:{value:new K(jT.surface)},uPeakColor:{value:new K(jT.peak)},uPeakThreshold:{value:MT.peakThreshold},uPeakTransition:{value:MT.peakTransition},uTroughThreshold:{value:MT.troughThreshold},uTroughTransition:{value:MT.troughTransition},uFresnelScale:{value:MT.fresnelScale},uFresnelPower:{value:MT.fresnelPower},uScale:{value:new H(1,1,1)},uWaterCenter:{value:new H},uRipples:{value:Array.from({length:8},()=>new ri)},uRippleFoam:{value:Array.from({length:8},()=>0)},uSplashFoam:{value:+!!this.splash},uSunDirection:{value:new H(.5,.8,.3)},uSunColor:{value:new K(this.sunColor)},uSunIntensity:{value:this.sunIntensity},uDetailStrength:{value:this.detailStrength},uAbsorption:{value:this.absorption},uUseSceneDepth:{value:0},uUseRefraction:{value:0},uSceneColor:{value:null},uEnableFoam:{value:0},uFoamColor:{value:new K(NT.color)},uFoamIntensity:{value:NT.intensity},uFoamRange:{value:PT(this.size[0]??1,this.size[1]??1)},uFoamScale:{value:NT.scale},uDepthTexture:{value:null},uCameraNear:{value:.1},uCameraFar:{value:1e3},uEdgeColor:{value:new K(NT.edgeColor)},uEdgeIntensity:{value:NT.edgeIntensity},uEdgeWidth:{value:NT.edgeWidth}},transparent:!0,depthTest:!0,depthWrite:!0,side:2})}buildLakeMaterial(){return new Rs({vertexShader:rT,fragmentShader:iT,fog:!0,uniforms:{fogColor:{value:new K(`#ffffff`)},fogNear:{value:1},fogFar:{value:1e3},fogDensity:{value:25e-5},uTime:{value:0},uOpacity:{value:this.opacity},uColor:{value:new K(this.color)},uSurfaceColor:{value:new K(this.color)},uSkyColor:{value:new K(this.color)},uSunDirection:{value:new H(.5,.8,.3).normalize()},uSunColor:{value:new K(this.sunColor)},uSunIntensity:{value:this.sunIntensity},uDetailStrength:{value:this.detailStrength},uFresnelPower:{value:MT.fresnelPower},uScale:{value:new H(1,1,1)},uWaterCenter:{value:new H},uRipples:{value:Array.from({length:8},()=>new ri)},uRippleFoam:{value:Array.from({length:8},()=>0)},uSplashFoam:{value:+!!this.splash},uFoamColor:{value:new K(NT.color)}},transparent:!0,depthTest:!0,depthWrite:!1,side:2})}syncFancy(e){let t=e.material.uniforms,n=this.size[0]??1,r=this.size[1]??1,i=aE(this);t.uTime&&(t.uTime.value=this.time),t.uOpacity&&(t.uOpacity.value=this.opacity),t.uWavesAmplitude&&(t.uWavesAmplitude.value=this.waveHeight*IT),(t.uScale?.value)?.set(n/2,r/2,r/2),(t.uWaterCenter?.value)?.set(i.x,i.y,i.z);let a=this.resolvePalette();(t.uTroughColor?.value)?.copy(a.trough),(t.uSurfaceColor?.value)?.copy(a.surface),(t.uPeakColor?.value)?.copy(a.peak),(t.uSunDirection?.value)?.set(this.sunDirection[0]??0,this.sunDirection[1]??1,this.sunDirection[2]??0).normalize(),(t.uSunColor?.value)?.set(this.sunColor),t.uSunIntensity&&(t.uSunIntensity.value=this.sunIntensity),t.uDetailStrength&&(t.uDetailStrength.value=this.detailStrength),t.uAbsorption&&(t.uAbsorption.value=this.absorption),!this.reflection&&t.uUseEnvironmentMap&&(t.uUseEnvironmentMap.value=0),!this.foam&&t.uEnableFoam&&(t.uEnableFoam.value=0),!this.refraction&&t.uUseRefraction&&(t.uUseRefraction.value=0),t.uSplashFoam&&(t.uSplashFoam.value=+!!this.splash);let o=t.uRipples?.value,s=t.uRippleFoam?.value;if(o)for(let e=0;e<o.length;e++){let t=this._ripples[e];t?o[e].set(t.x,t.z,t.age,t.amp):o[e].set(0,0,0,0),s&&(s[e]=t?t.foam??0:0)}}_applySunDirection(t){let n=e.props.sunDirection?.default;this.sunDirection.every((e,t)=>e===n[t])&&(this._ensureObject3D().material.uniforms?.uSunDirection?.value)?.set(t[0],t[1],t[2])}resolvePalette(){let t=this.colors,n=this.color===e.props.color?.default?null:this.color,r=t.surface??n??jT.surface;return fE.surface.set(r),t.trough?fE.trough.set(t.trough):n?fE.trough.set(n).lerp(lE,.45):fE.trough.set(jT.trough),t.peak?fE.peak.set(t.peak):n?fE.peak.set(n).lerp(uE,.3):fE.peak.set(jT.peak),fE}syncSimple(e){let t=e.material;t.opacity=this.opacity;let n=t.uniforms,r=this.size[0]??1,i=this.size[1]??1,a=aE(this,this._scratchA);n.uTime&&(n.uTime.value=this.time),n.uOpacity&&(n.uOpacity.value=this.opacity),(n.uSurfaceColor?.value)?.set(this.color).lerp(uE,.12),(n.uColor?.value)?.set(this.color).lerp(lE,.4),(n.uSkyColor?.value)?.set(this.color).lerp(dE,.82),(n.uScale?.value)?.set(r/2,i/2,i/2),(n.uWaterCenter?.value)?.set(a.x,a.y,a.z),(n.uSunDirection?.value)?.set(this.sunDirection[0]??0,this.sunDirection[1]??1,this.sunDirection[2]??0).normalize(),(n.uSunColor?.value)?.set(this.sunColor),n.uSunIntensity&&(n.uSunIntensity.value=this.sunIntensity),n.uDetailStrength&&(n.uDetailStrength.value=this.detailStrength),n.uSplashFoam&&(n.uSplashFoam.value=+!!this.splash);let o=n.uRipples?.value,s=n.uRippleFoam?.value;if(o)for(let e=0;e<o.length;e++){let t=this._ripples[e];t?o[e].set(t.x,t.z,t.age,t.amp):o[e].set(0,0,0,0),s&&(s[e]=t?t.foam??0:0)}let c=e.geometry,l=c.getAttribute(`position`),u=c.getAttribute(`normal`),d=this.time,f=this.waveHeight,p=a.x,m=a.z,h=this._ripples,g=.5;for(let e=0;e<l.count;e++){let t=l.getX(e),n=l.getZ(e),r=OT(t,n,d,f)+tT(t+p,n+m,h);l.setY(e,r);let i=OT(t+g,n,d,f)+tT(t+p+g,n+m,h),a=OT(t,n+g,d,f)+tT(t+p,n+m+g,h),o=(r-i)/g,s=(r-a)/g,c=1/Math.hypot(o,1,s);u.setXYZ(e,o*c,c,s*c)}l.needsUpdate=!0,u.needsUpdate=!0}stepInteraction(e){let t=this;for(;t.parent;)t=t.parent;let n=this._bodies;n.length=0,iE(t,n);let r=aE(this,this._scratchA),i=(this.size[0]??0)/2,a=(this.size[1]??0)/2,o=[];for(let e of n){let t=aE(e,this._scratchB);if(Math.abs(t.x-r.x)>i||Math.abs(t.z-r.z)>a)continue;let n=Yw(e.collider);o.push({id:e,x:t.x,y:t.y,z:t.z,waterline:r.y+this.waveOffsetAt(t.x,t.z,r)+n})}let{entered:s,exited:c,inWater:l}=Zw(this.inWater,o,WT);this.inWater=l;let u=this.splash;for(let e of s){let t=Math.abs(oE(e.id)?.[1]??0);Qw(this._ripples,e.x,e.z,sE(e.id),u?Xw(t):0),this.emit(`entered`,e.id)}for(let e of c)Qw(this._ripples,e.x,e.z,JT,u?$T:0),this.emit(`exited`,e.id);for(let t of nT(this.bobTimers,l,e,YT)){let e=aE(t,this._scratchB);Qw(this._ripples,e.x,e.z,XT)}for(let e of l){let t=aE(e,this._scratchB),n=this.wadeAnchors.get(e);if(!n){this.wadeAnchors.set(e,{x:t.x,z:t.z});continue}let r=t.x-n.x,i=t.z-n.z;if(r*r+i*i>=ZT*ZT){let r=oE(e),i=Math.hypot(r?.[0]??0,r?.[2]??0),a=u?Math.min(eE+tE*i,nE):0;Qw(this._ripples,t.x,t.z,QT,a),n.x=t.x,n.z=t.z}}for(let e of this.wadeAnchors.keys())l.has(e)||this.wadeAnchors.delete(e)}waveOffsetAt(e,t,n){return this.quality===`simple`?OT(e-n.x,t-n.z,this.time,this.waveHeight):DT(e,t,this.time,{amplitude:this.waveHeight*IT,frequency:hT.frequency,persistence:hT.persistence,lacunarity:hT.lacunarity,iterations:hT.iterations,speed:hT.speed,scaleNorm:FT})}};function iE(e,t){(e instanceof ny||e instanceof ty)&&t.push(e);for(let n of e.children)iE(n,t)}function aE(e,t={x:0,y:0,z:0}){let n=0,r=0,i=0,a=e;for(;a;)a instanceof Zv&&(n+=a.position[0]??0,r+=a.position[1]??0,i+=a.position[2]??0),a=a.parent;return t.x=n,t.y=r,t.z=i,t}function oE(e){return e.velocity??e.linearVelocity}function sE(e){let t=Math.abs(oE(e)?.[1]??0);return Math.min(GT+KT*t,qT)}function cE(e,t,n){let r=new Os(e,t,n,n);return r.rotateX(-Math.PI/2),r}var lE=new K(`#000000`),uE=new K(`#ffffff`),dE=new K(`#cfe3f0`),fE={trough:new K,surface:new K,peak:new K},pE=new B,mE=new H,hE=3,gE=new Go(new H(0,-1,0),0),_E=[gE],vE=new Go(new H(0,1,0),0),yE=[vE],bE=new Hs;bE.colorWrite=!1;function xE(){Ke(),M(Zv),M(uC),M(eC),M(ob),M(sb),M(Ob),M(Vw),M(Fy),M(rE),M(RS),M(Tx),M(xw),M(sC),M(lb),M(ZS),M(QS),M($v),M(ty),M(ey),M(ny)}var SE=`
|
|
6875
6875
|
varying vec2 vUv;
|
|
6876
6876
|
void main() {
|
|
6877
6877
|
vUv = uv;
|
|
6878
6878
|
gl_Position = vec4(position.xy, 0.0, 1.0);
|
|
6879
6879
|
}
|
|
6880
|
-
`,
|
|
6880
|
+
`,CE=`
|
|
6881
6881
|
precision highp float;
|
|
6882
6882
|
varying vec2 vUv;
|
|
6883
6883
|
uniform sampler2D tDepth;
|
|
@@ -7039,7 +7039,7 @@ void main() {
|
|
|
7039
7039
|
// a separate full-res pass (this raymarch runs at HALF res for performance)
|
|
7040
7040
|
gl_FragColor = vec4(accum, alpha);
|
|
7041
7041
|
}
|
|
7042
|
-
`,
|
|
7042
|
+
`,wE=`
|
|
7043
7043
|
precision highp float;
|
|
7044
7044
|
varying vec2 vUv;
|
|
7045
7045
|
uniform sampler2D tTex;
|
|
@@ -7057,7 +7057,7 @@ void main() {
|
|
|
7057
7057
|
c += texture2D(tTex, vUv - uDir * 4.0) * 0.015;
|
|
7058
7058
|
gl_FragColor = c;
|
|
7059
7059
|
}
|
|
7060
|
-
`,
|
|
7060
|
+
`,TE=`
|
|
7061
7061
|
precision highp float;
|
|
7062
7062
|
varying vec2 vUv;
|
|
7063
7063
|
uniform sampler2D tScene;
|
|
@@ -7067,11 +7067,11 @@ void main() {
|
|
|
7067
7067
|
vec4 cloud = texture2D(tClouds, vUv); // already separable-blurred + premultiplied
|
|
7068
7068
|
gl_FragColor = vec4(scene.rgb * (1.0 - cloud.a) + cloud.rgb, 1.0);
|
|
7069
7069
|
}
|
|
7070
|
-
`;function
|
|
7070
|
+
`;function EE(){let e=new Rs({vertexShader:SE,fragmentShader:wE,depthTest:!1,depthWrite:!1,uniforms:{tTex:{value:null},uDir:{value:new B}}}),t=new ho(new Os(2,2),e);return t.frustumCulled=!1,{mesh:t,uniforms:e.uniforms}}function DE(){let e=new Rs({vertexShader:SE,fragmentShader:CE,depthTest:!1,depthWrite:!1,uniforms:{tDepth:{value:null},uInvViewProj:{value:new G},uCameraPos:{value:new H},uTime:{value:0},uSunDir:{value:new H(0,1,0)},uSunColor:{value:new K(`#fff3da`)},uCloudColor:{value:new K(`#ffffff`)},uShadeColor:{value:new K(`#9fb0c8`)},uHorizonColor:{value:new K(`#cdd9e6`)},uBase:{value:120},uTop:{value:320},uCoverage:{value:.5},uDensity:{value:1},uScale:{value:240},uWind:{value:new B(1,.3)},uFarFade:{value:6e3}}}),t=new ho(new Os(2,2),e);return t.frustumCulled=!1,{mesh:t,uniforms:e.uniforms}}function OE(){let e=new Rs({vertexShader:SE,fragmentShader:TE,depthTest:!1,depthWrite:!1,uniforms:{tScene:{value:null},tClouds:{value:null}}}),t=new ho(new Os(2,2),e);return t.frustumCulled=!1,{mesh:t,uniforms:e.uniforms}}var kE=class extends yc{constructor(e){super(e),this.type=It}parse(e){let t=function(e,t){switch(e){case 1:throw Error(`THREE.HDRLoader: Read Error: `+(t||``));case 2:throw Error(`THREE.HDRLoader: Write Error: `+(t||``));case 3:throw Error(`THREE.HDRLoader: Bad File Format: `+(t||``));default:case 4:throw Error(`THREE.HDRLoader: Memory Error: `+(t||``))}},n=function(e,t,n){t||=1024;let r=e.pos,i=-1,a=0,o=``,s=String.fromCharCode.apply(null,new Uint16Array(e.subarray(r,r+128)));for(;0>(i=s.indexOf(`
|
|
7071
7071
|
`))&&a<t&&r<e.byteLength;)o+=s,a+=s.length,r+=128,s=String.fromCharCode.apply(null,new Uint16Array(e.subarray(r,r+128)));return-1<i?(!1!==n&&(e.pos+=a+i+1),o+s.slice(0,i)):!1},r=function(e){let r=/^#\?(\S+)/,i=/^\s*GAMMA\s*=\s*(\d+(\.\d+)?)\s*$/,a=/^\s*EXPOSURE\s*=\s*(\d+(\.\d+)?)\s*$/,o=/^\s*FORMAT=(\S+)\s*$/,s=/^\s*\-Y\s+(\d+)\s+\+X\s+(\d+)\s*$/,c={valid:0,string:``,comments:``,programtype:`RGBE`,format:``,gamma:1,exposure:1,width:0,height:0},l,u;for((e.pos>=e.byteLength||!(l=n(e)))&&t(1,`no header found`),(u=l.match(r))||t(3,`bad initial token`),c.valid|=1,c.programtype=u[1],c.string+=l+`
|
|
7072
7072
|
`;l=n(e),!1!==l;){if(c.string+=l+`
|
|
7073
7073
|
`,l.charAt(0)===`#`){c.comments+=l+`
|
|
7074
|
-
`;continue}if((u=l.match(i))&&(c.gamma=parseFloat(u[1])),(u=l.match(a))&&(c.exposure=parseFloat(u[1])),(u=l.match(o))&&(c.valid|=2,c.format=u[1]),(u=l.match(s))&&(c.valid|=4,c.height=parseInt(u[1],10),c.width=parseInt(u[2],10)),c.valid&2&&c.valid&4)break}return c.valid&2||t(3,`missing format specifier`),c.valid&4||t(3,`missing image size specifier`),c},i=function(e,n,r){let i=n;if(i<8||i>32767||e[0]!==2||e[1]!==2||e[2]&128)return new Uint8Array(e);i!==(e[2]<<8|e[3])&&t(3,`wrong scanline width`);let a=new Uint8Array(4*n*r);a.length||t(4,`unable to allocate buffer space`);let o=0,s=0,c=4*i,l=new Uint8Array(4),u=new Uint8Array(c),d=r;for(;d>0&&s<e.byteLength;){s+4>e.byteLength&&t(1),l[0]=e[s++],l[1]=e[s++],l[2]=e[s++],l[3]=e[s++],(l[0]!=2||l[1]!=2||(l[2]<<8|l[3])!=i)&&t(3,`bad rgbe scanline format`);let n=0,r;for(;n<c&&s<e.byteLength;){r=e[s++];let i=r>128;if(i&&(r-=128),(r===0||n+r>c)&&t(3,`bad scanline data`),i){let t=e[s++];for(let e=0;e<r;e++)u[n++]=t}else u.set(e.subarray(s,s+r),n),n+=r,s+=r}let f=i;for(let e=0;e<f;e++){let t=0;a[o]=u[e+t],t+=i,a[o+1]=u[e+t],t+=i,a[o+2]=u[e+t],t+=i,a[o+3]=u[e+t],o+=4}d--}return a},a=function(e,t,n,r){let i=2**(e[t+3]-128)/255;n[r+0]=e[t+0]*i,n[r+1]=e[t+1]*i,n[r+2]=e[t+2]*i,n[r+3]=1},o=function(e,t,n,r){let i=2**(e[t+3]-128)/255;n[r+0]=wa.toHalfFloat(Math.min(e[t+0]*i,65504)),n[r+1]=wa.toHalfFloat(Math.min(e[t+1]*i,65504)),n[r+2]=wa.toHalfFloat(Math.min(e[t+2]*i,65504)),n[r+3]=wa.toHalfFloat(1)},s=new Uint8Array(e);s.pos=0;let c=r(s),l=c.width,u=c.height,d=i(s.subarray(s.pos),l,u),f,p,m;switch(this.type){case Ft:m=d.length/4;let e=new Float32Array(m*4);for(let t=0;t<m;t++)a(d,t*4,e,t*4);f=e,p=Ft;break;case It:m=d.length/4;let t=new Uint16Array(m*4);for(let e=0;e<m;e++)o(d,e*4,t,e*4);f=t,p=It;break;default:throw Error(`THREE.HDRLoader: Unsupported type: `+this.type)}return{width:l,height:u,data:f,header:c.string,gamma:c.gamma,exposure:c.exposure,type:p}}setDataType(e){return this.type=e,this}load(e,t,n,r){function i(e,n){switch(e.type){case Ft:case It:e.colorSpace=Xn,e.minFilter=Et,e.magFilter=Et,e.generateMipmaps=!1,e.flipY=!0;break}t&&t(e,n)}return super.load(e,i,n,r)}},
|
|
7074
|
+
`;continue}if((u=l.match(i))&&(c.gamma=parseFloat(u[1])),(u=l.match(a))&&(c.exposure=parseFloat(u[1])),(u=l.match(o))&&(c.valid|=2,c.format=u[1]),(u=l.match(s))&&(c.valid|=4,c.height=parseInt(u[1],10),c.width=parseInt(u[2],10)),c.valid&2&&c.valid&4)break}return c.valid&2||t(3,`missing format specifier`),c.valid&4||t(3,`missing image size specifier`),c},i=function(e,n,r){let i=n;if(i<8||i>32767||e[0]!==2||e[1]!==2||e[2]&128)return new Uint8Array(e);i!==(e[2]<<8|e[3])&&t(3,`wrong scanline width`);let a=new Uint8Array(4*n*r);a.length||t(4,`unable to allocate buffer space`);let o=0,s=0,c=4*i,l=new Uint8Array(4),u=new Uint8Array(c),d=r;for(;d>0&&s<e.byteLength;){s+4>e.byteLength&&t(1),l[0]=e[s++],l[1]=e[s++],l[2]=e[s++],l[3]=e[s++],(l[0]!=2||l[1]!=2||(l[2]<<8|l[3])!=i)&&t(3,`bad rgbe scanline format`);let n=0,r;for(;n<c&&s<e.byteLength;){r=e[s++];let i=r>128;if(i&&(r-=128),(r===0||n+r>c)&&t(3,`bad scanline data`),i){let t=e[s++];for(let e=0;e<r;e++)u[n++]=t}else u.set(e.subarray(s,s+r),n),n+=r,s+=r}let f=i;for(let e=0;e<f;e++){let t=0;a[o]=u[e+t],t+=i,a[o+1]=u[e+t],t+=i,a[o+2]=u[e+t],t+=i,a[o+3]=u[e+t],o+=4}d--}return a},a=function(e,t,n,r){let i=2**(e[t+3]-128)/255;n[r+0]=e[t+0]*i,n[r+1]=e[t+1]*i,n[r+2]=e[t+2]*i,n[r+3]=1},o=function(e,t,n,r){let i=2**(e[t+3]-128)/255;n[r+0]=wa.toHalfFloat(Math.min(e[t+0]*i,65504)),n[r+1]=wa.toHalfFloat(Math.min(e[t+1]*i,65504)),n[r+2]=wa.toHalfFloat(Math.min(e[t+2]*i,65504)),n[r+3]=wa.toHalfFloat(1)},s=new Uint8Array(e);s.pos=0;let c=r(s),l=c.width,u=c.height,d=i(s.subarray(s.pos),l,u),f,p,m;switch(this.type){case Ft:m=d.length/4;let e=new Float32Array(m*4);for(let t=0;t<m;t++)a(d,t*4,e,t*4);f=e,p=Ft;break;case It:m=d.length/4;let t=new Uint16Array(m*4);for(let e=0;e<m;e++)o(d,e*4,t,e*4);f=t,p=It;break;default:throw Error(`THREE.HDRLoader: Unsupported type: `+this.type)}return{width:l,height:u,data:f,header:c.string,gamma:c.gamma,exposure:c.exposure,type:p}}setDataType(e){return this.type=e,this}load(e,t,n,r){function i(e,n){switch(e.type){case Ft:case It:e.colorSpace=Xn,e.minFilter=Et,e.magFilter=Et,e.generateMipmaps=!1,e.flipY=!0;break}t&&t(e,n)}return super.load(e,i,n,r)}},AE=class extends kE{constructor(e){console.warn(`RGBELoader has been deprecated. Please use HDRLoader instead.`),super(e)}},jE=class e extends ho{constructor(){let t=e.SkyShader,n=new Rs({name:t.name,uniforms:Fs.clone(t.uniforms),vertexShader:t.vertexShader,fragmentShader:t.fragmentShader,side:1,depthWrite:!1});super(new Ss(1,1,1),n),this.isSky=!0}};jE.SkyShader={name:`SkyShader`,uniforms:{turbidity:{value:2},rayleigh:{value:1},mieCoefficient:{value:.005},mieDirectionalG:{value:.8},sunPosition:{value:new H},up:{value:new H(0,1,0)},cloudScale:{value:2e-4},cloudSpeed:{value:1e-4},cloudCoverage:{value:.4},cloudDensity:{value:.4},cloudElevation:{value:.5},showSunDisc:{value:1},time:{value:0}},vertexShader:`
|
|
7075
7075
|
uniform vec3 sunPosition;
|
|
7076
7076
|
uniform float rayleigh;
|
|
7077
7077
|
uniform float turbidity;
|
|
@@ -7291,13 +7291,13 @@ void main() {
|
|
|
7291
7291
|
#include <tonemapping_fragment>
|
|
7292
7292
|
#include <colorspace_fragment>
|
|
7293
7293
|
|
|
7294
|
-
}`};var OE=[`type`,`sunPosition`,`elevationDeg`,`azimuthDeg`,`turbidity`,`rayleigh`],kE=[`color`,`near`,`far`],AE=[`coverage`,`density`,`base`,`top`,`color`,`shadeColor`,`speed`,`scale`],jE=[`mapSize`,`radius`],ME=[1024,2048],NE=2,PE=1,FE=50,IE=800,LE=`#cfd8e0`,RE=Math.PI/180;function zE(e){return{exposure:UE(e?.exposure),sky:WE(e?.sky),fog:GE(e?.fog,e?.sky!==void 0),clouds:KE(e?.clouds),shadows:qE(e?.shadows)}}function BE(e,t){let n=(90-e)*RE,r=t*RE;return[Math.sin(n)*Math.sin(r),Math.cos(n),Math.sin(n)*Math.cos(r)]}function VE(e){let[t,n,r]=e.sunPosition,i=Math.hypot(t,n,r)||1;return[t/i,n/i,r/i]}function HE(e){let t=[191,213,232],n=[233,228,217],r=[242,201,150],i=XE((e.turbidity-NE)/8),a=VE(e),o=XE((18-Math.asin(YE(a[1],-1,1))/RE)/18)*.8,s=e=>Math.round(ZE(ZE(t[e],n[e],i),r[e],o));return`#${[s(0),s(1),s(2)].map(e=>e.toString(16).padStart(2,`0`)).join(``)}`}function UE(e){if(e===void 0)return 1;if(typeof e!=`number`||!Number.isFinite(e)||e<=0)throw new y(`BAD_FORMAT`,`environment.exposure must be a finite number > 0 (tone-mapping exposure, default 1), got ${JSON.stringify(e)}.`,{prop:`exposure`});return e}function WE(e){if(e===void 0)return null;if(typeof e!=`object`||!e||Array.isArray(e))throw new y(`BAD_FORMAT`,`environment.sky must be an object ({ type?: "atmosphere", sunPosition? | elevationDeg?+azimuthDeg?, turbidity?, rayleigh? }), got ${JSON.stringify(e)}.`,{prop:`sky`});let t=e;for(let e of Object.keys(t))if(!OE.includes(e))throw new y(`BAD_FORMAT`,`environment.sky has unknown key '${e}'. Valid keys: [${OE.join(`, `)}].`,{prop:`sky`,validOptions:OE});if(t.type!==void 0&&t.type!==`atmosphere`)throw new y(`BAD_FORMAT`,`environment.sky.type must be 'atmosphere' (the only sky type so far), got ${JSON.stringify(t.type)}.`,{prop:`sky`,validOptions:[`atmosphere`]});let n=t.elevationDeg!==void 0||t.azimuthDeg!==void 0;if(t.sunPosition!==void 0&&n)throw new y(`BAD_FORMAT`,`environment.sky takes sunPosition OR elevationDeg/azimuthDeg, not both.`,{prop:`sky`,validOptions:[`sunPosition`,`elevationDeg+azimuthDeg`]});let r;if(t.sunPosition!==void 0){let e=t.sunPosition;if(!Array.isArray(e)||e.length!==3||!e.every(e=>typeof e==`number`&&Number.isFinite(e))||Math.hypot(e[0],e[1],e[2])===0)throw new y(`BAD_FORMAT`,`environment.sky.sunPosition must be a non-zero [x, y, z] vector, got ${JSON.stringify(e)}.`,{prop:`sky`});r=[e[0],e[1],e[2]]}else{let e=JE(t.elevationDeg,32,`sky.elevationDeg`),n=JE(t.azimuthDeg,135,`sky.azimuthDeg`);if(e<-90||e>90)throw new y(`BAD_FORMAT`,`environment.sky.elevationDeg must be in [-90, 90] (degrees above the horizon), got ${e}.`,{prop:`sky`});r=BE(e,n)}let i=JE(t.turbidity,NE,`sky.turbidity`);if(i<=0)throw new y(`BAD_FORMAT`,`environment.sky.turbidity must be > 0 (atmospheric haze; 2 ≈ clear day), got ${i}.`,{prop:`sky`});let a=JE(t.rayleigh,PE,`sky.rayleigh`);if(a<0)throw new y(`BAD_FORMAT`,`environment.sky.rayleigh must be >= 0 (Rayleigh scattering; 1 ≈ earth-like), got ${a}.`,{prop:`sky`});return{sunPosition:r,turbidity:i,rayleigh:a}}function GE(e,t){if(e===void 0)return null;if(typeof e!=`object`||!e||Array.isArray(e))throw new y(`BAD_FORMAT`,`environment.fog must be an object ({ color?, near?, far? }), got ${JSON.stringify(e)}.`,{prop:`fog`});let n=e;for(let e of Object.keys(n))if(!kE.includes(e))throw new y(`BAD_FORMAT`,`environment.fog has unknown key '${e}'. Valid keys: [${kE.join(`, `)}].`,{prop:`fog`,validOptions:kE});if(n.color!==void 0&&typeof n.color!=`string`)throw new y(`BAD_FORMAT`,`environment.fog.color must be a hex color string, got ${JSON.stringify(n.color)}.`,{prop:`fog`});let r=JE(n.near,FE,`fog.near`),i=JE(n.far,IE,`fog.far`);if(r<0)throw new y(`BAD_FORMAT`,`environment.fog.near must be >= 0 meters, got ${r}.`,{prop:`fog`});if(i<=r)throw new y(`BAD_FORMAT`,`environment.fog.far must be > near (got near ${r}, far ${i}).`,{prop:`fog`});return{color:n.color??(t?``:LE),near:r,far:i}}function KE(e){if(e===void 0)return null;if(typeof e!=`object`||!e||Array.isArray(e))throw new y(`BAD_FORMAT`,`environment.clouds must be an object ({ coverage?, density?, base?, top?, color?, shadeColor?, speed?, scale? }), got ${JSON.stringify(e)}.`,{prop:`clouds`});let t=e;for(let e of Object.keys(t))if(!AE.includes(e))throw new y(`BAD_FORMAT`,`environment.clouds has unknown key '${e}'. Valid keys: [${AE.join(`, `)}].`,{prop:`clouds`,validOptions:AE});for(let e of[`color`,`shadeColor`])if(t[e]!==void 0&&typeof t[e]!=`string`)throw new y(`BAD_FORMAT`,`environment.clouds.${e} must be a hex color string, got ${JSON.stringify(t[e])}.`,{prop:`clouds`});let n=JE(t.coverage,.5,`clouds.coverage`);if(n<0||n>1)throw new y(`BAD_FORMAT`,`environment.clouds.coverage must be in [0, 1] (how much sky is cloudy), got ${n}.`,{prop:`clouds`});let r=JE(t.density,1,`clouds.density`);if(r<0)throw new y(`BAD_FORMAT`,`environment.clouds.density must be >= 0 (optical thickness), got ${r}.`,{prop:`clouds`});let i=JE(t.base,120,`clouds.base`),a=JE(t.top,320,`clouds.top`);if(a<=i)throw new y(`BAD_FORMAT`,`environment.clouds.top must be > base (got base ${i}, top ${a}).`,{prop:`clouds`});let o=JE(t.speed,1,`clouds.speed`),s=JE(t.scale,240,`clouds.scale`);if(s<=0)throw new y(`BAD_FORMAT`,`environment.clouds.scale must be > 0 (feature size in world units), got ${s}.`,{prop:`clouds`});return{coverage:n,density:r,base:i,top:a,color:t.color??`#ffffff`,shadeColor:t.shadeColor??`#9fb0c8`,speed:o,scale:s}}function qE(e){if(e===void 0)return null;if(e===!1)return!1;if(e===!0)return{mapSize:2048,radius:1};if(typeof e!=`object`||!e||Array.isArray(e))throw new y(`BAD_FORMAT`,`environment.shadows must be true, false or an object ({ mapSize?, radius? }), got ${JSON.stringify(e)}.`,{prop:`shadows`});let t=e;for(let e of Object.keys(t))if(!jE.includes(e))throw new y(`BAD_FORMAT`,`environment.shadows has unknown key '${e}'. Valid keys: [${jE.join(`, `)}].`,{prop:`shadows`,validOptions:jE});let n=t.mapSize===void 0?2048:t.mapSize;if(!ME.includes(n))throw new y(`BAD_FORMAT`,`environment.shadows.mapSize must be one of [${ME.join(`, `)}], got ${JSON.stringify(t.mapSize)}.`,{prop:`shadows`,validOptions:ME.map(String)});let r=JE(t.radius,1,`shadows.radius`);if(r<0)throw new y(`BAD_FORMAT`,`environment.shadows.radius must be >= 0, got ${r}.`,{prop:`shadows`});return{mapSize:n,radius:r}}function JE(e,t,n){if(e===void 0)return t;if(typeof e!=`number`||!Number.isFinite(e))throw new y(`BAD_FORMAT`,`environment.${n} must be a finite number, got ${JSON.stringify(e)}.`,{prop:n});return e}function YE(e,t,n){return Math.min(Math.max(e,t),n)}function XE(e){return YE(e,0,1)}function ZE(e,t,n){return e+(t-e)*n}var QE=`https://agent8-games.verse8.io/assets/3D/default/textures/hdri`,$E={apartment:`lebombo_1k.hdr`,city:`potsdamer_platz_1k.hdr`,dawn:`kiara_1_dawn_1k.hdr`,forest:`forest_slope_1k.hdr`,lobby:`st_fagans_interior_1k.hdr`,night:`dikhololo_night_1k.hdr`,park:`rooitou_park_1k.hdr`,studio:`studio_small_03_1k.hdr`,sunset:`venice_sunset_1k.hdr`,warehouse:`empty_warehouse_01_1k.hdr`};function eD(e){if(typeof e.hdri==`string`&&e.hdri!==``)return e.hdri;if(typeof e.preset==`string`){let t=$E[e.preset];if(!t)throw Error(`Unknown environment preset '${e.preset}'. Available: ${Object.keys($E).join(`, `)}.`);return`${QE}/${t}`}return null}var tD=75,nD=class{scene;ambient=new Vc(`#ffffff`,0);envKey=null;config=zE(void 0);hdriUrl=null;hdriTexture=null;_sky=null;skyKey=``;skyEnvKey=``;skyEnvTarget=null;fog=new Ui(`#ffffff`,1,1e3);underwaterFog=null;underwaterBg=new K;constructor(e){this.scene=e,this.scene.add(this.ambient)}get sunDirection(){return this.config.sky?VE(this.config.sky):null}get clouds(){return this.config.clouds}get sceneFog(){return this.scene.fog instanceof Ui?this.scene.fog:null}apply(e,t){let n=e===void 0?``:JSON.stringify(e);n!==this.envKey&&(this.envKey=n,this.config=zE(e));let r=this.config,i=e?.ambient;if(this.ambient.color.set(i?.color??`#ffffff`),this.ambient.intensity=i?.intensity??0,t&&(t.toneMappingExposure=r.exposure,t.shadowMap.enabled=r.shadows!==!1),this.applyHdri(e),this.applySky(r.sky,t),this._sky&&(this._sky.visible=!0),this.applyFog(r),this.scene.environment=this.hdriTexture??this.skyEnvTarget?.texture??null,this.scene.environmentIntensity=(this.scene.environment===this.skyEnvTarget?.texture&&this.skyEnvTarget?.55:1)*rD(e?.iblIntensity),e?.skybox===!0&&this.hdriTexture)this.scene.background=this.hdriTexture;else{let t=e?.background;this.scene.background=typeof t==`string`?new K(t):null}}applySunLight(e,t=null){if(!e)return;let n=this.sunDirection;if(t){let r=e.position.length()||100,i,a,o;if(n)[i,a,o]=n;else{i=e.position.x-e.target.position.x,a=e.position.y-e.target.position.y,o=e.position.z-e.target.position.z;let t=Math.hypot(i,a,o)||1;i/=t,a/=t,o/=t}e.target.position.set(t.x,t.y,t.z),e.target.updateMatrixWorld(),e.position.set(t.x+i*r,t.y+a*r,t.z+o*r)}else if(n){let t=e.position.length()||100;e.position.set(n[0],n[1],n[2]).multiplyScalar(t)}let r=this.config.shadows;if(r&&typeof r==`object`){if(!e.castShadow){let t=e.shadow.camera;`left`in t&&t.left===-5&&(t.left=-75,t.right=tD,t.top=tD,t.bottom=-75,t.near=.5,t.far=500,t.updateProjectionMatrix()),e.castShadow=!0}e.shadow.mapSize.x!==r.mapSize&&(e.shadow.mapSize.set(r.mapSize,r.mapSize),e.shadow.map?.dispose(),e.shadow.map=null),e.shadow.radius=r.radius}}applyUnderwater(e){e&&(this.underwaterFog||=new Ui(`#000000`,.5,e.visibility),this.underwaterFog.color.set(e.color),this.underwaterFog.near=.5,this.underwaterFog.far=e.visibility,this.scene.fog=this.underwaterFog,this.scene.background=this.underwaterBg.set(e.color),this._sky&&(this._sky.visible=!1))}applyHdri(e){let t=eD(e??{});t!==this.hdriUrl&&(this.hdriUrl=t,this.hdriTexture?.dispose(),this.hdriTexture=null,t&&new EE().load(t,e=>{if(this.hdriUrl!==t){e.dispose();return}e.mapping=303,this.hdriTexture=e}))}applySky(e,t){let n=e?JSON.stringify(e):``;if(n!==this.skyKey&&(this.skyKey=n,this._sky&&=(this.scene.remove(this._sky),this._sky.material.dispose(),this._sky.geometry.dispose(),null),e)){this._sky=new DE,this._sky.scale.setScalar(45e4);let t=this._sky.material.uniforms;(t.sunPosition?.value).set(...e.sunPosition),t.turbidity&&(t.turbidity.value=e.turbidity),t.rayleigh&&(t.rayleigh.value=e.rayleigh),this.scene.add(this._sky)}if(e&&t&&this._sky&&this.skyEnvKey!==n){this.skyEnvKey=n,this.skyEnvTarget?.dispose();let e=new Vl(t),r=new Wi;r.add(this._sky),this.skyEnvTarget=e.fromScene(r),e.dispose(),this.scene.add(this._sky)}!e&&this.skyEnvTarget&&(this.skyEnvTarget.dispose(),this.skyEnvTarget=null,this.skyEnvKey=``)}applyFog(e){if(!e.fog){this.scene.fog=null;return}let t=e.fog.color||(e.sky?HE(e.sky):`#cfd8e0`);this.fog.color.set(t),this.fog.near=e.fog.near,this.fog.far=e.fog.far,this.scene.fog=this.fog}dispose(){this.hdriTexture?.dispose(),this.hdriTexture=null,this.skyEnvTarget?.dispose(),this.skyEnvTarget=null,this._sky&&=(this.scene.remove(this._sky),this._sky.material.dispose(),this._sky.geometry.dispose(),null)}};function rD(e){if(e===void 0)return 1;if(typeof e!=`number`||!Number.isFinite(e)||e<0)throw new y(`BAD_FORMAT`,`environment.iblIntensity must be a finite number >= 0 (multiplies the image-based ambience: sky-derived base 0.55, HDRI base 1; default 1), got ${JSON.stringify(e)}.`,{prop:`iblIntensity`});return e}function iD(e){return e.spatial===!0&&typeof e._setSpatialPose==`function`}function aD(){let e=new Set,t=[],n=[],r=[];return{visited:e,cameras:t,renderHooks:n,emitters:r,state:{visited:e,cameras:t,renderHooks:n,emitters:r,assets:void 0,sunDirection:null,sunLight:null,alpha:1}}}function oD(e,t,n,r,i){let a=i??aD();a.visited.clear(),a.cameras.length=0,a.renderHooks.length=0,a.emitters.length=0;let o=a.state;o.assets=n,o.sunDirection=r?.sunDirection??null,o.sunLight=null,o.alpha=r?.alpha??1,fD(e,t,o),pD(t,a.visited);let s=null;for(let e=0;e<a.cameras.length;e++){let t=a.cameras[e];if(t.current){s=t;break}}s||=a.cameras[0]??null;let c=s?s._ensureObject3D():null;return dD(a.emitters,c),{activeCamera:c,renderHooks:a.renderHooks,sunLight:o.sunLight}}var sD=new H,cD=new H,lD=new H,uD=new H;function dD(e,t){if(e.length===0||!t)return;t.updateWorldMatrix(!0,!1),t.getWorldPosition(cD),t.getWorldDirection(lD),uD.set(0,1,0).applyQuaternion(t.quaternion);let n={position:[cD.x,cD.y,cD.z],forward:[lD.x,lD.y,lD.z],up:[uD.x,uD.y,uD.z]};for(let{node:t,parent:r}of e)r.updateWorldMatrix(!0,!1),r.getWorldPosition(sD),t._setSpatialPose({position:[sD.x,sD.y,sD.z],listener:n})}function fD(e,t,n){let r=t;if(e instanceof Zv){let i=e._ensureObject3D();if(i.parent!==t&&t.add(i),e._syncObject3D(n.alpha),n.assets&&e instanceof rC&&e._syncModel(n.assets),typeof e._onRender3D==`function`&&n.renderHooks.push(e),n.sunDirection){let t=e._applySunDirection;typeof t==`function`&&t.call(e,n.sunDirection)}e instanceof qS&&(!n.sunLight||e.intensity>n.sunLight.intensity)&&(n.sunLight=e),n.visited.add(i),e instanceof ab&&n.cameras.push(e),r=i}else iD(e)&&n.emitters.push({node:e,parent:r});for(let t of e.children)fD(t,r,n)}function pD(e,t){let n=e.children;for(let r=n.length-1;r>=0;r--){let i=n[r];i.userData.incantoNode&&!t.has(i)?e.remove(i):pD(i,t)}}var mD=`
|
|
7294
|
+
}`};var ME=[`type`,`sunPosition`,`elevationDeg`,`azimuthDeg`,`turbidity`,`rayleigh`],NE=[`color`,`near`,`far`],PE=[`coverage`,`density`,`base`,`top`,`color`,`shadeColor`,`speed`,`scale`],FE=[`mapSize`,`radius`],IE=[1024,2048],LE=2,RE=1,zE=50,BE=800,VE=`#cfd8e0`,HE=Math.PI/180;function UE(e){return{exposure:qE(e?.exposure),sky:JE(e?.sky),fog:YE(e?.fog,e?.sky!==void 0),clouds:XE(e?.clouds),shadows:ZE(e?.shadows)}}function WE(e,t){let n=(90-e)*HE,r=t*HE;return[Math.sin(n)*Math.sin(r),Math.cos(n),Math.sin(n)*Math.cos(r)]}function GE(e){let[t,n,r]=e.sunPosition,i=Math.hypot(t,n,r)||1;return[t/i,n/i,r/i]}function KE(e){let t=[191,213,232],n=[233,228,217],r=[242,201,150],i=eD((e.turbidity-LE)/8),a=GE(e),o=eD((18-Math.asin($E(a[1],-1,1))/HE)/18)*.8,s=e=>Math.round(tD(tD(t[e],n[e],i),r[e],o));return`#${[s(0),s(1),s(2)].map(e=>e.toString(16).padStart(2,`0`)).join(``)}`}function qE(e){if(e===void 0)return 1;if(typeof e!=`number`||!Number.isFinite(e)||e<=0)throw new y(`BAD_FORMAT`,`environment.exposure must be a finite number > 0 (tone-mapping exposure, default 1), got ${JSON.stringify(e)}.`,{prop:`exposure`});return e}function JE(e){if(e===void 0)return null;if(typeof e!=`object`||!e||Array.isArray(e))throw new y(`BAD_FORMAT`,`environment.sky must be an object ({ type?: "atmosphere", sunPosition? | elevationDeg?+azimuthDeg?, turbidity?, rayleigh? }), got ${JSON.stringify(e)}.`,{prop:`sky`});let t=e;for(let e of Object.keys(t))if(!ME.includes(e))throw new y(`BAD_FORMAT`,`environment.sky has unknown key '${e}'. Valid keys: [${ME.join(`, `)}].`,{prop:`sky`,validOptions:ME});if(t.type!==void 0&&t.type!==`atmosphere`)throw new y(`BAD_FORMAT`,`environment.sky.type must be 'atmosphere' (the only sky type so far), got ${JSON.stringify(t.type)}.`,{prop:`sky`,validOptions:[`atmosphere`]});let n=t.elevationDeg!==void 0||t.azimuthDeg!==void 0;if(t.sunPosition!==void 0&&n)throw new y(`BAD_FORMAT`,`environment.sky takes sunPosition OR elevationDeg/azimuthDeg, not both.`,{prop:`sky`,validOptions:[`sunPosition`,`elevationDeg+azimuthDeg`]});let r;if(t.sunPosition!==void 0){let e=t.sunPosition;if(!Array.isArray(e)||e.length!==3||!e.every(e=>typeof e==`number`&&Number.isFinite(e))||Math.hypot(e[0],e[1],e[2])===0)throw new y(`BAD_FORMAT`,`environment.sky.sunPosition must be a non-zero [x, y, z] vector, got ${JSON.stringify(e)}.`,{prop:`sky`});r=[e[0],e[1],e[2]]}else{let e=QE(t.elevationDeg,32,`sky.elevationDeg`),n=QE(t.azimuthDeg,135,`sky.azimuthDeg`);if(e<-90||e>90)throw new y(`BAD_FORMAT`,`environment.sky.elevationDeg must be in [-90, 90] (degrees above the horizon), got ${e}.`,{prop:`sky`});r=WE(e,n)}let i=QE(t.turbidity,LE,`sky.turbidity`);if(i<=0)throw new y(`BAD_FORMAT`,`environment.sky.turbidity must be > 0 (atmospheric haze; 2 ≈ clear day), got ${i}.`,{prop:`sky`});let a=QE(t.rayleigh,RE,`sky.rayleigh`);if(a<0)throw new y(`BAD_FORMAT`,`environment.sky.rayleigh must be >= 0 (Rayleigh scattering; 1 ≈ earth-like), got ${a}.`,{prop:`sky`});return{sunPosition:r,turbidity:i,rayleigh:a}}function YE(e,t){if(e===void 0)return null;if(typeof e!=`object`||!e||Array.isArray(e))throw new y(`BAD_FORMAT`,`environment.fog must be an object ({ color?, near?, far? }), got ${JSON.stringify(e)}.`,{prop:`fog`});let n=e;for(let e of Object.keys(n))if(!NE.includes(e))throw new y(`BAD_FORMAT`,`environment.fog has unknown key '${e}'. Valid keys: [${NE.join(`, `)}].`,{prop:`fog`,validOptions:NE});if(n.color!==void 0&&typeof n.color!=`string`)throw new y(`BAD_FORMAT`,`environment.fog.color must be a hex color string, got ${JSON.stringify(n.color)}.`,{prop:`fog`});let r=QE(n.near,zE,`fog.near`),i=QE(n.far,BE,`fog.far`);if(r<0)throw new y(`BAD_FORMAT`,`environment.fog.near must be >= 0 meters, got ${r}.`,{prop:`fog`});if(i<=r)throw new y(`BAD_FORMAT`,`environment.fog.far must be > near (got near ${r}, far ${i}).`,{prop:`fog`});return{color:n.color??(t?``:VE),near:r,far:i}}function XE(e){if(e===void 0)return null;if(typeof e!=`object`||!e||Array.isArray(e))throw new y(`BAD_FORMAT`,`environment.clouds must be an object ({ coverage?, density?, base?, top?, color?, shadeColor?, speed?, scale? }), got ${JSON.stringify(e)}.`,{prop:`clouds`});let t=e;for(let e of Object.keys(t))if(!PE.includes(e))throw new y(`BAD_FORMAT`,`environment.clouds has unknown key '${e}'. Valid keys: [${PE.join(`, `)}].`,{prop:`clouds`,validOptions:PE});for(let e of[`color`,`shadeColor`])if(t[e]!==void 0&&typeof t[e]!=`string`)throw new y(`BAD_FORMAT`,`environment.clouds.${e} must be a hex color string, got ${JSON.stringify(t[e])}.`,{prop:`clouds`});let n=QE(t.coverage,.5,`clouds.coverage`);if(n<0||n>1)throw new y(`BAD_FORMAT`,`environment.clouds.coverage must be in [0, 1] (how much sky is cloudy), got ${n}.`,{prop:`clouds`});let r=QE(t.density,1,`clouds.density`);if(r<0)throw new y(`BAD_FORMAT`,`environment.clouds.density must be >= 0 (optical thickness), got ${r}.`,{prop:`clouds`});let i=QE(t.base,120,`clouds.base`),a=QE(t.top,320,`clouds.top`);if(a<=i)throw new y(`BAD_FORMAT`,`environment.clouds.top must be > base (got base ${i}, top ${a}).`,{prop:`clouds`});let o=QE(t.speed,1,`clouds.speed`),s=QE(t.scale,240,`clouds.scale`);if(s<=0)throw new y(`BAD_FORMAT`,`environment.clouds.scale must be > 0 (feature size in world units), got ${s}.`,{prop:`clouds`});return{coverage:n,density:r,base:i,top:a,color:t.color??`#ffffff`,shadeColor:t.shadeColor??`#9fb0c8`,speed:o,scale:s}}function ZE(e){if(e===void 0)return null;if(e===!1)return!1;if(e===!0)return{mapSize:2048,radius:1};if(typeof e!=`object`||!e||Array.isArray(e))throw new y(`BAD_FORMAT`,`environment.shadows must be true, false or an object ({ mapSize?, radius? }), got ${JSON.stringify(e)}.`,{prop:`shadows`});let t=e;for(let e of Object.keys(t))if(!FE.includes(e))throw new y(`BAD_FORMAT`,`environment.shadows has unknown key '${e}'. Valid keys: [${FE.join(`, `)}].`,{prop:`shadows`,validOptions:FE});let n=t.mapSize===void 0?2048:t.mapSize;if(!IE.includes(n))throw new y(`BAD_FORMAT`,`environment.shadows.mapSize must be one of [${IE.join(`, `)}], got ${JSON.stringify(t.mapSize)}.`,{prop:`shadows`,validOptions:IE.map(String)});let r=QE(t.radius,1,`shadows.radius`);if(r<0)throw new y(`BAD_FORMAT`,`environment.shadows.radius must be >= 0, got ${r}.`,{prop:`shadows`});return{mapSize:n,radius:r}}function QE(e,t,n){if(e===void 0)return t;if(typeof e!=`number`||!Number.isFinite(e))throw new y(`BAD_FORMAT`,`environment.${n} must be a finite number, got ${JSON.stringify(e)}.`,{prop:n});return e}function $E(e,t,n){return Math.min(Math.max(e,t),n)}function eD(e){return $E(e,0,1)}function tD(e,t,n){return e+(t-e)*n}var nD=`https://agent8-games.verse8.io/assets/3D/default/textures/hdri`,rD={apartment:`lebombo_1k.hdr`,city:`potsdamer_platz_1k.hdr`,dawn:`kiara_1_dawn_1k.hdr`,forest:`forest_slope_1k.hdr`,lobby:`st_fagans_interior_1k.hdr`,night:`dikhololo_night_1k.hdr`,park:`rooitou_park_1k.hdr`,studio:`studio_small_03_1k.hdr`,sunset:`venice_sunset_1k.hdr`,warehouse:`empty_warehouse_01_1k.hdr`};function iD(e){if(typeof e.hdri==`string`&&e.hdri!==``)return e.hdri;if(typeof e.preset==`string`){let t=rD[e.preset];if(!t)throw Error(`Unknown environment preset '${e.preset}'. Available: ${Object.keys(rD).join(`, `)}.`);return`${nD}/${t}`}return null}var aD=75,oD=class{scene;ambient=new Vc(`#ffffff`,0);envKey=null;config=UE(void 0);hdriUrl=null;hdriTexture=null;_sky=null;skyKey=``;skyEnvKey=``;skyEnvTarget=null;fog=new Ui(`#ffffff`,1,1e3);underwaterFog=null;underwaterBg=new K;constructor(e){this.scene=e,this.scene.add(this.ambient)}get sunDirection(){return this.config.sky?GE(this.config.sky):null}get clouds(){return this.config.clouds}get sceneFog(){return this.scene.fog instanceof Ui?this.scene.fog:null}apply(e,t){let n=e===void 0?``:JSON.stringify(e);n!==this.envKey&&(this.envKey=n,this.config=UE(e));let r=this.config,i=e?.ambient;if(this.ambient.color.set(i?.color??`#ffffff`),this.ambient.intensity=i?.intensity??0,t&&(t.toneMappingExposure=r.exposure,t.shadowMap.enabled=r.shadows!==!1),this.applyHdri(e),this.applySky(r.sky,t),this._sky&&(this._sky.visible=!0),this.applyFog(r),this.scene.environment=this.hdriTexture??this.skyEnvTarget?.texture??null,this.scene.environmentIntensity=(this.scene.environment===this.skyEnvTarget?.texture&&this.skyEnvTarget?.55:1)*sD(e?.iblIntensity),e?.skybox===!0&&this.hdriTexture)this.scene.background=this.hdriTexture;else{let t=e?.background;this.scene.background=typeof t==`string`?new K(t):null}}applySunLight(e,t=null){if(!e)return;let n=this.sunDirection;if(t){let r=e.position.length()||100,i,a,o;if(n)[i,a,o]=n;else{i=e.position.x-e.target.position.x,a=e.position.y-e.target.position.y,o=e.position.z-e.target.position.z;let t=Math.hypot(i,a,o)||1;i/=t,a/=t,o/=t}e.target.position.set(t.x,t.y,t.z),e.target.updateMatrixWorld(),e.position.set(t.x+i*r,t.y+a*r,t.z+o*r)}else if(n){let t=e.position.length()||100;e.position.set(n[0],n[1],n[2]).multiplyScalar(t)}let r=this.config.shadows;if(r&&typeof r==`object`){if(!e.castShadow){let t=e.shadow.camera;`left`in t&&t.left===-5&&(t.left=-75,t.right=aD,t.top=aD,t.bottom=-75,t.near=.5,t.far=500,t.updateProjectionMatrix()),e.castShadow=!0}e.shadow.mapSize.x!==r.mapSize&&(e.shadow.mapSize.set(r.mapSize,r.mapSize),e.shadow.map?.dispose(),e.shadow.map=null),e.shadow.radius=r.radius}}applyUnderwater(e){e&&(this.underwaterFog||=new Ui(`#000000`,.5,e.visibility),this.underwaterFog.color.set(e.color),this.underwaterFog.near=.5,this.underwaterFog.far=e.visibility,this.scene.fog=this.underwaterFog,this.scene.background=this.underwaterBg.set(e.color),this._sky&&(this._sky.visible=!1))}applyHdri(e){let t=iD(e??{});t!==this.hdriUrl&&(this.hdriUrl=t,this.hdriTexture?.dispose(),this.hdriTexture=null,t&&new AE().load(t,e=>{if(this.hdriUrl!==t){e.dispose();return}e.mapping=303,this.hdriTexture=e}))}applySky(e,t){let n=e?JSON.stringify(e):``;if(n!==this.skyKey&&(this.skyKey=n,this._sky&&=(this.scene.remove(this._sky),this._sky.material.dispose(),this._sky.geometry.dispose(),null),e)){this._sky=new jE,this._sky.scale.setScalar(45e4);let t=this._sky.material.uniforms;(t.sunPosition?.value).set(...e.sunPosition),t.turbidity&&(t.turbidity.value=e.turbidity),t.rayleigh&&(t.rayleigh.value=e.rayleigh),this.scene.add(this._sky)}if(e&&t&&this._sky&&this.skyEnvKey!==n){this.skyEnvKey=n,this.skyEnvTarget?.dispose();let e=new Vl(t),r=new Wi;r.add(this._sky),this.skyEnvTarget=e.fromScene(r),e.dispose(),this.scene.add(this._sky)}!e&&this.skyEnvTarget&&(this.skyEnvTarget.dispose(),this.skyEnvTarget=null,this.skyEnvKey=``)}applyFog(e){if(!e.fog){this.scene.fog=null;return}let t=e.fog.color||(e.sky?KE(e.sky):`#cfd8e0`);this.fog.color.set(t),this.fog.near=e.fog.near,this.fog.far=e.fog.far,this.scene.fog=this.fog}dispose(){this.hdriTexture?.dispose(),this.hdriTexture=null,this.skyEnvTarget?.dispose(),this.skyEnvTarget=null,this._sky&&=(this.scene.remove(this._sky),this._sky.material.dispose(),this._sky.geometry.dispose(),null)}};function sD(e){if(e===void 0)return 1;if(typeof e!=`number`||!Number.isFinite(e)||e<0)throw new y(`BAD_FORMAT`,`environment.iblIntensity must be a finite number >= 0 (multiplies the image-based ambience: sky-derived base 0.55, HDRI base 1; default 1), got ${JSON.stringify(e)}.`,{prop:`iblIntensity`});return e}function cD(e){return e.spatial===!0&&typeof e._setSpatialPose==`function`}function lD(){let e=new Set,t=[],n=[],r=[];return{visited:e,cameras:t,renderHooks:n,emitters:r,state:{visited:e,cameras:t,renderHooks:n,emitters:r,assets:void 0,sunDirection:null,sunLight:null,alpha:1}}}function uD(e,t,n,r,i){let a=i??lD();a.visited.clear(),a.cameras.length=0,a.renderHooks.length=0,a.emitters.length=0;let o=a.state;o.assets=n,o.sunDirection=r?.sunDirection??null,o.sunLight=null,o.alpha=r?.alpha??1,gD(e,t,o),_D(t,a.visited);let s=null;for(let e=0;e<a.cameras.length;e++){let t=a.cameras[e];if(t.current){s=t;break}}s||=a.cameras[0]??null;let c=s?s._ensureObject3D():null;return hD(a.emitters,c),{activeCamera:c,renderHooks:a.renderHooks,sunLight:o.sunLight}}var dD=new H,fD=new H,pD=new H,mD=new H;function hD(e,t){if(e.length===0||!t)return;t.updateWorldMatrix(!0,!1),t.getWorldPosition(fD),t.getWorldDirection(pD),mD.set(0,1,0).applyQuaternion(t.quaternion);let n={position:[fD.x,fD.y,fD.z],forward:[pD.x,pD.y,pD.z],up:[mD.x,mD.y,mD.z]};for(let{node:t,parent:r}of e)r.updateWorldMatrix(!0,!1),r.getWorldPosition(dD),t._setSpatialPose({position:[dD.x,dD.y,dD.z],listener:n})}function gD(e,t,n){let r=t;if(e instanceof Zv){let i=e._ensureObject3D();if(i.parent!==t&&t.add(i),e._syncObject3D(n.alpha),n.assets&&e instanceof sC&&e._syncModel(n.assets),typeof e._onRender3D==`function`&&n.renderHooks.push(e),n.sunDirection){let t=e._applySunDirection;typeof t==`function`&&t.call(e,n.sunDirection)}e instanceof ZS&&(!n.sunLight||e.intensity>n.sunLight.intensity)&&(n.sunLight=e),n.visited.add(i),e instanceof lb&&n.cameras.push(e),r=i}else cD(e)&&n.emitters.push({node:e,parent:r});for(let t of e.children)gD(t,r,n)}function _D(e,t){let n=e.children;for(let r=n.length-1;r>=0;r--){let i=n[r];i.userData.incantoNode&&!t.has(i)?e.remove(i):_D(i,t)}}var vD=`
|
|
7295
7295
|
varying vec2 vUv;
|
|
7296
7296
|
void main() {
|
|
7297
7297
|
vUv = uv;
|
|
7298
7298
|
gl_Position = vec4(position.xy, 0.0, 1.0);
|
|
7299
7299
|
}
|
|
7300
|
-
`,
|
|
7300
|
+
`,yD=`
|
|
7301
7301
|
precision highp float;
|
|
7302
7302
|
varying vec2 vUv;
|
|
7303
7303
|
uniform sampler2D tColor;
|
|
@@ -7350,9 +7350,9 @@ void main() {
|
|
|
7350
7350
|
}
|
|
7351
7351
|
gl_FragColor = color;
|
|
7352
7352
|
}
|
|
7353
|
-
`;function gD(){let e=new Rs({vertexShader:mD,fragmentShader:hD,depthTest:!1,depthWrite:!1,uniforms:{tColor:{value:null},tDepth:{value:null},uInvViewProj:{value:new G},uCameraPos:{value:new H},uWaterLevel:{value:0},uTime:{value:0},uCausticColor:{value:new K(`#cdeeff`)},uCausticIntensity:{value:.55},uCausticScale:{value:.32},uMaxDist:{value:22}}}),t=new ho(new Os(2,2),e);return t.frustumCulled=!1,{mesh:t,uniforms:e.uniforms}}var _D=class{viewOverride=null;overrideCam=new Nc(60,1,.05,5e3);lastCamera=null;lastSize={w:1,h:1};webgl;threeScene=new Wi;environment=new nD(this.threeScene);engine;disconnect;canvas;assets;ownsAssets;loadedAssetScenes=new WeakSet;compiledScene=null;syncScratch=aD();renderCtx=null;causticsTarget=null;causticsScene=null;causticsUniforms=null;cloudsTarget=null;cloudsHalfTarget=null;cloudsBlurTarget=null;cloudsScene=null;cloudsUniforms=null;cloudsBlurScene=null;cloudsBlurUniforms=null;cloudsCompositeScene=null;cloudsCompositeUniforms=null;constructor(e){this.canvas=e.canvas,this.engine=e.engine,this.ownsAssets=!e.assets,this.assets=e.assets??new Hv;let t=qe(e.engine.scene?.environment,{antialias:!0,pixelRatio:Math.min(globalThis.devicePixelRatio??1,2)},globalThis.devicePixelRatio??1,{pixelRatio:e.pixelRatio});this.webgl=new Ff({canvas:e.canvas,antialias:t.antialias}),this.webgl.setPixelRatio(t.pixelRatio),this.webgl.shadowMap.enabled=!0,this.webgl.shadowMap.type=1,this.webgl.toneMapping=4,this.webgl.toneMappingExposure=1,this.debugLines=new cs(new Ha,new Xo({color:`#00ff6e`,depthTest:!1})),this.debugLines.frustumCulled=!1,this.debugLines.renderOrder=9999,this.debugLines.visible=!1,this.threeScene.add(this.debugLines),this.disconnect=this.engine.updated.connect(()=>this.render())}debugLines;syncDebugLines(){let e=null;for(let t of Kf(`3d`))if(e=t.debugLines(),e)break;this.debugLines.visible=e!==null,e&&this.debugLines.geometry.setAttribute(`position`,new q(e,3))}render(){let e=this.engine.scene;if(!e)return;this.syncDebugLines(),e.assets&&!this.loadedAssetScenes.has(e)&&(this.assets.load(e.assets),this.loadedAssetScenes.add(e)),this.environment.apply(e.environment,this.webgl);let{activeCamera:t,renderHooks:n,sunLight:r}=oD(e.root,this.threeScene,this.assets,{sunDirection:this.environment.sunDirection,alpha:this.engine.interpolationAlpha},this.syncScratch),i=t;if(this.viewOverride){let[e,t,n]=this.viewOverride.position,[r,a,o]=this.viewOverride.target;this.overrideCam.position.set(e,t,n),this.overrideCam.lookAt(yD.set(r,a,o)),i=this.overrideCam}if(!i)return;this.lastCamera=i,i.updateWorldMatrix(!0,!1);let a=i.getWorldPosition(SD),o=r?r._ensureObject3D():null,s=!!o&&r.shadowFollowsCamera===!0;this.environment.applySunLight(o,s?a:null),this.compiledScene!==e&&(this.compiledScene=e,this.webgl.compile(this.threeScene,i));let c=this.canvas.clientWidth||this.canvas.width,l=this.canvas.clientHeight||this.canvas.height,u=this.webgl.getSize(vD);(u.x!==c||u.y!==l)&&this.webgl.setSize(c,l,!1),this.lastSize={w:c,h:l};let d=l===0?1:c/l;i.aspect!==d&&(i.aspect=d,i.updateProjectionMatrix()),this.renderCtx||={gl:this.webgl,scene:this.threeScene,camera:i};let f=this.renderCtx;f.camera=i;for(let e=0;e<n.length;e++)n[e]?._onRender3D(f);let p=null;for(let e=0;e<n.length;e++){let t=n[e].underwaterAt?.(a.x,a.y,a.z);if(t){p=t;break}}this.environment.applyUnderwater(p);let m=this.environment.clouds;p?.caustics.enabled?this.renderWithCaustics(i,p):m&&!p?this.renderWithClouds(i,m,o):this.webgl.render(this.threeScene,i)}renderWithClouds(e,t,n){let r=this.webgl.getDrawingBufferSize(vD),i=Math.max(1,r.x),a=Math.max(1,r.y),o=Math.max(1,Math.ceil(i/3)),s=Math.max(1,Math.ceil(a/3));if(this.cloudsTarget&&(this.cloudsTarget.width!==i||this.cloudsTarget.height!==a)&&(this.cloudsTarget.depthTexture?.dispose(),this.cloudsTarget.dispose(),this.cloudsTarget=null,this.cloudsHalfTarget?.dispose(),this.cloudsHalfTarget=null,this.cloudsBlurTarget?.dispose(),this.cloudsBlurTarget=null),!this.cloudsTarget){let e=new ys(i,a);e.type=Ft,this.cloudsTarget=new ai(i,a,{depthTexture:e,depthBuffer:!0})}if(this.cloudsHalfTarget||(this.cloudsHalfTarget=new ai(o,s,{depthBuffer:!1}),this.cloudsBlurTarget=new ai(o,s,{depthBuffer:!1})),!this.cloudsScene){let{mesh:e,uniforms:t}=CE();this.cloudsScene=new Wi,this.cloudsScene.add(e),this.cloudsUniforms=t}if(!this.cloudsBlurScene){let{mesh:e,uniforms:t}=SE();this.cloudsBlurScene=new Wi,this.cloudsBlurScene.add(e),this.cloudsBlurUniforms=t}if(!this.cloudsCompositeScene){let{mesh:e,uniforms:t}=wE();this.cloudsCompositeScene=new Wi,this.cloudsCompositeScene.add(e),this.cloudsCompositeUniforms=t}this.webgl.setRenderTarget(this.cloudsTarget),this.webgl.render(this.threeScene,e);let c=this.cloudsUniforms;c.tDepth.value=this.cloudsTarget.depthTexture,c.uInvViewProj.value.multiplyMatrices(e.projectionMatrix,e.matrixWorldInverse).invert(),e.getWorldPosition(c.uCameraPos.value),c.uTime.value=(globalThis.performance?.now()??0)*.001*t.speed;let l=this.environment.sunDirection??[.4,.8,.3];c.uSunDir.value.set(l[0],l[1],l[2]).normalize(),n&&c.uSunColor.value.copy(n.color),c.uCloudColor.value.set(t.color),c.uShadeColor.value.set(t.shadeColor);let u=this.environment.sceneFog;u?(c.uHorizonColor.value.copy(u.color),c.uFarFade.value=u.far):(c.uHorizonColor.value.set(`#cdd9e6`),c.uFarFade.value=6e3),c.uBase.value=t.base,c.uTop.value=t.top,c.uCoverage.value=t.coverage,c.uDensity.value=t.density,c.uScale.value=t.scale,c.uWind.value.set(1,.35),this.webgl.setRenderTarget(this.cloudsHalfTarget),this.webgl.render(this.cloudsScene,e);let d=this.cloudsBlurUniforms,f=this.cloudsBlurScene;d.tTex.value=this.cloudsHalfTarget.texture,d.uDir.value.set(1/o,0),this.webgl.setRenderTarget(this.cloudsBlurTarget),this.webgl.render(f,e),d.tTex.value=this.cloudsBlurTarget.texture,d.uDir.value.set(0,1/s),this.webgl.setRenderTarget(this.cloudsHalfTarget),this.webgl.render(f,e),this.webgl.setRenderTarget(null);let p=this.cloudsCompositeUniforms;p.tScene.value=this.cloudsTarget.texture,p.tClouds.value=this.cloudsHalfTarget.texture,this.webgl.render(this.cloudsCompositeScene,e)}renderWithCaustics(e,t){let n=this.webgl.getDrawingBufferSize(vD),r=Math.max(1,n.x),i=Math.max(1,n.y);if(this.causticsTarget&&(this.causticsTarget.width!==r||this.causticsTarget.height!==i)&&(this.causticsTarget.depthTexture?.dispose(),this.causticsTarget.dispose(),this.causticsTarget=null),!this.causticsTarget){let e=new ys(r,i);e.type=Ft,this.causticsTarget=new ai(r,i,{depthTexture:e,depthBuffer:!0})}if(!this.causticsScene){let{mesh:e,uniforms:t}=gD();this.causticsScene=new Wi,this.causticsScene.add(e),this.causticsUniforms=t}this.webgl.setRenderTarget(this.causticsTarget),this.webgl.render(this.threeScene,e),this.webgl.setRenderTarget(null);let a=this.causticsUniforms;a.tColor.value=this.causticsTarget.texture,a.tDepth.value=this.causticsTarget.depthTexture,a.uInvViewProj.value.multiplyMatrices(e.projectionMatrix,e.matrixWorldInverse).invert(),e.getWorldPosition(a.uCameraPos.value),a.uWaterLevel.value=t.surfaceY,a.uTime.value=(globalThis.performance?.now()??0)*.001*t.caustics.speed,a.uCausticColor.value.set(t.caustics.color),a.uCausticIntensity.value=t.caustics.intensity,a.uCausticScale.value=t.caustics.scale,a.uMaxDist.value=t.visibility,this.webgl.render(this.causticsScene,e)}screenFromWorld(e,t,n){let r=this.lastCamera;return r?(bD.set(e,t,n).project(r),{x:(bD.x+1)/2*this.lastSize.w,y:(1-bD.y)/2*this.lastSize.h,behind:bD.z>1}):{x:0,y:0,behind:!0}}pick(e,t){let n=this.lastCamera;if(!n)return null;wD.set(e/this.lastSize.w*2-1,-(t/this.lastSize.h*2-1)),CD.setFromCamera(wD,n);let r=CD.intersectObjects(this.threeScene.children,!0);for(let e of r){let t=e.object;for(;t&&!t.userData.incantoNode;)t=t.parent;let n=t?.userData.incantoNode;if(n)return n}return null}stats(){let e=this.webgl.info;return{triangles:e.render.triangles,drawCalls:e.render.calls,geometries:e.memory.geometries,textures:e.memory.textures}}cameraBasis(){let e=this.lastCamera,t=e?e.getWorldQuaternion(xD):xD.identity();return{right:new H(1,0,0).applyQuaternion(t),up:new H(0,1,0).applyQuaternion(t),forward:new H(0,0,-1).applyQuaternion(t)}}dispose(){this.disconnect(),this.threeScene.traverse(e=>{let t=e;if(!(!t.isMesh||t.userData?.incantoModelShared))if(t.geometry?.dispose(),Array.isArray(t.material))for(let e of t.material)e.dispose();else t.material?.dispose()}),this.causticsTarget?.depthTexture?.dispose(),this.causticsTarget?.dispose();let e=this.causticsScene?.children[0];e?.geometry?.dispose(),e?.material?.dispose(),this.cloudsTarget?.depthTexture?.dispose(),this.cloudsTarget?.dispose(),this.cloudsHalfTarget?.dispose(),this.cloudsBlurTarget?.dispose();for(let e of[this.cloudsScene,this.cloudsBlurScene,this.cloudsCompositeScene]){let t=e?.children[0];t?.geometry?.dispose(),t?.material?.dispose()}this.ownsAssets&&this.assets.dispose(),this.environment.dispose(),this.webgl.dispose()}},vD=new B,yD=new H,bD=new H,xD=new V,SD=new H,CD=new pl,wD=new B,TD=e({DEFAULT_TERRAIN_TEXTURE_BASE:()=>xy,WATER_MAX_RIPPLES:()=>8,enablePhysics3D:()=>By}),ED=new WeakMap,DD=class e{account;roomId;roomState=new t;allUserStates=new t;userJoined=new t;userLeft=new t;globalState=new t;globalMyState=new t;asset=new t;latestUserStates={};latestRoomState={};latestGlobalState={};latestGlobalMyState={};latestAsset={};server;engine;throttleMs;subs=[];messageSignals=new Map;collectionSignals=new Map;latestCollections=new Map;globalMessageSignals=new Map;globalCollectionSignals=new Map;latestGlobalCollections=new Map;scenes=new Map;lastSent=new Map;sendAccumulator=0;detachReplication=null;lastOwner=null;boundScene=null;static get(e){return ED.get(e)??null}static async create(t,n={}){await ED.get(t)?.dispose();let r=n.transport??await MD(n.config);await r.connect();let i=n.room??t.scene?.multiplayer?.room??`auto`,a=new e(t,r,await r.remoteFunction(`joinRoom`,[i===`auto`?void 0:i],{needResponse:!0}),n.throttleMs??50);return ED.set(t,a),a}constructor(e,t,n,r){this.engine=e,this.server=t,this.roomId=n,this.account=t.account,this.throttleMs=Math.max(30,r),this.boundScene=e.scene,this.subs.push(t.subscribeRoomState(n,e=>{this.latestRoomState=e,this.roomState.emit(e)}),t.subscribeRoomAllUserStates(n,e=>{this.latestUserStates=e,this.allUserStates.emit(e)}),t.onRoomUserJoin(n,e=>this.userJoined.emit(e)),t.onRoomUserLeave(n,e=>this.userLeft.emit(e))),t.subscribeGlobalState&&this.subs.push(t.subscribeGlobalState(e=>{this.latestGlobalState=e,this.globalState.emit(e)})),t.subscribeGlobalMyState&&this.subs.push(t.subscribeGlobalMyState(e=>{this.latestGlobalMyState=e,this.globalMyState.emit(e)})),t.subscribeAsset&&this.subs.push(t.subscribeAsset(this.account,e=>{this.latestAsset=e,this.asset.emit(e)})),this.detachReplication=e.fixedUpdated.connect(e=>this.replicate(e))}message(e){let n=this.messageSignals.get(e);return n||(n=new t,this.messageSignals.set(e,n),this.subs.push(this.server.onRoomMessage(this.roomId,e,e=>n?.emit(e)))),n}collection(e){let n=this.collectionSignals.get(e);return n||(n=new t,this.collectionSignals.set(e,n),this.subs.push(this.server.subscribeRoomCollection(this.roomId,e,t=>{this.latestCollections.set(e,t),n?.emit(t)}))),n}latestCollection(e){return this.collection(e),this.latestCollections.get(e)??{}}globalMessage(e){let n=this.globalMessageSignals.get(e);if(!n){n=new t,this.globalMessageSignals.set(e,n);let r=this.server.onGlobalMessage?.(e,e=>n?.emit(e));r&&this.subs.push(r)}return n}globalCollection(e){let n=this.globalCollectionSignals.get(e);if(!n){n=new t,this.globalCollectionSignals.set(e,n);let r=this.server.subscribeGlobalCollection?.(e,t=>{this.latestGlobalCollections.set(e,t),n?.emit(t)});r&&this.subs.push(r)}return n}latestGlobalCollection(e){return this.globalCollection(e),this.latestGlobalCollections.get(e)??{}}setMyState(e){return this.server.remoteFunction(`setMyState`,[this.roomId,e],{throttle:this.throttleMs,throttleKey:`incanto:myState`})}patchRoomState(e){return this.server.remoteFunction(`patchRoomState`,[this.roomId,e])}addEntity(e,t){return this.server.remoteFunction(`addEntity`,[this.roomId,e,t])}updateEntity(e,t,n){return this.server.remoteFunction(`updateEntity`,[this.roomId,e,t,n])}removeEntity(e,t){return this.server.remoteFunction(`removeEntity`,[this.roomId,e,t])}sendEvent(e,t){return this.server.remoteFunction(`sendEvent`,[this.roomId,e,t])}call(e,...t){return this.server.remoteFunction(e,[this.roomId,...t],{needResponse:!0})}registerScene(e,t){this.scenes.set(e,t)}resolveScene(e){let t=this.scenes.get(e);if(!t)throw new y(`UNRESOLVED_INSTANCE`,`No scene registered for '${e}'. Registered: [${[...this.scenes.keys()].join(`, `)}]. Call manager.registerScene('${e}', sceneJson).`);return t}async dispose(){this.detachReplication?.();for(let e of this.subs)e();await this.server.remoteFunction(`leaveRoom`,[this.roomId]),ED.delete(this.engine)}replicate(e){let t=this.engine.scene;if(!t||t!==this.boundScene)return;this.sendAccumulator+=e*1e3;let n=OD(t.root,!0);if(!n)return;n!==this.lastOwner&&(this.lastOwner=n,this.lastSent.clear());let r=n.network,i=Math.max(30,typeof r.throttleMs==`number`?r.throttleMs:this.throttleMs),a=Array.isArray(r.sync)?r.sync:[],o={};for(let e of a){let t=AD(n,e);t!==void 0&&(se(this.lastSent.get(e)??null,t)||(o[e]=j(t)))}if(!(this.sendAccumulator<i)&&Object.keys(o).length!==0){for(let[e,t]of Object.entries(o))this.lastSent.set(e,j(t));this.sendAccumulator=0,this.setMyState({sync:o})}}};function OD(e,t=!1){let n=[];if(kD(e,n),t&&n.length>1)throw new y(`BAD_FORMAT`,`Multiple network owner nodes found (${n.map(e=>e.getPath()).join(`, `)}). Exactly ONE node per player may declare network.mode 'owner' — replicate spawned entities through collections instead.`);return n[0]??null}function kD(e,t){e.network?.mode===`owner`&&t.push(e);for(let n of e.children)kD(n,t)}function AD(e,t){let n=t.lastIndexOf(`.`),r=n===-1?e:e.getNodeOrNull(t.slice(0,n))??void 0,i=n===-1?t:t.slice(n+1);if(r)return r[i]}function jD(e,t,n){for(let[r,i]of Object.entries(t)){let t=r.lastIndexOf(`.`),a=t===-1?e:e.getNodeOrNull(r.slice(0,t)),o=t===-1?r:r.slice(t+1);a&&n(a,o,i)}}async function MD(e){let{createAgent8Server:t}=await Xf(async()=>{let{createAgent8Server:e}=await import(`./agent8-CjLdyAp1.js`);return{createAgent8Server:e}},[],import.meta.url);return t(e)}var ND=class extends Ae{static typeName=`NetworkSpawner`;static signals=[`spawned`,`despawned`];static props={source:{default:`users`},scene:{default:``},interpolate:{default:!0}};source=`users`;scene=``;interpolate=!0;spawned=new Map;positionTargets=new Map;failedKeys=new Set;update(e){let t=this.tree?.engine;if(!t)return;let n=DD.get(t);if(!n||this.scene===``)return;let r=this.currentEntries(n);for(let[e,t]of Object.entries(r)){let r=this.spawned.get(e);if(!r){if(this.failedKeys.has(e))continue;try{r=ot(n.resolveScene(this.scene).root)}catch(t){throw this.failedKeys.add(e),t}r.name=PD(e),this.addChild(r),this.spawned.set(e,r),this.emit(`spawned`,r,e)}let i=t.sync;i&&jD(r,i,(e,t,n)=>this.setProp(e,t,n))}for(let[e,t]of[...this.spawned.entries()])e in r||(this.spawned.delete(e),this.positionTargets.delete(t),this.emit(`despawned`,t,e),t.free());this.interpolate&&this.stepInterpolation(e)}currentEntries(e){if(this.source===`users`){let t={};for(let[n,r]of Object.entries(e.latestUserStates))n!==e.account&&(t[n]=r);return t}return this.source.startsWith(`collection:`)?e.latestCollection(this.source.slice(11)):{}}setProp(e,t,n){if(this.interpolate&&t===`position`&&Array.isArray(n)){this.positionTargets.set(e,n);return}e[t]=n}stepInterpolation(e){let t=Math.min(1,e*8);for(let[e,n]of this.positionTargets){let r=e.position;Array.isArray(r)&&(e.position=r.map((e,r)=>e+((n[r]??e)-e)*t))}}};function PD(e){return e.replace(/[/%]/g,`_`)||`remote`}function FD(){Ke(),M(ND)}var ID=80,LD=` `;function RD(e){return`${zD(e,0)}\n`}function zD(e,t){if(typeof e!=`object`||!e)return JSON.stringify(e);let n=BD(e);if(t*2+n.length<=ID)return n;let r=LD.repeat(t+1),i=LD.repeat(t);if(Array.isArray(e))return e.length===0?`[]`:`[\n${e.map(e=>r+zD(e,t+1)).join(`,
|
|
7354
|
-
`)}\n${i}]`;let a=Object.entries(e);return a.length===0?`{}`:`{\n${a.map(([e,n])=>`${r}${JSON.stringify(e)}: ${
|
|
7355
|
-
`)}\n${i}}`}function BD(e){if(typeof e!=`object`||!e)return JSON.stringify(e);if(Array.isArray(e))return e.length===0?`[]`:`[${e.map(BD).join(`, `)}]`;let t=Object.entries(e);return t.length===0?`{}`:`{ ${t.map(([e,t])=>`${JSON.stringify(e)}: ${BD(t)}`).join(`, `)} }`}async function VD(e){if(!e.ok)throw Error(`HTTP ${e.status} — ${await e.text()}`);return e}async function HD(){return await(await VD(await fetch(`/api/meta`))).json()}async function UD(){return await(await VD(await fetch(`/api/scenes`))).json()}async function WD(e){return await(await VD(await fetch(`/api/scenes`,{method:`POST`,body:JSON.stringify({path:e})}))).json()}function GD(e){return e?`/api/scene?file=${encodeURIComponent(e)}`:`/api/scene`}async function KD(e){return await(await VD(await fetch(GD(e)))).json()}async function qD(e,t){await VD(await fetch(GD(t),{method:`PUT`,body:RD(e)}))}var JD=localStorage.getItem(`incanto-editor-lang`)??`en`,YD=new Set;function XD(){return JD}function ZD(e){JD=e,localStorage.setItem(`incanto-editor-lang`,e);for(let e of YD)e()}function QD(e){YD.add(e)}function $D(e){return e[JD]}var eO={node:{paths:[`M5 5h14v14H5z`]},move:{paths:[`M12 3v18M3 12h18`,`M8 7l4-4 4 4M8 17l4 4 4-4M7 8l-4 4 4 4M17 8l4 4-4 4`]},image:{paths:[`M4 5h16v14H4z`,`M4 15l5-5 4 4 3-3 4 4`],circles:[[9,9,1.6]]},film:{paths:[`M4 4h16v16H4z`,`M4 9h16M4 15h16`,`M9 4v16M15 4v16`]},video:{paths:[`M3 7h12v10H3z`,`M15 10l6-3v10l-6-3`]},text:{paths:[`M5 7V5h14v2`,`M12 5v14`,`M9 19h6`]},layers:{paths:[`M12 3 3 8l9 5 9-5-9-5z`,`M3 12l9 5 9-5`,`M3 16l9 5 9-5`]},square:{paths:[`M5 5h14v14H5z`,`M5 12h14`]},ball:{paths:[`M12 8v0`],circles:[[12,12,8],[12,12,1.4]]},person:{paths:[`M5 21v-1a7 7 0 0 1 14 0v1`],circles:[[12,7,4]]},area:{paths:[`M5 5h14v14H5z`],dashed:!0},gamepad:{paths:[`M7 9h-0M6 12h4M8 10v4`,`M4 8h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2z`],circles:[[16,11,1],[18.5,13.5,1]]},clock:{paths:[`M12 7v5l3 2`],circles:[[12,12,9]]},cube:{paths:[`M21 16V8l-9-5-9 5v8l9 5 9-5z`,`M3.3 7.5 12 12.5l8.7-5`,`M12 22V12.5`]},sun:{paths:[`M12 2v3M12 19v3M2 12h3M19 12h3M4.9 4.9l2.1 2.1M17 17l2.1 2.1M19.1 4.9 17 7M7 17l-2.1 2.1`],circles:[[12,12,4]]},bulb:{paths:[`M9 18h6M10 21h4`,`M8 13a6 6 0 1 1 8 0c-1 1-1.5 2-1.5 3h-5c0-1-.5-2-1.5-3z`]},globe:{paths:[`M2 12h20`,`M12 2a15 15 0 0 1 0 20a15 15 0 0 1 0-20z`],circles:[[12,12,10]]},link:{paths:[`M10 14a5 5 0 0 0 7 0l3-3a5 5 0 0 0-7-7l-1 1`,`M14 10a5 5 0 0 0-7 0l-3 3a5 5 0 0 0 7 7l1-1`]},speaker:{paths:[`M11 5 6 9H3v6h3l5 4V5z`,`M15.5 8.5a5 5 0 0 1 0 7M18.5 5.5a9 9 0 0 1 0 13`]},swatch:{paths:[`M4 4h16v16H4z`,`M4 4l16 16`]},sparkles:{paths:[`M12 4v5M12 15v5M5 12h5M14 12h5`],circles:[[6,5,1],[18,19,1]]},waves:{paths:[`M2 8c3-3 5 3 8 0s5 3 8 0`,`M2 14c3-3 5 3 8 0s5 3 8 0`,`M2 20c3-3 5 3 8 0s5 3 8 0`]},plant:{paths:[`M12 21v-8`,`M12 13c0-4-3-6-7-6 0 4 3 6 7 6z`,`M12 11c0-4 3-6 7-6 0 4-3 6-7 6z`]},flower:{paths:[`M12 21v-7`],circles:[[12,9,2],[12,4.5,2.2],[16.3,7.5,2.2],[14.7,12.6,2.2],[9.3,12.6,2.2],[7.7,7.5,2.2]]},voxels:{paths:[`M4 14h8v8H4z`,`M12 14h8v8h-8z`,`M8 6h8v8H8z`]}},tO={Node:`node`,Timer:`clock`,AudioPlayer:`speaker`,ColorRect2D:`swatch`,Particles2D:`sparkles`,Particles3D:`sparkles`,Water3D:`waves`,Foliage3D:`plant`,Flowers3D:`flower`,VoxelGrid3D:`voxels`,ModelInstance3D:`person`,CharacterController3D:`gamepad`,Node2D:`move`,Sprite2D:`image`,AnimatedSprite2D:`film`,Camera2D:`video`,Label:`text`,UILayer:`layers`,StaticBody2D:`square`,RigidBody2D:`ball`,CharacterBody2D:`person`,Area2D:`area`,CharacterController2D:`gamepad`,Node3D:`move`,MeshInstance3D:`cube`,Camera3D:`video`,DirectionalLight3D:`sun`,OmniLight3D:`bulb`,StaticBody3D:`square`,RigidBody3D:`ball`,CharacterBody3D:`person`,Area3D:`area`,NetworkSpawner:`globe`};function nO(e){let t=eO[tO[e??``]??(e?`node`:`link`)],n=document.createElementNS(`http://www.w3.org/2000/svg`,`svg`);n.setAttribute(`viewBox`,`0 0 24 24`),n.setAttribute(`width`,`13`),n.setAttribute(`height`,`13`),n.setAttribute(`fill`,`none`),n.setAttribute(`stroke`,`currentColor`),n.setAttribute(`stroke-width`,`2`),n.setAttribute(`stroke-linecap`,`round`),n.setAttribute(`stroke-linejoin`,`round`),t.dashed&&n.setAttribute(`stroke-dasharray`,`3 2.4`);for(let e of t.paths){let t=document.createElementNS(`http://www.w3.org/2000/svg`,`path`);t.setAttribute(`d`,e),n.appendChild(t)}for(let[e,r,i]of t.circles??[]){let t=document.createElementNS(`http://www.w3.org/2000/svg`,`circle`);t.setAttribute(`cx`,String(e)),t.setAttribute(`cy`,String(r)),t.setAttribute(`r`,String(i)),n.appendChild(t)}return n}function rO(){let e=document.createElementNS(`http://www.w3.org/2000/svg`,`svg`);e.setAttribute(`viewBox`,`0 0 24 24`),e.setAttribute(`width`,`12`),e.setAttribute(`height`,`12`),e.setAttribute(`fill`,`none`),e.setAttribute(`stroke`,`currentColor`),e.setAttribute(`stroke-width`,`2.4`),e.setAttribute(`aria-hidden`,`true`);let t=document.createElementNS(`http://www.w3.org/2000/svg`,`path`);return t.setAttribute(`d`,`m6 9 6 6 6-6`),e.appendChild(t),e}var iO=[{type:`texture`,label:`image (texture)`,metaHints:[`filter`]},{type:`spritesheet`,label:`spritesheet`,metaHints:[`filter`,`frameWidth`,`frameHeight`]},{type:`model`,label:`3D model (GLB/VRM)`,metaHints:[]},{type:`animation`,label:`animation (GLB clips)`,metaHints:[`clip`]}],aO=new Set,oO=null,sO=null;function cO(e,t,n){let r=document.createElement(`input`);r.className=`rename-input`,r.value=e;let i=()=>{let i=r.value.trim().replace(/\//g,``);i&&i!==e?t(i):n()};r.addEventListener(`keydown`,e=>{e.stopPropagation(),e.key===`/`&&e.preventDefault(),e.key===`Enter`&&i(),e.key===`Escape`&&n()});for(let e of[`click`,`pointerdown`,`dblclick`,`mousedown`])r.addEventListener(e,e=>e.stopPropagation());return r.addEventListener(`blur`,i),queueMicrotask(()=>{r.focus(),r.select()}),r}function lO(e,t){let n={path:``,name:``,folders:new Map,assets:[]},r=e=>{let t=n;if(e===``)return t;for(let n of e.split(`/`)){let e=t.folders.get(n);e||(e={path:t.path?`${t.path}/${n}`:n,name:n,folders:new Map,assets:[]},t.folders.set(n,e)),t=e}return t};for(let t of e){let e=t.lastIndexOf(`/`);r(e===-1?``:t.slice(0,e)).assets.push(t)}for(let e of t)r(e);return n}function uO(e){let t=e.assets.length;for(let n of e.folders.values())t+=uO(n);return t}function dO(e,t){e.textContent=``;let n=t.working.assets??{},r=Object.keys(n);if(r.length===0&&t.pendingGroups.size===0&&!t.addingAsset&&!t.addingGroup){let t=document.createElement(`div`);t.className=`muted-note explorer-empty`,t.textContent=`no assets yet — + adds textures, models, animations`,e.appendChild(t);return}let i=(e,n)=>{e.addEventListener(`dragover`,t=>{t.preventDefault(),t.stopPropagation(),e.classList.add(`drop-target`)}),e.addEventListener(`dragleave`,()=>e.classList.remove(`drop-target`)),e.addEventListener(`drop`,r=>{r.preventDefault(),r.stopPropagation(),e.classList.remove(`drop-target`);let i=r.dataTransfer?.getData(`text/incanto-asset`);i&&t.moveAsset(i,n);let a=r.dataTransfer?.getData(`text/incanto-group`);a&&t.moveGroup(a,n)})};i(e,``);let a=(e,r)=>{let i=n[e],a=e.includes(`/`)?e.slice(e.lastIndexOf(`/`)+1):e,o=document.createElement(`div`);o.className=`tree-row asset-row${t.selectedAsset===e?` selected`:``}`,o.draggable=!0,o.addEventListener(`dragstart`,t=>{t.dataTransfer?.setData(`text/incanto-asset`,e)});let s=document.createElement(`span`);if(s.className=`tree-icon`,s.appendChild(CO(i.type??``)),s.addEventListener(`click`,e=>{e.stopPropagation(),wO(s,i.type??``)}),oO===e){let n=e.includes(`/`)?e.slice(0,e.lastIndexOf(`/`)+1):``;o.appendChild(cO(a,r=>{oO=null,t.renameAssetKey(e,n+r)},()=>{oO=null,t.selectAsset(t.selectedAsset)})),o.draggable=!1,o.insertBefore(s,o.firstChild),r.appendChild(o);return}let c=document.createElement(`span`);c.className=`tree-name asset-key`,c.textContent=`$${a}`,c.title=`$${e}`,c.addEventListener(`dblclick`,n=>{n.stopPropagation(),oO=e,t.selectAsset(e)}),o.append(s,c),o.addEventListener(`click`,()=>t.selectAsset(e)),r.appendChild(o)},o=(e,n)=>{let r=aO.has(e.path),s=document.createElement(`div`);s.className=`asset-folder${r?` collapsed`:``}${t.selectedGroup===e.path?` selected`:``}`,s.draggable=!0,s.addEventListener(`dragstart`,t=>{t.stopPropagation(),t.dataTransfer?.setData(`text/incanto-group`,e.path)});let c=document.createElement(`span`);c.className=`chev`,c.appendChild(rO());let l=document.createElementNS(`http://www.w3.org/2000/svg`,`svg`);l.setAttribute(`viewBox`,`0 0 24 24`),l.setAttribute(`width`,`12`),l.setAttribute(`height`,`12`),l.setAttribute(`fill`,`none`),l.setAttribute(`stroke`,`currentColor`),l.setAttribute(`stroke-width`,`1.8`),l.setAttribute(`aria-hidden`,`true`);let u=document.createElementNS(`http://www.w3.org/2000/svg`,`path`);if(u.setAttribute(`d`,`M3 6a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z`),l.appendChild(u),sO===e.path){let i=e.path.includes(`/`)?e.path.slice(0,e.path.lastIndexOf(`/`)+1):``;if(s.append(c,l,cO(e.name,n=>{sO=null,t.renameGroup(e.path,i+n)},()=>{sO=null,t.selectGroup(t.selectedGroup)})),s.draggable=!1,n.appendChild(s),!r){let t=document.createElement(`div`);t.className=`tree-children`;for(let n of e.folders.values())o(n,t);for(let n of e.assets)a(n,t);t.children.length>0&&n.appendChild(t)}return}let d=document.createElement(`span`);d.textContent=e.name,d.addEventListener(`dblclick`,n=>{n.stopPropagation(),sO=e.path,t.selectGroup(e.path)});let f=document.createElement(`span`);if(f.className=`count`,f.textContent=`(${uO(e)})`,s.append(c,l,d,f),s.addEventListener(`click`,()=>{aO.has(e.path)?aO.delete(e.path):aO.add(e.path),t.selectGroup(e.path)}),i(s,e.path),n.appendChild(s),!r){let t=document.createElement(`div`);t.className=`tree-children`;for(let n of e.folders.values())o(n,t);for(let n of e.assets)a(n,t);t.children.length>0&&n.appendChild(t)}},s=lO(r,t.pendingGroups);for(let t of s.assets)a(t,e);for(let t of s.folders.values())o(t,e)}function fO(e,t,n){if(t.selection!==null)return!1;if(t.addingGroup){e.appendChild(OO(t.newGroupParent?`NEW GROUP IN ${t.newGroupParent}/`:`NEW GROUP`));let n=document.createElement(`input`);n.placeholder=t.newGroupParent?`heroes`:`characters (or a/b to nest)`,n.className=`mono`,e.appendChild(vO(`name`,n));let r=document.createElement(`button`);r.type=`button`,r.className=`primary`,r.textContent=`add`;let i=()=>{let e=n.value.trim().replace(/^\/+|\/+$/g,``);e&&(t.cancelAddForms(),t.addGroup(t.newGroupParent?`${t.newGroupParent}/${e}`:e))};r.addEventListener(`click`,i),n.addEventListener(`keydown`,e=>{e.key===`Enter`&&i()});let a=document.createElement(`div`);return a.className=`pop-actions`,a.appendChild(r),e.appendChild(a),setTimeout(()=>n.focus(),0),!0}if(t.addingAsset)return gO(e,t);if(t.selectedGroup!==null){let r=t.selectedGroup,i=t.groupCount(r);e.appendChild(OO(`GROUP (${i} asset${i===1?``:`s`})`));let a=r.includes(`/`)?r.slice(0,r.lastIndexOf(`/`)+1):``,o=r.includes(`/`)?r.slice(r.lastIndexOf(`/`)+1):r;e.appendChild(yO(`name`,o,e=>{let n=e.trim().replace(/\/+/g,``);n&&n!==o&&t.renameGroup(r,a+n)}));let s=document.createElement(`button`);return s.type=`button`,s.className=`ghost danger`,s.textContent=`delete group`,s.addEventListener(`click`,()=>{if(i===0){t.deleteGroup(r);return}n(`'${r}/' contains ${i} asset${i===1?``:`s`} — deleting the group deletes them too.`,`delete group & assets`,()=>t.deleteGroup(r))}),e.appendChild(s),!0}let r=t.selectedAsset;if(r===null)return!1;let i=t.working.assets??{};if(!(r in i))return!1;e.appendChild(OO(`ASSET — ${String(i[r].type??`?`)}`)),e.appendChild(TO(r)),e.appendChild(EO(t,r)),e.appendChild(pO(t,i,r));let a=document.createElement(`button`);return a.type=`button`,a.className=`ghost danger`,a.textContent=`delete asset`,a.addEventListener(`click`,()=>{t.selectAsset(null),t.mutate(()=>{delete i[r],Object.keys(i).length===0&&delete t.working.assets})}),e.appendChild(a),!0}function pO(e,t,n){let r=t[n],i=document.createElement(`div`);i.className=`asset-editor`;let a=n.includes(`/`)?n.slice(n.lastIndexOf(`/`)+1):n,o=n.includes(`/`)?n.slice(0,n.lastIndexOf(`/`)+1):``,s=yO(`name`,a,t=>{let r=t.trim().replace(/^\$/,``).replace(/\//g,``);r&&r!==a&&e.renameAssetKey(n,o+r)});DO(s.querySelector(`input`)),i.appendChild(s),i.appendChild(yO(`url`,String(r.url??``),t=>{e.mutate(()=>{r.url=t.trim()})}));let c=document.createElement(`div`);c.className=`muted-note`,c.textContent=`meta (key · value)`,i.appendChild(c);for(let[t,n]of Object.entries(r))t===`type`||t===`url`||i.appendChild(mO(e,r,t,n));return i.appendChild(hO(e,r)),i}function mO(e,t,n,r){let i=document.createElement(`div`);i.className=`meta-row`;let a=document.createElement(`input`);a.value=n,a.className=`mono`;let o=document.createElement(`input`);o.value=typeof r==`string`?r:JSON.stringify(r),o.className=`mono`;let s=()=>{let r=a.value.trim();e.mutate(()=>{delete t[n],r&&(t[r]=bO(o.value))})};a.addEventListener(`change`,s),o.addEventListener(`change`,s);let c=document.createElement(`button`);return c.type=`button`,c.className=`linklike danger-link`,c.textContent=`✕`,c.addEventListener(`click`,()=>{e.mutate(()=>{delete t[n]})}),i.append(a,o,c),i}function hO(e,t){let n=document.createElement(`div`);n.className=`meta-row`;let r=document.createElement(`input`);r.placeholder=`filter…`,r.className=`mono`;let i=document.createElement(`input`);i.placeholder=`nearest`,i.className=`mono`;let a=()=>{let n=r.value.trim();!n||i.value.trim()===``||e.mutate(()=>{t[n]=bO(i.value)})};return r.addEventListener(`change`,a),i.addEventListener(`change`,a),n.append(r,i,document.createElement(`span`)),n}function gO(e,t){return e.appendChild(OO(`NEW ASSET`)),e.appendChild(_O(t)),!0}function _O(e){let t=document.createElement(`div`);t.className=`asset-editor`;let n=document.createElement(`select`);for(let e of iO){let t=document.createElement(`option`);t.value=e.type,t.textContent=e.label,n.appendChild(t)}t.appendChild(vO(`type`,n));let r=document.createElement(`input`);r.placeholder=`(optional) characters`,r.className=`mono`,r.value=e.newAssetGroup;let i=`asset-groups-list`;r.setAttribute(`list`,i);let a=document.createElement(`datalist`);a.id=i;let o=new Set;for(let t of Object.keys(e.working.assets??{})){let e=t.lastIndexOf(`/`);e!==-1&&o.add(t.slice(0,e))}for(let t of e.pendingGroups)o.add(t);for(let e of o){let t=document.createElement(`option`);t.value=e,a.appendChild(t)}t.appendChild(vO(`group`,r)),t.appendChild(a);let s=document.createElement(`input`);s.placeholder=`coin`,s.className=`mono`,DO(s),t.appendChild(vO(`key`,s));let c=document.createElement(`input`);c.placeholder=`/textures/coin.png · https://… · data:…`,c.className=`mono`,t.appendChild(vO(`url`,c));let l=document.createElement(`textarea`);l.rows=2,l.placeholder=`or paste a JSON object: { "type": "model", "url": "/m.glb" }`,t.appendChild(l);let u=document.createElement(`button`);u.type=`button`,u.className=`primary`,u.textContent=`add`,u.addEventListener(`click`,()=>{let t=s.value.trim().replace(/^\$/,``);if(!t)return;let i=r.value.trim().replace(/\/+$/,``),a=i?`${i}/${t}`:t,o=null,u=l.value.trim();if(u)try{o=JSON.parse(u)}catch{l.classList.add(`invalid`);return}else c.value.trim()&&(o={type:n.value,url:c.value.trim()});o&&(e.cancelAddForms(),e.selectedAsset=a,e.mutate(()=>{e.working.assets||(e.working.assets={});let t=e.working.assets;t[a]=o}))});let d=document.createElement(`div`);return d.className=`pop-actions`,d.appendChild(u),t.appendChild(d),t}function vO(e,t){let n=document.createElement(`label`);n.className=`field`;let r=document.createElement(`span`);return r.textContent=e,n.append(r,t),n}function yO(e,t,n){let r=document.createElement(`input`);return r.value=t,r.className=`mono`,r.addEventListener(`change`,()=>n(r.value)),vO(e,r)}function bO(e){let t=e.trim();if(t===`true`)return!0;if(t===`false`)return!1;if(t!==``&&Number.isFinite(Number(t)))return Number(t);if(t.startsWith(`{`)||t.startsWith(`[`))try{return JSON.parse(t)}catch{return e}return e}var xO={texture:`M3 5h18v14H3z M3 15l5-5 4 4 3-3 6 6 M8.5 9.5h.01`,spritesheet:`M3 4h18v16H3z M9 4v16 M15 4v16 M3 12h18`,model:`M12 2l9 5v10l-9 5-9-5V7z M12 12l9-5 M12 12L3 7 M12 12v10`,animation:`M12 2a10 10 0 1 0 10 10 M22 12l-3-3m3 3l3-3 M12 7v5l3 3`},SO={texture:{en:`image (texture) — Sprite2D.texture references it as "$key".`,ko:`이미지(텍스처) — Sprite2D.texture에서 "$키"로 참조합니다.`},spritesheet:{en:`spritesheet — AnimatedSprite2D slices it into named frame animations.`,ko:`스프라이트시트 — AnimatedSprite2D가 이름 붙은 프레임 애니메이션으로 자릅니다.`},model:{en:`3D model (GLB/glTF/VRM) — ModelInstance3D.model references it as "$key".`,ko:`3D 모델(GLB/glTF/VRM) — ModelInstance3D.model에서 "$키"로 참조합니다.`},animation:{en:`animation clips (GLB, in memory — drawn nowhere) — any model can play them; mixamo retargets onto VRM.`,ko:`애니메이션 클립(GLB, 메모리 전용 — 그려지지 않음) — 어떤 모델이든 재생 가능, mixamo는 VRM에 자동 리타게팅.`}};function CO(e){let t=document.createElementNS(`http://www.w3.org/2000/svg`,`svg`);t.setAttribute(`viewBox`,`0 0 24 24`),t.setAttribute(`width`,`13`),t.setAttribute(`height`,`13`),t.setAttribute(`fill`,`none`),t.setAttribute(`stroke`,`currentColor`),t.setAttribute(`stroke-width`,`1.8`),t.setAttribute(`aria-hidden`,`true`);let n=document.createElementNS(`http://www.w3.org/2000/svg`,`path`);return n.setAttribute(`d`,xO[e]??`M4 4h16v16H4z`),t.appendChild(n),t.classList.add(`asset-icon-${e}`),t}function wO(e,t){document.querySelector(`.balloon`)?.remove();let n=SO[t];if(!n)return;let r=document.createElement(`div`);r.className=`balloon floating`;let i=document.createElement(`div`);i.className=`balloon-title`,i.textContent=t;let a=document.createElement(`span`);a.textContent=$D(n),r.append(i,a),document.body.appendChild(r);let o=e.getBoundingClientRect();r.style.left=`${o.right+8}px`,r.style.top=`${Math.max(8,o.top-8)}px`;let s=()=>{r.remove(),document.removeEventListener(`pointerdown`,s,!0)};setTimeout(()=>document.addEventListener(`pointerdown`,s,!0),0)}function TO(e){let t=document.createElement(`div`);t.className=`field uid-line`;let n=document.createElement(`span`);n.textContent=`key`;let r=document.createElement(`div`);r.className=`uid-value`;let i=document.createElement(`code`);i.textContent=`$${e}`;let a=document.createElement(`button`);return a.type=`button`,a.className=`uid-copy`,a.title=`Copy key`,a.innerHTML=`<svg aria-hidden="true" width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>`,a.addEventListener(`click`,()=>{navigator.clipboard?.writeText(`$${e}`),a.classList.add(`copied`),setTimeout(()=>a.classList.remove(`copied`),600)}),r.append(i,a),t.append(n,r),t}function EO(e,t){let n=t.includes(`/`)?t.slice(0,t.lastIndexOf(`/`)):``,r=document.createElement(`select`),i=new Set([``]);for(let t of Object.keys(e.working.assets??{})){let e=t.split(`/`);for(let t=1;t<e.length;t++)i.add(e.slice(0,t).join(`/`))}for(let t of e.pendingGroups)i.add(t);for(let e of[...i].sort()){let t=document.createElement(`option`);t.value=e,t.textContent=e===``?`(root)`:`${e}/`,e===n&&(t.selected=!0),r.appendChild(t)}r.addEventListener(`change`,()=>{e.moveAsset(t,r.value)});let a=document.createElement(`label`);a.className=`field`;let o=document.createElement(`span`);return o.textContent=`group`,a.append(o,r),a}function DO(e){e&&(e.addEventListener(`keydown`,e=>{e.key===`/`&&e.preventDefault()}),e.addEventListener(`input`,()=>{e.value.includes(`/`)&&(e.value=e.value.replace(/\//g,``))}))}function OO(e){let t=document.createElement(`div`);t.className=`section-title`,t.textContent=e;let n=document.createElement(`span`);return n.className=`rule`,t.appendChild(n),t}var kO=window.parent!==window,AO=new URLSearchParams(window.location.search).get(`parentOrigin`),jO=!1;function MO(e){if(kO){if(!AO){jO||(jO=!0,console.warn(`incanto-editor: embedded without ?parentOrigin=<origin> — postMessage disabled`));return}window.parent.postMessage(e,AO)}}var NO={ready(e,t,n){MO({type:`incanto-editor:ready`,input:e,output:t,version:n})},open(e,t){MO({type:`incanto-editor:open`,input:e,output:t})},change(e){MO({type:`incanto-editor:change`,dirty:e})},save(e,t,n){MO({type:`incanto-editor:save`,input:e,output:t,data:n})},error(e){MO({type:`incanto-editor:error`,message:e})}},PO=[[`Core`,e=>!e.endsWith(`2D`)&&!e.endsWith(`3D`)&&e!==`Label`&&e!==`UILayer`&&e!==`NetworkSpawner`],[`2D`,e=>e.endsWith(`2D`)&&!/Body|Area|Controller/.test(e)||e===`Label`||e===`UILayer`],[`2D Physics`,e=>e.endsWith(`2D`)&&/Body|Area|Controller/.test(e)],[`3D`,e=>e.endsWith(`3D`)&&!/Body|Area|Controller/.test(e)],[`3D Physics`,e=>e.endsWith(`3D`)&&/Body|Area|Controller/.test(e)],[`Network`,e=>e===`NetworkSpawner`]],FO=[...PO,[`Other`,e=>PO.every(([,t])=>!t(e))]],IO={title:{en:`How Incanto scenes work`,ko:`Incanto 씬은 어떻게 동작하나`},sections:[{heading:{en:`Everything is a node in a tree`,ko:`모든 것은 트리 위의 노드`},text:{en:`A scene is ONE tree of typed nodes (Godot-style). Each node has a name (unique among its siblings), a type that defines its props and behavior, optional children, and an optional scene-wide-unique uid. The whole structure lives in a *.scene.json file — there is no hidden state: what you see in this editor IS the file.`,ko:`씬은 타입을 가진 노드들의 단일 트리입니다(Godot 방식). 각 노드는 이름(형제 간 유일), props와 동작을 결정하는 타입, 선택적 자식들, 그리고 선택적인 씬 전역 유일 uid를 가집니다. 전체 구조가 *.scene.json 파일 하나에 들어있고 숨겨진 상태는 없습니다 — 이 에디터에서 보는 것이 곧 파일 그 자체입니다.`}},{heading:{en:`Props are delta-only`,ko:`Props는 변경분만 기록`},text:{en:`Every prop has a default defined by the engine. The JSON only stores values that DIFFER from the default — so files stay small and diffs stay meaningful. This editor follows the same rule: set a value back to its default and it disappears from the file.`,ko:`모든 prop에는 엔진이 정의한 기본값이 있습니다. JSON에는 기본값과 다른 값만 기록되어 파일이 작고 diff가 의미를 가집니다. 이 에디터도 같은 규칙을 따릅니다 — 값을 기본값으로 되돌리면 파일에서 사라집니다.`}},{heading:{en:`Addressing: paths, names, uid, groups`,ko:`노드 찾기: 경로·이름·uid·그룹`},text:{en:`Code reaches nodes four ways: a path like "Player/Skin" (relative) or "%Unique" (marked-unique name), getNodesByName("Enemy") which returns a LIST (names repeat across the tree), getNodeByUid("n_x1y2") which returns exactly one node and survives moves/renames, and groups — free tags for queries like "every coin".`,ko:`코드는 네 가지 방법으로 노드에 접근합니다: "Player/Skin" 같은 경로(상대) 또는 "%Unique"(유일 표시 이름), 트리 전체에서 같은 이름을 모두 찾는 getNodesByName("Enemy") — 이름은 중복될 수 있어 리스트가 돌아옵니다 —, 정확히 한 노드를 돌려주고 이동/개명에도 살아남는 getNodeByUid("n_x1y2"), 그리고 "모든 코인"처럼 묶어 조회하는 자유 태그 groups.`}},{heading:{en:`Signals connect, behaviors act`,ko:`시그널로 잇고 비헤이비어로 움직인다`},text:{en:`Nodes emit signals (an Area2D fires triggerEnter, a Timer fires timeout). The scene's "connections" wire a signal to a handler — declaratively, in JSON. Game logic itself is a Behavior: a TypeScript class in YOUR game, attached by name via "script". The editor shows and edits these links but the code lives in the game.`,ko:`노드는 시그널을 발산합니다(Area2D의 triggerEnter, Timer의 timeout). 씬의 "connections"가 시그널을 핸들러에 선언적으로(JSON으로) 연결합니다. 게임 로직 자체는 Behavior — 게임 쪽 TypeScript 클래스이며 "script"에 이름으로 연결됩니다. 에디터는 이 연결을 보여주고 편집하지만 코드는 게임에 있습니다.`}},{heading:{en:`Assets are declared, then referenced`,ko:`에셋은 선언하고 $키로 참조`},text:{en:`The scene header declares assets (textures, spritesheets, 3D models, animation clips) under a key; nodes reference them as "$key". Animations are data too: an {type:"animation"} asset loads GLB clips into memory and any ModelInstance3D can play them — Mixamo clips retarget onto VRM avatars automatically.`,ko:`씬 헤더에서 에셋(텍스처·스프라이트시트·3D 모델·애니메이션 클립)을 키로 선언하고, 노드는 "$key"로 참조합니다. 애니메이션도 데이터입니다 — {type:"animation"} 에셋이 GLB 클립을 메모리에 올리고 어떤 ModelInstance3D든 재생할 수 있으며, Mixamo 클립은 VRM 아바타에 자동 리타게팅됩니다.`}}]},Z=(e,t,...n)=>({type:e,summary:t,body:n}),LO=[{id:`core`,label:{en:`Core`,ko:`Core`},intro:{en:`Dimension-free building blocks: plain containers and the game clock. They render nothing themselves.`,ko:`차원과 무관한 기본 블록 — 순수 컨테이너와 게임 시계입니다. 스스로는 아무것도 그리지 않습니다.`},nodes:[Z(`Node`,{en:`A plain container with no transform.`,ko:`변환 없이 자식을 묶는 순수 컨테이너.`},{en:`Use it to group related nodes (all coins, all UI) without affecting their positions. Lifecycle, signals, groups, script — everything works; it just has no visual or spatial meaning of its own.`,ko:`위치에 영향을 주지 않으면서 관련 노드(코인 전부, UI 전부)를 묶을 때 씁니다. 라이프사이클·시그널·그룹·스크립트가 모두 동작하며, 시각적·공간적 의미만 없습니다.`}),Z(`Timer`,{en:"A serializable countdown that emits `timeout`.",ko:"`timeout` 시그널을 쏘는 직렬화 가능한 카운트다운."},{en:`Set waitTime (seconds), optionally autostart or oneShot, and connect its timeout signal to any handler. Because it is a node, the whole timing setup lives in the scene file — agents can read and tune it.`,ko:`waitTime(초)을 정하고 autostart·oneShot을 선택한 뒤 timeout 시그널을 핸들러에 연결하세요. 노드이기 때문에 타이밍 설정 전체가 씬 파일에 남아 에이전트가 읽고 조정할 수 있습니다.`}),Z(`AudioPlayer`,{en:`Plays a sound: zero-asset procedural SFX, BGM loops, or one-shot effects.`,ko:`소리 재생 — 무에셋 절차적 효과음, 배경음 루프, 단발 효과음.`},{en:`Set preset (coin/jump/hurt/explosion/…) for an instant zero-asset SFX (pitch/seed vary it), or leave it "custom" and set src (URL or "$assetKey"). volume 0..1; bus routes through engine.audio (sfx|music) for global volume/mute; loop for music; autoplay starts on ready (browsers may hold it until the first user gesture). Call play()/stop() from game code. Dimension-free — 2D and 3D alike.`,ko:`preset(coin/jump/hurt/explosion/…)을 설정하면 즉시 무에셋 효과음이 납니다(pitch·seed로 변형). 아니면 "custom"으로 두고 src(URL 또는 "$에셋키")를 쓰세요. volume은 0..1, bus는 engine.audio(sfx|music)를 통해 전역 볼륨/음소거에 연결, 음악은 loop, autoplay는 준비되면 재생합니다(브라우저가 첫 입력까지 보류 가능). 게임 코드에서 play()/stop()을 부르세요. 2D·3D 어디서나 동작합니다.`})]},{id:`2d`,label:{en:`2D`,ko:`2D`},intro:{en:`The 2D world is y-down pixels: 1 unit = 1 px, (0,0) top-left, rotation in clockwise degrees. Rendering needs a current Camera2D.`,ko:`2D 세계는 y-아래 픽셀 좌표입니다: 1유닛=1px, (0,0)은 좌상단, 회전은 시계방향 도(degree). 렌더링에는 current 카메라(Camera2D)가 필요합니다.`},nodes:[Z(`Node2D`,{en:`The 2D transform container (position/rotation/scale).`,ko:`2D 변환 컨테이너(position/rotation/scale).`},{en:`Children inherit its transform — move the parent, everything follows. renderOrder orders drawing; visible hides the subtree.`,ko:`자식은 변환을 상속합니다 — 부모를 움직이면 전부 따라옵니다. renderOrder로 그리기 순서를, visible로 서브트리 표시를 제어합니다.`}),Z(`Sprite2D`,{en:`A textured quad.`,ko:`텍스처 사각형(스프라이트).`},{en:`texture is "$assetKey" from the scene assets. anchor [0.5,0.5] centers; flipX/flipY mirror; tint multiplies color; opacity fades. Size comes from the texture × scale.`,ko:`texture는 씬 에셋의 "$키"입니다. anchor [0.5,0.5]면 중앙 기준, flipX/flipY로 반전, tint로 색 곱, opacity로 투명도. 크기는 텍스처 × scale로 정해집니다.`}),Z(`ColorRect2D`,{en:`A flat colored rectangle — no texture needed.`,ko:`단색 사각형 — 텍스처 불필요.`},{en:`size [w,h] px, color, opacity, anchor. Backgrounds, platforms, walls, UI panels, generated-level tiles (maze2d/dungeon2d emit these) — blocky art without any asset.`,ko:`size [w,h] px, color, opacity, anchor. 배경·플랫폼·벽·UI 패널·생성 레벨 타일(maze2d/dungeon2d가 이걸 만듭니다) — 에셋 없이 만드는 블록 그래픽입니다.`}),Z(`AnimatedSprite2D`,{en:`Spritesheet animation player.`,ko:`스프라이트시트 애니메이션 플레이어.`},{en:`Point sheet at a "$sheet" asset, define animations as named frame ranges with fps/loop in JSON, set autoplay. Emits animationFinished(name) for one-shot chains.`,ko:`sheet에 "$시트" 에셋을 연결하고, JSON에 fps/loop를 가진 이름별 프레임 구간으로 animations를 정의한 뒤 autoplay를 지정하세요. 단발 애니메이션 연결을 위해 animationFinished(name)를 발산합니다.`}),Z(`Camera2D`,{en:`The 2D view: follow, zoom, limits.`,ko:`2D 시점 — 추적·줌·경계.`},{en:`position is the view CENTER. follow tracks a node path with smoothing (0..1, higher = snappier). limits [minX,minY,maxX,maxY] clamps the view inside a world region. current: true makes it THE camera.`,ko:`position은 화면의 중심입니다. follow가 노드 경로를 smoothing(0..1, 높을수록 빠릿)으로 추적합니다. limits [minX,minY,maxX,maxY]가 시점을 월드 영역 안에 가둡니다. current: true인 카메라가 실제 시점이 됩니다.`}),Z(`Particles2D`,{en:`A 2D particle emitter (fire, sparks, smoke…).`,ko:`2D 파티클 이미터(불·스파크·연기…).`},{en:`Start from a preset (fire/smoke/sparks/…) then override any prop: rate, lifetime/speed ranges, direction + spread, gravity, size/color/alpha start→end, additive blend. burst > 0 with emitting: false makes a one-shot that emits finished. Animates LIVE in this editor.`,ko:`preset(fire/smoke/sparks/…)에서 시작해 어떤 prop이든 덮어쓰세요: rate, lifetime/speed 구간, direction+spread, gravity, size/color/alpha 시작→끝, additive blend. emitting: false에 burst > 0이면 단발 발사 후 finished를 발산합니다. 이 에디터에서 라이브로 움직입니다.`}),Z(`Label`,{en:`Text rendered to a quad.`,ko:`텍스트를 그리는 노드.`},{en:`text/fontSize/color/font/align — rendered via CanvasTexture, so any system font string works. Inside a UILayer it pins to the screen (HUD).`,ko:`text/fontSize/color/font/align — CanvasTexture로 그려져 시스템 폰트 문자열을 그대로 쓸 수 있습니다. UILayer 아래에 두면 화면에 고정됩니다(HUD).`}),Z(`UILayer`,{en:`A screen-space subtree (HUD).`,ko:`화면 고정 서브트리(HUD).`},{en:`Everything under it ignores the camera: positions are screen pixels from the top-left. Score counters, prompts, menus go here.`,ko:`이 아래의 모든 것은 카메라를 무시합니다 — 좌상단 기준 화면 픽셀 좌표입니다. 점수, 안내 문구, 메뉴를 여기에 둡니다.`})]},{id:`2d-physics`,label:{en:`2D Physics`,ko:`2D 물리`},intro:{en:`Rapier-backed. Colliders are PROPS ({shape, …}) — the green dashed wireframes in this editor. Physics runs when the game calls enablePhysics2D (and in play mode). Gravity lives on the scene row.`,ko:`Rapier 기반입니다. 콜라이더는 prop({shape, …})이며 에디터의 초록 점선이 그것입니다. 물리는 게임이 enablePhysics2D를 부를 때(그리고 플레이 모드에서) 돌고, 중력은 씬 행에 있습니다.`},nodes:[Z(`StaticBody2D`,{en:`Immovable collision: ground, walls, platforms.`,ko:`움직이지 않는 충돌체 — 바닥·벽·플랫폼.`},{en:`Other bodies collide with it; it never moves. Cheapest body type — use it for all level geometry.`,ko:`다른 바디가 부딪히지만 자신은 절대 움직이지 않습니다. 가장 저렴한 바디 — 레벨 지형 전부에 쓰세요.`}),Z(`RigidBody2D`,{en:`Fully simulated: falls, bounces, pushes.`,ko:`완전 시뮬레이션 — 떨어지고 튕기고 밀립니다.`},{en:`Gravity and collisions drive it. Set linearVelocity to launch. For player characters prefer CharacterBody2D (direct control).`,ko:`중력과 충돌이 움직임을 결정합니다. linearVelocity로 발사하세요. 플레이어 캐릭터에는 직접 제어가 가능한 CharacterBody2D를 권합니다.`}),Z(`CharacterBody2D`,{en:`Kinematic character: you set velocity, it slides.`,ko:`키네마틱 캐릭터 — 속도를 주면 미끄러지듯 이동.`},{en:`moveAndSlide() resolves collisions without physics pushing back; isOnFloor() gates jumps. Sensors (Area2D) do not block it but still fire triggers.`,ko:`moveAndSlide()가 밀려나지 않으면서 충돌을 처리하고 isOnFloor()로 점프를 판정합니다. 센서(Area2D)는 길을 막지 않으면서 트리거를 발사합니다.`}),Z(`Area2D`,{en:`A sensor: overlap triggers, no collision response.`,ko:`센서 — 겹침 감지만, 충돌 반응 없음.`},{en:`Fires triggerEnter(other)/triggerExit(other). Coins, checkpoints, damage zones. Filter with other.isInGroup("player").`,ko:`triggerEnter(other)/triggerExit(other)를 발산합니다. 코인·체크포인트·데미지 존에 쓰고, other.isInGroup("player")로 거릅니다.`}),Z(`CharacterController2D`,{en:`Zero-code platformer/top-down movement.`,ko:`코드 없는 플랫포머/탑다운 이동.`},{en:`Put it UNDER a CharacterBody2D. mode "platformer" (gravity + jump via jumpAction) or "topDown" (free 2-axis). Reads the scene input map actions (moveAction/jumpAction). maxSpeed/jumpHeight are intent-level numbers.`,ko:`CharacterBody2D의 자식으로 두세요. mode는 "platformer"(중력+jumpAction 점프) 또는 "topDown"(자유 2축)이며 씬 입력 맵의 액션(moveAction/jumpAction)을 읽습니다. maxSpeed/jumpHeight는 의도 그대로의 숫자입니다.`})]},{id:`3d`,label:{en:`3D`,ko:`3D`},intro:{en:`Meters, y-up, rotations in degrees. Standard materials need LIGHT — add a DirectionalLight3D or scene ambient, or you get silhouettes.`,ko:`미터 단위, y-위, 회전은 도(degree)입니다. 표준 머티리얼은 빛이 필요합니다 — DirectionalLight3D나 씬 ambient가 없으면 실루엣만 보입니다.`},nodes:[Z(`Node3D`,{en:`The 3D transform container.`,ko:`3D 변환 컨테이너.`},{en:`position [x,y,z] in meters, rotation [x,y,z] in degrees, scale per axis. Children inherit.`,ko:`position [x,y,z] 미터, rotation [x,y,z] 도, scale은 축별. 자식이 상속합니다.`}),Z(`MeshInstance3D`,{en:`A primitive mesh + material.`,ko:`프리미티브 메시 + 머티리얼.`},{en:`mesh: box/sphere/plane/cylinder…, size per primitive, material {color, roughness, metalness, emissive, emissiveIntensity, wireframe, map, normalMap, repeat} (this editor gives it swatches, sliders, texture-URL fields, and a repeat [u,v] tiling vector). map/normalMap are texture URLs; repeat only takes effect with a map. castShadow/receiveShadow per node.`,ko:`mesh: box/sphere/plane/cylinder…, 프리미티브별 size, material {color, roughness, metalness, emissive, emissiveIntensity, wireframe, map, normalMap, repeat}(에디터가 스와치·슬라이더·텍스처 URL 입력·repeat [u,v] 타일링 벡터 제공). map/normalMap은 텍스처 URL, repeat는 map이 있을 때만 적용. 그림자는 castShadow/receiveShadow.`}),Z(`ModelInstance3D`,{en:`A GLB/glTF/VRM model file.`,ko:`GLB/glTF/VRM 모델 파일.`},{en:'model: "$assetKey" or URL. targetHeight scales the model to stand N units tall (run `npx incanto-model file.glb` to read its real size first). animation plays an embedded clip name OR an "$animation" asset — Mixamo clips retarget onto VRM humanoids automatically. One node per VRM asset (the avatar runtime mounts live).',ko:'model은 "$에셋키" 또는 URL입니다. targetHeight가 모델을 N유닛 높이로 맞춥니다(먼저 `npx incanto-model 파일.glb`로 실제 크기를 확인하세요). animation은 내장 클립 이름 또는 "$애니메이션" 에셋을 재생하며, Mixamo 클립은 VRM 휴머노이드에 자동 리타게팅됩니다. VRM 에셋은 노드 하나만 쓸 수 있습니다(아바타 런타임이 라이브로 마운트됨).'}),Z(`Sprite3D`,{en:`A 2D image as a camera-facing billboard (the 2.5D look).`,ko:`카메라를 향하는 2D 빌보드 스프라이트(2.5D 룩).`},{en:`A textured quad that turns to face the camera inside the 3D world (Octopath / Don't Starve / MapleStory-in-3D). texture is an image URL; size is [w,h] in METERS. billboard: "y" stays upright and yaws toward the camera (characters/props), "full" faces it completely (items/FX), "none" is fixed to the node rotation. anchor [0.5,0] plants the feet on the ground. pixelArt nearest-filters for crisp pixels; alphaTest is a hard cutout that depth-sorts against 3D geometry (a tree occludes it); flipX mirrors left/right; tint/opacity recolor.`,ko:`3D 월드 안에서 카메라를 향해 도는 텍스처 쿼드(Octopath/Don't Starve, 3D 속 메이플스토리 룩). texture는 이미지 URL, size는 [너비,높이] 미터. billboard: "y"는 세로로 선 채 카메라로 yaw(캐릭터/사물), "full"은 완전히 향함(아이템/FX), "none"은 노드 회전에 고정. anchor [0.5,0]은 발을 바닥에 둡니다. pixelArt는 nearest 필터로 픽셀을 또렷하게, alphaTest는 3D 지형과 깊이정렬되는 하드 컷아웃(나무가 가림), flipX는 좌우 반전, tint/opacity로 색·투명도.`}),Z(`AnimatedSprite3D`,{en:`A billboard sprite that plays spritesheet animations.`,ko:`스프라이트시트 애니를 재생하는 빌보드 스프라이트.`},{en:`Everything Sprite3D does, plus frame animation. sheet is a spritesheet image URL whose cells are frameWidth×frameHeight; animations maps a name to {frames, fps, loop} where frames is an inclusive [start,end] range or an explicit list; autoplay starts one on load. Game code calls play("walk") / stop(); a non-looping clip clamps on its last frame and emits animationFinished(name) — chain attack→idle from it.`,ko:`Sprite3D의 모든 기능 + 프레임 애니. sheet는 frameWidth×frameHeight 칸을 가진 스프라이트시트 URL, animations는 이름→{frames, fps, loop}(frames는 포함 [시작,끝] 범위 또는 명시 목록), autoplay는 로드 시 하나를 시작. 게임 코드에서 play("walk")/stop() 호출, 비반복 클립은 마지막 프레임에 머물며 animationFinished(name)를 emit(attack→idle 연결).`}),Z(`Camera3D`,{en:`The 3D view.`,ko:`3D 시점.`},{en:`Perspective camera; fov in degrees; current: true selects it. Use the editor's "game cam" (0) to preview exactly what it renders.`,ko:`원근 카메라이며 fov는 도 단위, current: true가 실제 시점이 됩니다. 에디터의 "game cam"(0)으로 정확한 렌더 결과를 미리 보세요.`}),Z(`DirectionalLight3D`,{en:`Sun-like light from a direction.`,ko:`태양광 — 방향에서 평행하게.`},{en:`Position sets the direction toward the origin. intensity/color; pair with scene ambient for soft fill.`,ko:`위치가 원점을 향한 방향을 정합니다. intensity/color를 조절하고 부드러운 채움광은 씬 ambient와 함께 쓰세요.`}),Z(`OmniLight3D`,{en:`A point light radiating everywhere.`,ko:`점광 — 사방으로 퍼지는 빛.`},{en:`Lamps, torches, glows. range limits reach; intensity/color shape the falloff.`,ko:`램프·횃불·발광체. range로 도달 거리를, intensity/color로 감쇠를 만듭니다.`}),Z(`Particles3D`,{en:`A 3D particle emitter (fire, magic, weather…).`,ko:`3D 파티클 이미터(불·마법·날씨…).`},{en:`The same preset + override model as Particles2D, in meters with [x,y,z] gravity. Camera-facing billboard quads, instanced — hundreds are cheap. Animates LIVE in this editor.`,ko:`Particles2D와 같은 preset+덮어쓰기 모델을 미터 단위와 [x,y,z] 중력으로. 카메라를 향하는 빌보드 쿼드를 인스턴싱해 수백 개도 가볍습니다. 이 에디터에서 라이브로 움직입니다.`}),Z(`Terrain3D`,{en:`Procedural heightfield terrain with biome texture splatting.`,ko:`바이옴 텍스처 스플래팅이 적용된 절차적 하이트필드 지형.`},{en:`Seeded simplex hills displaced once on the CPU; theme (island/alpine/plains/desert/custom) picks the 4 blended biome textures by height and slope. heightAt(x,z) answers the surface height anywhere. For physics, parent it under a StaticBody3D with collider {shape:'heightfield'}.`,ko:`시드 기반 심플렉스 언덕을 CPU에서 한 번 변위합니다. theme(island/alpine/plains/desert/custom)이 높이·경사로 블렌딩되는 바이옴 텍스처 4장을 고릅니다. heightAt(x,z)로 어디서든 지표 높이를 얻고, 물리는 StaticBody3D 아래에 두고 collider {shape:'heightfield'}를 쓰세요.`}),Z(`Water3D`,{en:`A shader-water surface with splash signals.`,ko:`스플래시 신호를 내는 셰이더 물 표면.`},{en:`size [w,d] meters on XZ. quality 'fancy' (default) = FBM wave shader with a trough/surface/peak ramp (colors), CubeCamera reflections (reflection/reflectionInterval) and opt-in shoreline foam; 'simple' = the cheap sine material (color). Bodies crossing the surface emit entered/exited(body) and raise ripples (interaction). Volumetric gameplay still wants an Area3D. Waves animate LIVE in this editor.`,ko:`XZ 평면에 size [w,d] 미터. quality 'fancy'(기본)는 FBM 파도 셰이더 — trough/surface/peak 램프(colors), CubeCamera 반사(reflection/reflectionInterval), 옵트인 해안 거품(foam). 'simple'은 저사양용 사인파 머티리얼(color). 바디가 수면을 지나면 entered/exited(body) 신호와 물결이 일어납니다(interaction). 부피 기반 게임플레이엔 여전히 Area3D를 쓰세요. 이 에디터에서 파도가 라이브로 움직입니다.`}),Z(`Foliage3D`,{en:`An instanced grass/flower carpet that sways.`,ko:`바람에 흔들리는 인스턴스 풀밭/꽃밭.`},{en:`kind grass/flowers/reeds scattered over area [w,d] at density (capped by maxInstances — instanced, tens of thousands of blades cheap). Grass defaults to style 'mesh': every instance is a REAL tapered blade curved by a bezier vertex shader — Voronoi-clump hue/lean, groundColor soil roots (match the terrain), sunDirection tip sheen, 2-octave rolling wind, fadeStart/fadeEnd camera LOD, and (interaction) blade-bending around moving bodies. style 'blades' keeps the 8-SDF-blades-per-quad ported shader, 'simple' the legacy quads. colorA/colorB tint bottom→top, sway sets the wind, seed makes the scatter reproducible.`,ko:`kind grass/flowers/reeds를 area [w,d]에 density로 흩뿌립니다(maxInstances 상한 — 인스턴싱이라 수만 가닥도 가벼움). grass는 기본 style 'mesh': 인스턴스 하나하나가 베지어 버텍스 셰이더로 휘어지는 진짜 잎 메시 — 보로노이 클럼프 색/기울기, groundColor 흙빛 뿌리(지형 색에 맞추세요), sunDirection 잎끝 광택, 2옥타브 굽이치는 바람, fadeStart/fadeEnd 카메라 LOD, (interaction) 움직이는 바디 주변 풀 눕힘까지. style 'blades'는 쿼드당 8가닥 SDF 셰이더, 'simple'은 기존 쿼드 룩. colorA/colorB가 아래→위 색, sway가 바람 세기, seed가 배치를 재현 가능하게 합니다.`}),Z(`Flowers3D`,{en:`Instanced flower PLANTS — stems, leaves, multi-petal heads.`,ko:`인스턴스 꽃밭 — 줄기·잎·여러 장 꽃잎의 진짜 꽃 식물.`},{en:`Real procedural flower plants (curved stem, 2-3 leaves, a 5-8 petal head around a contrasting center disc, 1-3 blooms per plant) scattered over area [w,d]. density is the vibe dial: 'lush' / 'sparse' (default) / 'none', or a number in plants/m². varieties picks a subset of daisy/cosmos/bellflower ([] = all three); palette sets the head colors ([] = white/yellow/violet). clustering 0-1 gathers plants into Voronoi patches that bloom one species + color together; sway bobs the heads in a gentle wind; seed makes the field reproducible. ≤3 varieties → ≤6 draw calls.`,ko:`진짜 프로시저럴 꽃 식물(휘어진 줄기, 잎 2-3장, 대비되는 중심 원반을 두른 꽃잎 5-8장 머리, 포기당 꽃 1-3송이)을 area [w,d]에 흩뿌립니다. density가 바이브 다이얼: 'lush'(풍성하게) / 'sparse'(듬성듬성, 기본) / 'none'(없게) 또는 m²당 개수. varieties는 daisy/cosmos/bellflower 부분집합([] = 셋 다), palette는 꽃 색([] = 흰/노랑/보라). clustering 0-1이 보로노이 패치로 모아 한 패치가 같은 종·같은 색으로 피고, sway가 바람에 머리를 끄덕이며, seed로 배치가 재현됩니다. 품종 ≤3 → 드로우 콜 ≤6.`}),Z(`Tree3D`,{en:`Procedural ez-tree groves — branchy trunks, textured leaves.`,ko:`프로시저럴 ez-tree 나무 — 가지 달린 줄기와 텍스처 잎까지.`},{en:`type conifer/broadleaf/dead picks the recipe family; tier is the cost dial: simple = primitive low-poly, medium = light forest presets (≤3k tris/tree), high = full ez-tree presets (8–20k, hero trees). count > 1 scatters a forest patch over area [w,d] — up to 3 seed variants, each branches + leaves InstancedMesh (≤6 draw calls); count × tris/tree is budget-checked at load. seed makes every branch and leaf reproducible; height jitters ±20% per instance; leaves sway in a simplex wind.`,ko:`type conifer/broadleaf/dead가 수종을, tier가 비용을 정합니다: simple = 기존 로우폴리, medium = 가벼운 forest 프리셋(나무당 ≤3k tris), high = 풀 ez-tree 프리셋(8–20k, 주인공 나무). count > 1이면 area [w,d]에 숲 패치를 흩뿌립니다 — 시드 변형 최대 3종, 변형마다 가지+잎 InstancedMesh(드로우 콜 ≤6), count × tris는 로드 시 예산 검사. seed로 가지와 잎이 전부 재현되고 height는 인스턴스마다 ±20% 지터링, 잎은 심플렉스 바람에 흔들립니다.`}),Z(`VoxelGrid3D`,{en:`A Minecraft-style block grid in ONE node.`,ko:`마인크래프트식 블록 그리드 — 노드 하나로.`},{en:`voxels is a list of [x,y,z,palette] integer cells (terrain/island generators emit these). Greedy-meshed and instanced — large worlds stay one draw batch. Game code edits blocks via setBlock/getBlock; emits blocksChanged.`,ko:`voxels는 [x,y,z,팔레트] 정수 셀 목록입니다(terrain/island 생성기가 만들어 냅니다). 그리디 메싱+인스턴싱으로 큰 월드도 드로우 배치 하나를 유지합니다. 게임 코드는 setBlock/getBlock으로 수정하고 blocksChanged가 발산됩니다.`})]},{id:`3d-physics`,label:{en:`3D Physics`,ko:`3D 물리`},intro:{en:`Same model as 2D, in meters with y-up gravity ([0,-9.81,0] default). Colliders: box {size}, sphere {radius}, capsule {radius,height}.`,ko:`2D와 같은 모델을 미터·y-위 중력([0,-9.81,0] 기본)으로. 콜라이더는 box {size}, sphere {radius}, capsule {radius,height}.`},nodes:[Z(`StaticBody3D`,{en:`Immovable 3D collision.`,ko:`움직이지 않는 3D 충돌체.`},{en:`Floors, walls, level geometry.`,ko:`바닥·벽·레벨 지형.`}),Z(`RigidBody3D`,{en:`Simulated 3D body.`,ko:`시뮬레이션되는 3D 바디.`},{en:`Crates, balls, debris — gravity and impacts drive it.`,ko:`상자·공·파편 — 중력과 충격이 움직입니다.`}),Z(`CharacterBody3D`,{en:`Kinematic 3D character.`,ko:`키네마틱 3D 캐릭터.`},{en:`moveAndSlide with up = +y; isOnFloor for jumps.`,ko:`+y를 위로 moveAndSlide, 점프 판정은 isOnFloor.`}),Z(`Area3D`,{en:`3D overlap sensor.`,ko:`3D 겹침 센서.`},{en:`triggerEnter/Exit — pickups, zones, goals.`,ko:`triggerEnter/Exit — 아이템·존·골인 지점.`}),Z(`CharacterController3D`,{en:`Zero-code 3D movement + camera rig.`,ko:`코드 없는 3D 이동 + 카메라 리그.`},{en:`Put it UNDER a CharacterBody3D. view: thirdPerson (orbit + zoom), firstPerson (eyeHeight, mouseLook), sideView or flightView. Reads moveAction/jumpAction/sprintAction from the scene input map; skinPath turns the visual child to face travel. Intent-level numbers: maxSpeed, jumpVelocity, camDistance.`,ko:`CharacterBody3D의 자식으로 두세요. view는 thirdPerson(궤도+줌), firstPerson(eyeHeight, mouseLook), sideView, flightView 중 하나입니다. 씬 입력 맵의 moveAction/jumpAction/sprintAction을 읽고 skinPath의 비주얼 자식을 진행 방향으로 돌립니다. maxSpeed·jumpVelocity·camDistance 같은 의도 수준의 숫자만 만집니다.`})]},{id:`network`,label:{en:`Network`,ko:`네트워크`},intro:{en:`Multiplayer is transport-agnostic: the engine speaks one NetworkTransport interface (built-in offline Loopback + an @agent8/gameserver adapter). The scene declares replication; one owner node per player broadcasts its sync keys.`,ko:`멀티플레이어는 트랜스포트 불가지론입니다 — 엔진은 NetworkTransport 인터페이스 하나만 사용합니다(내장 오프라인 Loopback + @agent8/gameserver 어댑터). 복제는 씬이 선언하며, 플레이어당 하나의 owner 노드가 sync 키를 송출합니다.`},nodes:[Z(`NetworkSpawner`,{en:`Spawns a registered scene per remote player/entity.`,ko:`원격 플레이어/엔티티마다 등록된 씬을 생성.`},{en:`source "users" mirrors every OTHER account in the room (self skipped); "collection:<id>" mirrors a room collection. Replicated sync patches apply to each instance; position interpolates. Emits spawned/despawned.`,ko:`source "users"는 방의 다른 모든 계정을 미러링하고(자신 제외) "collection:<id>"는 방 컬렉션을 미러링합니다. 복제 sync 패치가 인스턴스에 적용되고 position은 보간됩니다. spawned/despawned를 발산합니다.`})]}],RO=`overview`,zO=null;function BO(){document.querySelector(`#docs`)?.removeAttribute(`hidden`),UO()}function VO(){document.querySelector(`#docs`)?.setAttribute(`hidden`,``)}function HO(){let e=document.querySelector(`#docs`);e&&(e.addEventListener(`pointerdown`,t=>{t.target===e&&VO()}),document.querySelector(`#docs-close`)?.addEventListener(`click`,VO))}function UO(){let e=document.querySelector(`#docs-tabs`),t=document.querySelector(`#docs-body`);if(!e||!t)return;e.textContent=``;let n=(t,n)=>{let r=document.createElement(`button`);r.type=`button`,r.className=`docs-tab${RO===t?` active`:``}`,r.textContent=n,r.addEventListener(`click`,()=>{RO=t,zO=null,UO()}),e.appendChild(r)};n(`overview`,$D({en:`Overview`,ko:`개요`}));for(let e of LO)n(e.id,$D(e.label));if(t.textContent=``,RO===`overview`){WO(t);return}let r=LO.find(e=>e.id===RO);r&&GO(t,r)}function WO(e){let t=document.createElement(`h2`);t.textContent=$D(IO.title),e.appendChild(t);for(let t of IO.sections){let n=document.createElement(`h3`);n.textContent=$D(t.heading);let r=document.createElement(`p`);r.textContent=$D(t.text),e.append(n,r)}}function GO(e,t){let n=document.createElement(`p`);n.className=`docs-intro`,n.textContent=$D(t.intro),e.appendChild(n);for(let n of t.nodes){let t=document.createElement(`div`);t.className=`docs-node${zO===n.type?` open`:``}`;let r=document.createElement(`button`);r.type=`button`,r.className=`docs-node-head`;let i=document.createElement(`span`);i.className=`tree-icon`,i.appendChild(nO(n.type));let a=document.createElement(`strong`);a.textContent=n.type;let o=document.createElement(`span`);if(o.className=`docs-summary`,o.textContent=$D(n.summary),r.append(i,a,o),r.addEventListener(`click`,()=>{zO=zO===n.type?null:n.type,UO()}),t.appendChild(r),zO===n.type){let e=document.createElement(`div`);e.className=`docs-detail`;for(let t of n.body){let n=document.createElement(`p`);n.textContent=$D(t),e.appendChild(n)}e.appendChild(KO(n.type)),t.appendChild(e)}e.appendChild(t)}}function KO(e){let t=document.createElement(`table`);t.className=`docs-props`;let n=document.createElement(`tr`);for(let e of[$D({en:`prop`,ko:`prop`}),$D({en:`default`,ko:`기본값`})]){let t=document.createElement(`th`);t.textContent=e,n.appendChild(t)}t.appendChild(n);try{let n=fe(e);for(let[e,r]of Object.entries(n)){let n=document.createElement(`tr`),i=document.createElement(`td`);i.className=`mono`,i.textContent=e;let a=document.createElement(`td`);a.className=`mono`,a.textContent=JSON.stringify(r.default),n.append(i,a),t.appendChild(n)}}catch{}return t}function Q(e){return Math.round(e*100)/100}function qO(e,t,n,r,i){return{name:e,type:`StaticBody3D`,props:{collider:{shape:`box`,size:t},position:n,...i?{rotation:i}:{}},children:[{name:`Skin`,type:`MeshInstance3D`,props:{mesh:`box`,size:t,...r}}]}}function JO(e,t,n,r,i,a){let o=Q(r.range(i[0],i[1])),s=Q(r.range(.5,.7));return{name:`Rock${e}`,type:`MeshInstance3D`,props:{mesh:`sphere`,size:[1,1,1],position:[t,Q(o*s*.6),n],rotation:[0,r.int(0,359),0],scale:[o,Q(o*s),Q(o*r.range(.8,1.1))],material:{color:a??YO(r),roughness:1},castShadow:!0}}}function YO(e){let t=Math.round(e.range(110,160)).toString(16).padStart(2,`0`);return`#${t}${t}${t}`}function XO(e){return[{name:`Sun`,type:`DirectionalLight3D`,props:{position:[Q(e*.8),Q(e*1.5),Q(e*.6)],intensity:1,castShadow:!0,shadowArea:Q(e*1.2)}},{name:`FillLight`,type:`DirectionalLight3D`,props:{position:[Q(-e*.8),Q(e*.8),Q(-e*.6)],intensity:.4,color:`#b9d4ff`}}]}function ZO(e,t){return e===void 0?t:typeof e==`number`?[e,e]:e}function QO(e,t,n){return Math.min(Math.max(e,t),n)}var $O=[`boxes`,`ruins`,`garden`],ek=.5,tk=.1,nk=2,rk={boxes:{floor:`#3f3f3f`,wall:`#55504a`,obstacles:[`#b0413e`,`#5b8266`,`#3e6990`,`#a26b38`,`#6d5a96`,`#878787`]},ruins:{floor:`#7d766b`,wall:`#8a8378`,obstacles:[`#8a8378`,`#979085`,`#a39a8d`,`#7b746a`]},garden:{floor:`#4d7c3a`,wall:`#2f6b2f`,obstacles:[`#2f6b2f`,`#3a7a38`,`#356e33`]}};function ik(e){let{seed:t,width:n=30,depth:r=30,wallHeight:i=3,obstacles:a=8,theme:o=`boxes`}=e;if(!$O.includes(o))throw new y(`BAD_FORMAT`,`generateArena theme must be one of [${$O.join(`, `)}], got '${o}'.`,{prop:`theme`,validOptions:[...$O]});let s=rk[o],c=new b(t),l=[qO(`Floor`,[n,tk,r],[0,-.1/2,0],{material:{color:s.floor,roughness:1},receiveShadow:!0})],u=i/2,d=[n+ek*2,i,ek],f=[ek,i,r],p={material:{color:s.wall,roughness:.9},receiveShadow:!0};l.push(qO(`Wall1`,d,[0,u,-(r+ek)/2],p),qO(`Wall2`,d,[0,u,(r+ek)/2],p),qO(`Wall3`,f,[-(n+ek)/2,u,0],p),qO(`Wall4`,f,[(n+ek)/2,u,0],p)),o===`ruins`?l.push(...ok(c,s,n,r,i,a)):l.push(...ak(c,s,n,r,i,a,o)),o===`garden`&&l.push(...sk(c,n,r));let m=Math.max(n,r);return l.push({name:`Sun`,type:`DirectionalLight3D`,props:{position:[Q(m*.8),Q(m*1.5),Q(m*.6)],intensity:1,castShadow:!0,shadowArea:Q(m*1.2)}},{name:`FillLight`,type:`DirectionalLight3D`,props:{position:[Q(-m*.8),Q(m*.8),Q(-m*.6)],intensity:.4,color:`#b9d4ff`}},{name:`Lamp`,type:`OmniLight3D`,props:{position:[0,Q(i+2),0],intensity:.5,color:`#fff3d6`,range:Q(m)}}),{name:`Arena`,type:`Node3D`,children:l}}function ak(e,t,n,r,i,a,o){let s=[],c=o===`garden`?Math.min(n,r)*.16:0;for(let o=1;o<=a;o++){let a=[Q(e.range(.8,2.6)),Q(e.range(.8,Math.max(1.2,i*.8))),Q(e.range(.8,2.6))],l=Q(e.range(-(n/2-nk),n/2-nk)),u=Q(e.range(-(r/2-nk),r/2-nk));if(c>0&&Math.hypot(l,u)<c+1.5){let t=Math.max(Math.hypot(l,u),.001);l=Q(l/t*(c+1.5+e.range(0,2))),u=Q(u/t*(c+1.5+e.range(0,2)))}let d=e.int(0,359);s.push(qO(`Obstacle${o}`,a,[l,Q(a[1]/2),u],{material:{color:e.pick(t.obstacles),roughness:.8},castShadow:!0},[0,d,0]))}return s}function ok(e,t,n,r,i,a){let o=[];if(a<=0)return o;let s=Math.max(1,Math.round(Math.sqrt(a/2))),c=Math.ceil(a/s),l=n-nk*2,u=r-nk*2,d=0;for(let n=0;n<s&&d<a;n++){let r=Q(s===1?0:-u/2+n/(s-1)*u);for(let n=0;n<c&&d<a;n++){d++;let a=Q((c===1?0:-l/2+n/(c-1)*l)+e.range(-.4,.4)),s=Q(e.next()>.35?e.range(i*.7,i*1.2):e.range(.4,.9)),u=Q(e.range(.8,1.2));o.push(qO(`Obstacle${d}`,[u,s,u],[a,Q(s/2),Q(r+e.range(-.4,.4))],{material:{color:e.pick(t.obstacles),roughness:.95},castShadow:!0},[0,e.int(-8,8),0]))}}return o}function sk(e,t,n){let r=[],i=Math.min(t,n)*.16,a=r=>{let a=Math.min(t,n)/2-r/2-1,o=Q(e.range(-a,a)),s=Q(e.range(-a,a)),c=Math.max(Math.hypot(o,s),.001);return c<i+r/2&&(o=Q(o/c*(i+r/2+.5)),s=Q(s/c*(i+r/2+.5))),[o,s]};for(let i=1;i<=3;i++){let o=Q(Math.min(t,n)*e.range(.18,.26)),[s,c]=a(o);r.push({name:`Grass${i}`,type:`Foliage3D`,props:{kind:`grass`,area:[o,o],density:10,seed:e.int(1,1e9),position:[s,0,c]}})}for(let i=1;i<=2;i++){let o=Q(Math.min(t,n)*e.range(.12,.18)),[s,c]=a(o);r.push({name:`FlowerBed${i}`,type:`Flowers3D`,props:{density:`lush`,clustering:.3,area:[o,o],seed:e.int(1,1e9),position:[s,0,c]}})}return r.push({name:`Pool`,type:`Water3D`,props:{size:[Q(i*2),Q(i*2)],position:[0,.3,0],waveHeight:.04}}),r}var ck=32,lk=[4,8],uk=`#332f3a`,dk=`#6b6357`;function fk(e){let{seed:t,rooms:n=5}=e,[r,i]=ZO(e.size,[960,720]),a=Math.max(8,Math.floor(r/ck)),o=Math.max(8,Math.floor(i/ck)),s=new b(t),c=[];for(let e=0;e<n*12&&c.length<n;e++){let e=s.int(lk[0],lk[1]),t=s.int(lk[0],lk[1]),n={x:s.int(1,Math.max(1,a-e-1)),y:s.int(1,Math.max(1,o-t-1)),w:e,h:t};c.some(e=>pk(e,n,1))||c.push(n)}let l=new Set,u=e=>{for(let t=e.y;t<e.y+e.h;t++)for(let n=e.x;n<e.x+e.w;n++)l.add(`${n},${t}`)};for(let e of c)u(e);let d=[];for(let e=1;e<c.length;e++){let[t,n]=mk(c[e-1]),[r,i]=mk(c[e]),a={x:Math.min(t,r),y:n,w:Math.abs(t-r)+1,h:1},o={x:r,y:Math.min(n,i),w:1,h:Math.abs(n-i)+1};for(let[t,n]of[[`H`,a],[`V`,o]])u(n),(n.w>1||n.h>1)&&d.push({name:`Corridor${e}${t}`,rect:n})}let f=new Set;for(let e of l){let[t,n]=e.split(`,`).map(Number);for(let e=-1;e<=1;e++)for(let r=-1;r<=1;r++){let i=`${t+r},${n+e}`;l.has(i)||f.add(i)}}let p=-(a*ck)/2,m=-(o*ck)/2,h=(e,t,n)=>({name:e,type:`ColorRect2D`,props:{position:[p+(t.x+t.w/2)*ck,m+(t.y+t.h/2)*ck],size:[t.w*ck,t.h*ck],color:n}}),g=c.map((e,t)=>h(`Room${t+1}`,e,uk));for(let e of d)g.push(h(e.name,e.rect,uk));let _=0;for(let e=-1;e<=o;e++){let t=-1;for(;t<=a;){if(!f.has(`${t},${e}`)){t++;continue}let n=1;for(;t+n<=a&&f.has(`${t+n},${e}`);)n++;_++;let r=[n*ck,ck];g.push({name:`Wall${_}`,type:`StaticBody2D`,props:{position:[p+(t+n/2)*ck,m+(e+.5)*ck],collider:{shape:`rect`,size:r}},children:[{name:`Skin`,type:`ColorRect2D`,props:{size:r,color:dk}}]}),t+=n}}return{name:`Dungeon`,type:`Node2D`,children:g}}function pk(e,t,n){return e.x-n<t.x+t.w&&e.x+e.w+n>t.x&&e.y-n<t.y+t.h&&e.y+e.h+n>t.y}function mk(e){return[Math.floor(e.x+e.w/2),Math.floor(e.y+e.h/2)]}var hk=[[0,-1],[1,0],[0,1],[-1,0]];function gk(e,t,n){let r=2*t+1,i=2*n+1,a=Array.from({length:i},()=>Array(r).fill(!1)),o=(e,t)=>{a[t][e]=!0};o(1,1);let s=new Set([`0,0`]),c=[[0,0]];for(;c.length>0;){let[r,i]=c[c.length-1],a=[];for(let[e,o]of hk){let c=r+e,l=i+o;c>=0&&c<t&&l>=0&&l<n&&!s.has(`${c},${l}`)&&a.push([c,l])}if(a.length===0){c.pop();continue}let[l,u]=e.pick(a);s.add(`${l},${u}`),o(2*l+1,2*u+1),o(r+l+1,i+u+1),c.push([l,u])}return o(0,1),o(2*t,2*n-1),{cols:t,rows:n,cells:a}}var _k=[`stone`,`hedge`,`canyon`],vk=.1,yk=`https://agent8-games.verse8.io/assets/3D/default/textures/wall`,bk=`https://agent8-games.verse8.io/assets/3D/default/textures/terrain`,xk={stone:{wall:{color:`#e8e2d8`,map:`${yk}/blocks.png`,normalMap:`${yk}/blocks_normal.png`,tile:2,roughness:.95},floor:{color:`#99938a`,map:`${bk}/stone.png`,normalMap:`${bk}/stone_normal.png`,tile:3,roughness:1},cap:`#6b5848`,pillar:`#cfc8bb`,path:`#665f55`,sun:{color:`#c9d6ea`,intensity:.75,height:.5},fill:`#9fb4cc`,mood:{sky:{elevationDeg:10,azimuthDeg:150,turbidity:16,rayleigh:3.2},fog:{near:.5,far:3.5,color:`#86909c`},exposure:.82,ambient:{color:`#c9d4e2`,intensity:.12}}},hedge:{wall:{color:`#55a83e`,map:`${bk}/grass.png`,normalMap:`${bk}/grass_normal.png`,tile:1.4,roughness:1},floor:{color:`#86b06d`,map:`${bk}/grass.png`,normalMap:`${bk}/grass_normal.png`,tile:3,roughness:1},pillar:`#3d7531`,path:`#7d6845`,sun:{color:`#e9eee6`,intensity:.7,height:1},fill:`#b9c8b4`,mood:{sky:{elevationDeg:35,azimuthDeg:150,turbidity:18,rayleigh:4.2},fog:{near:.8,far:5,color:`#aab8a6`},exposure:.88,ambient:{color:`#dde5d8`,intensity:.15}}},canyon:{wall:{color:`#f0b070`,map:`${bk}/stone.png`,normalMap:`${bk}/stone_normal.png`,tile:2.4,roughness:1},floor:{color:`#e3c193`,map:`${bk}/sand.png`,normalMap:`${bk}/sand_normal.png`,tile:3.5,roughness:1},pillar:`#d8a868`,path:`#a98a58`,sun:{color:`#ffb572`,intensity:1.15,height:.35},fill:`#caa37e`,mood:{sky:{elevationDeg:9,azimuthDeg:230,turbidity:9,rayleigh:3.5},fog:{near:.7,far:4.5,color:`#c79c6e`},exposure:.92,ambient:{color:`#ffdcb6`,intensity:.13}}}};function Sk(e,t,n){return{color:e.color,roughness:e.roughness,map:e.map,...e.normalMap?{normalMap:e.normalMap}:{},repeat:[Q(t/e.tile),Q(n/e.tile)]}}var Ck=10;function wk(e){let{seed:t,width:n=8,depth:r=8,cellSize:i=2,wallHeight:a=2.5,theme:o=`stone`}=e;if(!_k.includes(o))throw new y(`BAD_FORMAT`,`generateMaze theme must be one of [${_k.join(`, `)}], got '${o}'.`,{prop:`theme`,validOptions:[..._k]});let s=xk[o],c=new b(t),l=gk(c,n,r),u=2*n+1,d=2*r+1,f=Q(u*i),p=Q(d*i),m=(e,t)=>[Q((e+.5)*i-f/2),Q((t+.5)*i-p/2)],h=[qO(`Floor`,[f,vk,p],[0,-.1/2,0],{material:Sk(s.floor,f,p),receiveShadow:!0})],g=[],_=0;for(let e=0;e<d;e++){let t=0;for(;t<u;){if(l.cells[e]?.[t]){t++;continue}let n=1;for(;t+n<u&&!l.cells[e]?.[t+n];)n++;g.push({gx:t,gz:e,run:n}),_++;let r=Q(n*i);h.push(qO(`Wall${_}`,[r,a,i],[Q((t+n/2)*i-f/2),Q(a/2),Q((e+.5)*i-p/2)],{material:Sk(s.wall,r,a),castShadow:!0,receiveShadow:!0})),t+=n}}return s.cap&&h.push(...Tk(g,i,a,f,p,s.cap)),h.push(...Ek(c,l,i,a,m,s)),o===`hedge`?(h.push(...Ak(c,g,i,a,m)),h.push(...jk(c,l,i,m,n,r))):o===`canyon`&&h.push(...Mk(c,g,i,a,m)),h.push(...Ok(l,i,a,m,s)),h.push(...XO(Math.max(f,p)).map((e,t)=>{if(t!==0)return{...e,props:{...e.props,color:s.fill}};let n=e.props?.position;return{...e,props:{...e.props,color:s.sun.color,intensity:s.sun.intensity,position:[n[0]??0,Q((n[1]??0)*s.sun.height),n[2]??0]}}})),{name:`Maze`,type:`Node3D`,children:h}}function Tk(e,t,n,r,i,a){return e.map((e,o)=>({name:`Cap${o+1}`,type:`MeshInstance3D`,props:{mesh:`box`,size:[Q(e.run*t+.16),.12,Q(t+.16)],position:[Q((e.gx+e.run/2)*t-r/2),Q(n+.06),Q((e.gz+.5)*t-i/2)],material:{color:a,roughness:1},castShadow:!0,receiveShadow:!0}}))}function Ek(e,t,n,r,i,a){let o=[],s=(e,n)=>t.cells[n]?.[e]===!1;for(let e=2;e<t.cells.length-1;e+=2)for(let n=2;n<(t.cells[e]?.length??0)-1;n+=2)s(n,e)&&Number(s(n-1,e))+Number(s(n+1,e))+Number(s(n,e-1))+Number(s(n,e+1))>=3&&o.push([n,e]);let c=Math.min(Ck,o.length),l=[],u=new Set,d=Q(n*1.2),f=Q(r*1.12);for(let t=1;t<=c;t++){let n=e.int(0,o.length-1);for(;u.has(n);)n=(n+1)%o.length;u.add(n);let[r,s]=o[n],[c,p]=i(r,s);l.push(Dk(`Pillar${t}`,c,p,d,f,a))}return l}function Dk(e,t,n,r,i,a){return{name:e,type:`MeshInstance3D`,props:{mesh:`box`,size:[r,i,r],position:[t,Q(i/2),n],material:{...Sk(a.wall,r,i),color:a.pillar},castShadow:!0,receiveShadow:!0}}}function Ok(e,t,n,r,i){let a=2*e.cols,o=2*e.rows-1,s=Q(t*1.1),c=Q(n*1.25),l=[[0,0],[0,2],[a,o-1],[a,o+1]].map(([e,t],n)=>{let[a,o]=r(e,t);return Dk(`Gate${n+1}`,a,o,s,c,i)});for(let[e,n,s]of[[`EntrancePath`,0,1],[`ExitPath`,a,o]]){let[a,o]=r(n,s);l.push({name:e,type:`MeshInstance3D`,props:{mesh:`box`,size:[Q(t*.96),.04,Q(t*.96)],position:[a,.02,o],material:{...Sk(i.floor,t,t),color:i.path},receiveShadow:!0}})}return l}var kk=10;function Ak(e,t,n,r,i){return[...t].filter(e=>e.run>=2).sort((e,t)=>t.run-e.run||e.gz-t.gz||e.gx-t.gx).slice(0,kk).map((t,a)=>{let[,o]=i(t.gx,t.gz),[s]=i(t.gx,t.gz),[c]=i(t.gx+t.run-1,t.gz);return{name:`HedgeTop${a+1}`,type:`Foliage3D`,props:{kind:`grass`,style:`tufts`,area:[Q(t.run*n*.92),Q(n*.7)],density:14,height:.35,sway:.4,colorA:`#2f5e26`,colorB:`#5d8a3c`,seed:e.int(1,1e9),position:[Q((s+c)/2),r,o]}}})}function jk(e,t,n,r,i,a){let o=[];for(let e=0;e<t.cells.length;e++)for(let n=0;n<(t.cells[e]?.length??0);n++)t.cells[e]?.[n]&&o.push([n,e]);let s=Math.min(o.length,Math.max(3,Math.floor(i*a/12))),c=[],l=new Set;for(let t=1;t<=s&&l.size<o.length;t++){let i=e.int(0,o.length-1);for(;l.has(i);)i=(i+1)%o.length;l.add(i);let[a,s]=o[i],[u,d]=r(a,s),f=Q(n*.8);c.push({name:`Grass${t}`,type:`Foliage3D`,props:{kind:`grass`,area:[f,f],density:8,seed:e.int(1,1e9),position:[u,0,d]}})}return c}function Mk(e,t,n,r,i){let a=[];if(t.length===0)return a;let o=Math.min(8,t.length);for(let s=1;s<=o;s++){let o=e.pick(t),[c,l]=i(o.gx+e.int(0,o.run-1),o.gz),u=JO(s,c,l,e,[.3,Q(n*.35)],`#8f7355`),d=u.props?.position;d[1]=Q(d[1]+r),a.push(u)}return a}var Nk=`#23222b`,Pk=`#5f6672`;function Fk(e){let{seed:t,cols:n=10,rows:r=8,cellPx:i=64}=e,a=gk(new b(t),n,r),o=2*n+1,s=2*r+1,c=Q(o*i),l=Q(s*i),u=[{name:`Floor`,type:`ColorRect2D`,props:{size:[c,l],color:Nk}}],d=0;for(let e=0;e<s;e++){let t=0;for(;t<o;){if(a.cells[e]?.[t]){t++;continue}let n=1;for(;t+n<o&&!a.cells[e]?.[t+n];)n++;d++;let r=[Q(n*i),i];u.push({name:`Wall${d}`,type:`StaticBody2D`,props:{position:[Q((t+n/2)*i-c/2),Q((e+.5)*i-l/2)],collider:{shape:`rect`,size:r}},children:[{name:`Skin`,type:`ColorRect2D`,props:{size:r,color:Pk}}]}),t+=n}}return{name:`Maze2D`,type:`Node2D`,children:u}}var Ik=16,Lk=[`#5b8266`,`#3e6990`,`#a26b38`,`#6d5a96`,`#b0413e`];function Rk(e){let{seed:t,count:n=10,width:r=[80,160],gapX:i=[40,120],stepY:a=[-80,40],start:o=[0,300]}=e,s=new b(t),c=[],l=Q(s.range(r[0],r[1])),u=o[0],d=o[1];for(let e=1;e<=n&&(c.push(zk(e,u,d,l,s.pick(Lk))),e!==n);e++){let e=Q(s.range(r[0],r[1])),t=Q(s.range(i[0],i[1]));u=Q(u+l/2+t+e/2),d=Q(d+s.range(a[0],a[1])),l=e}return{name:`Platforms`,type:`Node2D`,children:c}}function zk(e,t,n,r,i){return{name:`Platform${e}`,type:`StaticBody2D`,props:{position:[t,n],collider:{shape:`rect`,size:[r,Ik]}},children:[{name:`Skin`,type:`ColorRect2D`,props:{size:[r,Ik],color:i}}]}}var Bk=[3,5],Vk=3,Hk={color:`#ffffff`,roughness:1,emissive:`#ffffff`,emissiveIntensity:.25};function Uk(e){let{seed:t,count:n=8,altitude:r=18}=e,[i,a]=ZO(e.area,[60,60]),o=new b(t),s=[];for(let e=1;e<=n;e++){let t=o.int(Bk[0],Bk[1]),n=[];for(let e=1;e<=t;e++){let r=Q(o.range(1,2.2));n.push({name:`Puff${e}`,type:`MeshInstance3D`,props:{mesh:`sphere`,size:[1,1,1],position:[Q((e-(t+1)/2)*o.range(1,1.6)),Q(o.range(-.3,.3)),Q(o.range(-.6,.6))],scale:[Q(r*o.range(1.1,1.6)),Q(r*.55),r],material:Hk}})}s.push({name:`Cloud${e}`,type:`Node3D`,props:{position:[Q(o.range(-i/2,i/2)),Q(r+o.range(-3,Vk)),Q(o.range(-a/2,a/2))]},children:n})}return{name:`Clouds`,type:`Node3D`,children:s}}var Wk=[`island`,`alpine`,`plains`,`desert`,`meadow`,`forest`,`savanna`,`snow`,`wetland`,`volcanic`],Gk=128,Kk=20,qk=.8,Jk=.12,Yk={island:{splat:`island`,maxHeight:4.5,sun:`#fff4d6`,fill:`#b9d4ff`,sky:{elevationDeg:38,azimuthDeg:145,turbidity:2.6,rayleigh:1.1},fog:{near:1,far:4},iblIntensity:.72},alpine:{splat:`alpine`,maxHeight:8,roughness:.65,detail:5,sun:`#f4f7ff`,fill:`#c9d8f2`,sky:{elevationDeg:45,azimuthDeg:35,turbidity:1.4,rayleigh:1.3},fog:{near:1.8,far:6.5},sunIntensity:1.1,exposure:.92,iblIntensity:.72},plains:{splat:`plains`,maxHeight:4,sun:`#fff2cf`,fill:`#bcd3ef`,sky:{elevationDeg:36,azimuthDeg:140,turbidity:2.4,rayleigh:1},fog:{near:.9,far:4},iblIntensity:.58},desert:{splat:`desert`,maxHeight:5,sun:`#ffe3b3`,fill:`#e8c9a6`,sky:{elevationDeg:42,azimuthDeg:160,turbidity:7,rayleigh:.6},fog:{near:.8,far:3.2,color:`#e8d3ae`},sunIntensity:1.35,iblIntensity:.75},meadow:{splat:`grassland`,maxHeight:1.2,sun:`#fff8e2`,fill:`#bfe0c9`,sky:{elevationDeg:55,azimuthDeg:125,turbidity:2.4,rayleigh:.95},fog:{near:1,far:4.4},sunIntensity:2.6,exposure:1.12,iblIntensity:.62},forest:{splat:`forest`,maxHeight:2.5,sun:`#ffdca8`,fill:`#a9c8b4`,sky:{elevationDeg:29,azimuthDeg:120,turbidity:5.5,rayleigh:1},fog:{near:.22,far:1.8,color:`#9fb494`},sunIntensity:2.6,ambient:.26,iblIntensity:.55},savanna:{splat:`savanna`,maxHeight:3,sun:`#ffdca0`,fill:`#e6d2a4`,sky:{elevationDeg:34,azimuthDeg:150,turbidity:5,rayleigh:.7},fog:{near:1,far:4.2,color:`#e3cf9f`},sunIntensity:2,exposure:1.05,iblIntensity:.6},snow:{splat:`snow`,maxHeight:2.2,sun:`#dfe9ff`,fill:`#c2d2f0`,sky:{elevationDeg:22,azimuthDeg:35,turbidity:1.6,rayleigh:1.6},fog:{near:1.2,far:5,color:`#dbe6f5`},sunIntensity:1,exposure:.9,iblIntensity:.72},wetland:{splat:`wetland`,maxHeight:1.4,sun:`#d6ddc8`,fill:`#9fb29a`,sky:{elevationDeg:24,azimuthDeg:115,turbidity:9,rayleigh:1.1},fog:{near:.5,far:2,color:`#92a288`},sunIntensity:1.5,exposure:.94,ambient:.24,iblIntensity:.66},volcanic:{splat:`volcanic`,maxHeight:4,roughness:.7,sun:`#ff8a4a`,fill:`#7a4a3a`,sky:{elevationDeg:14,azimuthDeg:135,turbidity:10,rayleigh:.3},fog:{near:.18,far:1.4,color:`#2a211c`},sunIntensity:1.7,exposure:.86,ambient:.22,iblIntensity:.5}};function Xk(e){let{seed:t,theme:n=`island`,size:r=200,water:i=!1}=e,a=Yk[n];if(!a)throw new y(`BAD_FORMAT`,`generateTerrain theme must be one of [${Wk.join(`, `)}], got '${n}'.`,{prop:`theme`,validOptions:[...Wk]});let o=e.maxHeight||a.maxHeight,s=new b(t),c=n===`island`,l=e=>sy({width:r,depth:r,segsX:Gk,segsZ:Gk,maxHeight:e,seed:t,...a.roughness===void 0?{}:{roughness:a.roughness},...a.detail===void 0?{}:{detail:a.detail},islandEdge:c}),u=l(o);if(c)for(let e=0;e<3;e++){let e=Qk(u)-Kk,t=u.maxHeight-u.minHeight,n=u.minHeight+Jk*t;if(e+qk<=n)break;let r=Kk-qk,i=Qk(u)-u.minHeight-Jk*t;o=Q(o*Math.min(r/i*.95,.9)),u=l(o)}let d=[{name:`Ground`,type:`StaticBody3D`,props:{collider:{shape:`heightfield`}},children:[{name:`Surface`,type:`Terrain3D`,props:{size:[r,r],maxHeight:o,seed:t,theme:a.splat,...a.roughness===void 0?{}:{roughness:a.roughness},...a.detail===void 0?{}:{detail:a.detail}}}]}],f=n===`wetland`;if(c)d.push({name:`Sea`,type:`Water3D`,props:{size:[r*8,r*8],position:[0,Zk(u),0],opacity:1}});else if(f){let e=u.maxHeight-u.minHeight||1,t=Q(u.minHeight+.22*e);d.push({name:`Swamp`,type:`Water3D`,props:{size:[r,r],position:[0,t,0],waveHeight:.02,quality:`simple`,color:`#3a4a30`,opacity:.95}})}else if(i){let e=Q(u.minHeight+.1*(u.maxHeight-u.minHeight));d.push({name:`Lake`,type:`Water3D`,props:{size:[r,r],position:[0,e,0],waveHeight:.04}})}return d.push(...mA(n,s,u,r)),c&&d.push(Uk({seed:s.int(1,1e9),count:6,area:r,altitude:Math.round(u.maxHeight+12)})),d.push(...XO(r).map((e,t)=>hA(e,t===0?a.sun:a.fill,t===0?a.sunIntensity??1.7:void 0))),{name:`Terrain`,type:`Node3D`,children:d}}function Zk(e){let t=Qk(e)-Kk,n=e.minHeight+Jk*(e.maxHeight-e.minHeight);return Q(Math.max(Math.min(t+qk,n),t+.55))}function Qk(e){let t=-1/0,n=e.width/2,r=e.depth/2;for(let i=0;i<=Gk;i++){let a=-n+i/Gk*e.width,o=-r+i/Gk*e.depth;t=Math.max(t,e.baseHeight(a,-r),e.baseHeight(a,r),e.baseHeight(-n,o),e.baseHeight(n,o))}return t}function $k(e,t,n){let r=Math.min(t.width/2-n.margin,n.within??1/0),i=t.maxHeight-t.minHeight||1,a=[];for(let o=0;o<n.count*30&&a.length<n.count;o++){let o=Q(e.range(-r,r)),s=Q(e.range(-r,r));if(n.clearing&&Math.hypot(o,s)<n.clearing||n.within&&Math.hypot(o,s)>n.within)continue;let c=t.heightAt(o,s),l=(c-t.minHeight)/i;l<n.band[0]||l>n.band[1]||t.slopeAt(o,s)>n.maxSlope||a.push({x:o,z:s,y:c})}return a}var eA=[{canopy:`#2f5d44`,trunk:`#6e4a32`},{canopy:`#356a4c`,trunk:`#71503a`},{canopy:`#2c5740`,trunk:`#5f4530`},{canopy:`#3a6b4a`,trunk:`#6a4c34`}],tA=[{canopy:`#4a7c3f`,trunk:`#7a5a3a`},{canopy:`#56883c`,trunk:`#806044`},{canopy:`#7a9d3e`,trunk:`#9a9488`},{canopy:`#86a346`,trunk:`#a39c8e`},{canopy:`#b8862f`,trunk:`#7e5e38`},{canopy:`#a8702c`,trunk:`#74552f`}],nA={canopy:`#9aa052`,trunk:`#8a6a44`},rA={canopy:`#39513f`,trunk:`#5a5650`};function iA(e,t){return e===`conifer`?t.pick(eA):e===`broadleaf`?t.pick(tA):null}function aA(e,t,n,r,i){let a=i?.height??[4.5,7],o=i?.sink??(i?.count===void 0?.05:.3),s={};i?.tier!==void 0&&(s.tier=i.tier),i?.count!==void 0&&i.area!==void 0&&(s.count=i.count,s.area=[i.area,i.area]);let c=iA(r,n),l=i?.palette??c;return l&&(s.canopyColor=l.canopy,s.trunkColor=l.trunk),{name:e,type:`Tree3D`,props:{type:r,seed:n.int(1,1e9),height:Q(n.range(a[0],a[1])),position:[t.x,Q(t.y-o),t.z],...s}}}function oA(e,t,n,r,i,a=0,o){return{name:e,type:`Foliage3D`,props:{kind:`grass`,style:`tufts`,area:[r,r],density:i,height:o?.height??.3,...o?.colors?{colorA:o.colors[0],colorB:o.colors[1]}:{},seed:n.int(1,1e9),position:[t.x,Q(t.y+.02),t.z],...a>0?{flowers:a}:{}}}}function sA(e,t,n,r,i){return{name:e,type:`Foliage3D`,props:{kind:`reeds`,style:`simple`,area:[r,r],density:i,height:.9,colorA:`#2f4a26`,colorB:`#5a6f33`,seed:n.int(1,1e9),position:[t.x,Q(t.y+.02),t.z]}}}function cA(e,t,n,r,i){return{name:e,type:`Flowers3D`,props:{density:i,area:[r,r],seed:n.int(1,1e9),position:[t.x,Q(t.y+.02),t.z]}}}function lA(e,t,n,r){return e.map((e,i)=>{let a=JO(i+1,e.x,e.z,t,n,r?.(t)),o=a.props?.position;return o[1]=Q(o[1]+e.y),a})}function uA(e){let t=Math.round(e.range(104,128)),n=Math.round(t-e.range(10,18)),r=Math.round(t-e.range(20,30)),i=e=>e.toString(16).padStart(2,`0`);return`#${i(n)}${i(t)}${i(r)}`}function dA(e){let t=Math.round(e.range(196,224)),n=Math.min(255,t+Math.round(e.range(4,12))),r=e=>e.toString(16).padStart(2,`0`);return`#${r(t)}${r(t)}${r(n)}`}function fA(e){let t=Math.round(e.range(34,56)),n=Math.min(255,t+Math.round(e.range(2,8))),r=e=>e.toString(16).padStart(2,`0`);return`#${r(n)}${r(t)}${r(t)}`}var pA=[`#6f5b41`,`#7a644a`,`#665439`];function mA(e,t,n,r){let i=[];switch(e){case`island`:{let e=$k(t,n,{count:7,band:[.18,.6],maxSlope:.5,margin:26});i.push(...e.map((e,n)=>aA(`Tree${n+1}`,e,t,`conifer`)));break}case`alpine`:{let e=$k(t,n,{count:10,band:[.2,.5],maxSlope:.55,margin:6});i.push(...e.map((e,n)=>aA(`Tree${n+1}`,e,t,`conifer`)));let r=$k(t,n,{count:8,band:[.55,1],maxSlope:.9,margin:6});i.push(...lA(r,t,[.6,1.8]));break}case`plains`:{let e=$k(t,n,{count:8,band:[0,1],maxSlope:.4,margin:6});i.push(...e.map((e,n)=>aA(`Tree${n+1}`,e,t,`broadleaf`)));let r=$k(t,n,{count:6,band:[0,1],maxSlope:.5,margin:6});i.push(...lA(r,t,[.4,1.2]));break}case`desert`:{let e=$k(t,n,{count:6,band:[0,1],maxSlope:.45,margin:6});i.push(...e.map((e,n)=>aA(`Tree${n+1}`,e,t,`dead`)));let r=$k(t,n,{count:8,band:[0,1],maxSlope:.6,margin:6});i.push(...lA(r,t,[.5,1.6]));break}case`meadow`:{let e=Q(r*.09),a=$k(t,n,{count:7,band:[0,1],maxSlope:.05,margin:e/2+8});i.push(...a.map((n,r)=>oA(`Carpet${r+1}`,n,t,e,30)));let o=Q(r*.07),s=$k(t,n,{count:3,band:[0,1],maxSlope:.05,margin:o/2+8});i.push(...s.map((e,n)=>cA(`Flowers${n+1}`,e,t,o,`sparse`)));let c=$k(t,n,{count:4,band:[0,1],maxSlope:.1,margin:12});i.push(...c.map((e,n)=>aA(`Grove${n+1}`,e,t,`broadleaf`,{count:3,area:6})));let l=$k(t,n,{count:6,band:[0,1],maxSlope:.2,margin:8});i.push(...lA(l,t,[.3,.9]));break}case`forest`:{let e=Q(r*.12),a=r/2-8,o=Be(t.int(1,1e9)),s=(e,t,n)=>{let r=o(e/70,t/70);return r>.12?`conifer`:r<-.12?`broadleaf`:n%2==0?`conifer`:`broadleaf`},c=$k(t,n,{count:40,band:[0,1],maxSlope:.18,margin:8,clearing:e+6}),l=c.map((e,t)=>s(e.x,e.z,t));i.push(...c.map((e,n)=>aA(`Grove${n+1}`,e,t,l[n],{count:12,area:13,height:[5.5,8]}))),c.forEach((e,n)=>{n%3==0&&i.push(aA(`Sapling${n+1}`,e,t,l[n],{count:4,area:19,height:[2.5,3.4],sink:.12}))});let u=$k(t,n,{count:6,band:[0,1],maxSlope:.2,margin:12,clearing:e+4});i.push(...u.map((e,n)=>aA(`Elder${n+1}`,e,t,s(e.x,e.z,n),{tier:`high`,height:[8.4,9.8]})));let d=$k(t,n,{count:3,band:[0,1],maxSlope:.18,margin:10,clearing:e+6});i.push(...d.map((e,n)=>aA(`Accent${n+1}`,e,t,`broadleaf`,{tier:`high`,count:3,area:7,height:[5.6,6.8]})));let f=$k(t,n,{count:10,band:[0,1],maxSlope:.14,margin:9,clearing:e+2});i.push(...f.map((e,n)=>aA(`Bush${n+1}`,e,t,`bush`,{count:4,area:10,height:[2.1,3.1],sink:.12})));let p=0;c.forEach((e,r)=>{if(r%3==2)return;let o=t.range(0,Math.PI*2),s=t.range(2,4.5),c=t.int(1,1e9),l=Q(QO(e.x+Math.cos(o)*s,-a,a)),u=Q(QO(e.z+Math.sin(o)*s,-a,a));n.slopeAt(l,u)>.09||(p++,i.push({name:`Fern${p}`,type:`Foliage3D`,props:{kind:`grass`,style:`tufts`,tuftStyle:`fern`,area:[8,8],density:9,height:.4,colorA:`#4a6b34`,colorB:`#82a258`,seed:c,position:[l,Q(n.heightAt(l,u)+.02),u]}}))});let m=Q(r*.08),h=$k(t,n,{count:5,band:[0,1],maxSlope:.06,margin:m/2+8});i.push(...h.map((e,n)=>oA(`Grass${n+1}`,e,t,m,20,0,{height:.24,colors:[`#4f7034`,`#85a154`]})));let g=u.length>0?t.int(3,5):0;for(let e=0;e<g;e++){let r=u[e%u.length],o=t.range(0,Math.PI*2),s=t.range(2.5,4.5),c=Q(QO(r.x+Math.cos(o)*s,-a,a)),l=Q(QO(r.z+Math.sin(o)*s,-a,a));i.push({name:`Log${e+1}`,type:`Tree3D`,props:{type:`dead`,seed:t.int(1,1e9),height:Q(t.range(4.2,5.6)),trunkColor:`#4a4236`,position:[c,Q(n.heightAt(c,l)+.12),l],rotation:[0,t.int(0,359),Q(t.range(81,97))]}})}$k(t,n,{count:5,band:[0,1],maxSlope:.25,margin:9,clearing:e}).forEach((e,n)=>{let r=Q(t.range(.16,.28)),a=Q(t.range(.3,.55));i.push({name:`Stump${n+1}`,type:`MeshInstance3D`,props:{mesh:`cylinder`,size:[r,a,r],position:[e.x,Q(e.y+a/2-.06),e.z],rotation:[0,t.int(0,359),0],material:{color:t.pick(pA),roughness:1},castShadow:!0}})});let _=$k(t,n,{count:8,band:[0,1],maxSlope:.2,margin:8});i.push(...lA(_,t,[.3,1],uA));let v=Q(e*.75),y=$k(t,n,{count:3,band:[0,1],maxSlope:.06,margin:8,within:e-Q(v/Math.SQRT2)});i.push(...y.map((e,n)=>oA(`Clearing${n+1}`,e,t,v,30,0,{colors:[`#5d8438`,`#a8bc60`]})));let b=Q(e*.45),x=$k(t,n,{count:2,band:[0,1],maxSlope:.06,margin:8,within:e-Q(b/Math.SQRT2)});i.push(...x.map((e,n)=>cA(`Flowers${n+1}`,e,t,b,`sparse`)));break}case`savanna`:{let e=$k(t,n,{count:7,band:[0,.85],maxSlope:.35,margin:8});i.push(...e.map((e,n)=>aA(`Tree${n+1}`,e,t,`broadleaf`,{height:[3.5,5],palette:nA})));let a=Q(r*.09),o=$k(t,n,{count:6,band:[0,1],maxSlope:.05,margin:a/2+8});i.push(...o.map((e,n)=>oA(`Carpet${n+1}`,e,t,a,26,0,{height:.34,colors:[`#9a8f43`,`#c9bd6a`]})));let s=$k(t,n,{count:7,band:[0,1],maxSlope:.5,margin:8});i.push(...lA(s,t,[.4,1.4]));let c=$k(t,n,{count:4,band:[0,1],maxSlope:.2,margin:9});i.push(...c.map((e,n)=>aA(`Scrub${n+1}`,e,t,`bush`,{count:3,area:8,height:[1.4,2.2]})));break}case`snow`:{let e=$k(t,n,{count:8,band:[0,.9],maxSlope:.4,margin:8});i.push(...e.map((e,n)=>aA(`Tree${n+1}`,e,t,`conifer`,{height:[4,6.5],palette:rA})));let r=$k(t,n,{count:10,band:[0,1],maxSlope:.7,margin:8});i.push(...lA(r,t,[.5,1.8],dA));break}case`wetland`:{let e=Q(r*.08),a=$k(t,n,{count:6,band:[0,.5],maxSlope:.06,margin:e/2+8});i.push(...a.map((n,r)=>sA(`Reeds${r+1}`,n,t,e,4)));let o=Q(r*.08),s=$k(t,n,{count:4,band:[.2,1],maxSlope:.05,margin:o/2+8});i.push(...s.map((e,n)=>oA(`Moss${n+1}`,e,t,o,22,0,{height:.22,colors:[`#3a5a2c`,`#6f8a48`]})));let c=$k(t,n,{count:6,band:[.25,1],maxSlope:.35,margin:9});i.push(...c.map((e,n)=>aA(`Snag${n+1}`,e,t,`dead`,{height:[4,6]})));let l=$k(t,n,{count:6,band:[.2,1],maxSlope:.2,margin:9});i.push(...l.map((e,n)=>aA(`Bush${n+1}`,e,t,`bush`,{count:3,area:7,height:[1.6,2.6]})));let u=$k(t,n,{count:5,band:[.2,1],maxSlope:.3,margin:8});i.push(...lA(u,t,[.3,1],uA));break}case`volcanic`:{let e=$k(t,n,{count:7,band:[0,1],maxSlope:.45,margin:8});i.push(...e.map((e,n)=>aA(`Tree${n+1}`,e,t,`dead`,{height:[4,6.5]})));let r=$k(t,n,{count:12,band:[0,1],maxSlope:.7,margin:8});i.push(...lA(r,t,[.4,1.8],fA)),$k(t,n,{count:3,band:[0,.6],maxSlope:.18,margin:12}).forEach((e,n)=>{t.int(1,1e9),i.push({name:`Smoke${n+1}`,type:`Particles3D`,props:{preset:`smoke`,position:[e.x,Q(e.y+.2),e.z],rate:8,maxParticles:64,colorStart:`#5a5048`,colorEnd:`#2a2422`,sizeStart:18,sizeEnd:44}}),i.push({name:`Embers${n+1}`,type:`Particles3D`,props:{position:[e.x,Q(e.y+.1),e.z],rate:10,maxParticles:48,lifetime:[.8,1.8],speed:[12,34],directionDeg:-90,spreadDeg:40,gravity:[0,-18],sizeStart:4,sizeEnd:1,colorStart:`#ffce6a`,colorEnd:`#d83a14`,blend:`add`}})});break}}return i}function hA(e,t,n){return{...e,props:{...e.props,color:t,...n===void 0?{}:{intensity:n}}}}var gA={arena:{description:`FPS stage: floor, 4 perimeter walls, obstacles, lights — themes: boxes (crates), ruins (broken stone rows), garden (hedges, grass, pool)`,dimension:`3d`,params:{theme:{type:`string`,default:`boxes`,options:[...$O]},width:{type:`number`,default:30,min:4},depth:{type:`number`,default:30,min:4},wallHeight:{type:`number`,default:3,min:.5},obstacles:{type:`number`,default:8,min:0}}},terrain:{description:`Heightfield world: StaticBody3D{heightfield} + Terrain3D + theme dressing (trees, rocks, grass, sea/clouds on island, broad swamp water on wetland, smoke/ember emitters on volcanic; maxHeight 0 = theme default; water adds a lake to non-island themes)`,dimension:`3d`,params:{theme:{type:`string`,default:`island`,options:[...Wk]},size:{type:`number`,default:200,min:40,max:400},maxHeight:{type:`number`,default:0,min:0},water:{type:`boolean`,default:!1}}},maze:{description:`Recursive-backtracker 3D maze, west→east — themes: stone, hedge (green + grass), canyon (sandstone + rim rocks)`,dimension:`3d`,params:{theme:{type:`string`,default:`stone`,options:[..._k]},width:{type:`number`,default:8,min:2,max:40},depth:{type:`number`,default:8,min:2,max:40},cellSize:{type:`number`,default:2,min:.5},wallHeight:{type:`number`,default:2.5,min:.5}}},maze2d:{description:`The same maze algorithm as 2D ColorRect2D + StaticBody2D tiles`,dimension:`2d`,params:{cols:{type:`number`,default:10,min:2,max:40},rows:{type:`number`,default:8,min:2,max:40},cellPx:{type:`number`,default:64,min:8}}},dungeon2d:{description:`Roguelike rooms + L-corridors: floor rects, wall bodies (32px tiles)`,dimension:`2d`,params:{rooms:{type:`number`,default:5,min:1,max:20},size:{type:`number`,default:960,min:256}}},platforms2d:{description:`Left-to-right 2D platform course (tune ranges via the library)`,dimension:`2d`,params:{count:{type:`number`,default:10,min:1}}}},_A={arena:ik,terrain:Xk,maze:wk,maze2d:Fk,dungeon2d:fk,platforms2d:Rk};function vA(e,t){let n=_A[e];if(!n){let t=Object.keys(_A);throw new y(`BAD_FORMAT`,`Unknown generator '${e}'. Valid: [${t.join(`, `)}] — the old meadow/forest/island/rocks/clouds generators became themes (e.g. terrain theme: 'meadow', arena theme: 'garden'); scatter is library-only (needs item templates).`,{validOptions:t})}return n(t)}function yA(e){return Object.entries(gA).filter(([,t])=>t.dimension===e).map(([e,t])=>({name:e,meta:t}))}function bA(e,t){if(e.type===`boolean`)return t===!0||t===`true`;if(e.type===`number`){let n=typeof t==`boolean`?NaN:Number(t);return(t===``||!Number.isFinite(n))&&(n=e.default),e.min!==void 0&&(n=Math.max(e.min,n)),e.max!==void 0&&(n=Math.min(e.max,n)),n}let n=String(t);return e.options&&!e.options.includes(n)?e.default:n}function xA(){return Math.floor(Math.random()*1e6)}var SA=[],CA=new Map,wA=null;function TA(e){let t=document.querySelector(`#generate`);t&&(OA(e),t.removeAttribute(`hidden`))}function EA(){document.querySelector(`#generate`)?.setAttribute(`hidden`,``)}function DA(e){let t=document.querySelector(`#generate`);t&&(t.addEventListener(`pointerdown`,e=>{e.target===t&&EA()}),document.querySelector(`#generate-close`)?.addEventListener(`click`,EA),document.querySelector(`#generate-cancel`)?.addEventListener(`click`,EA),document.querySelector(`#generate-insert`)?.addEventListener(`click`,()=>kA(e)))}function OA(e){let t=document.querySelector(`#generate-body`),n=document.querySelector(`#generate-status`);if(!t)return;n&&(n.textContent=``),t.textContent=``;let r=e.working.dimension??`2d`;SA=yA(r);let i=document.createElement(`p`);i.className=`gen-hint`,i.textContent=$D({en:`Deterministic ${r.toUpperCase()} environment generators — the subtree inserts under the selected node (the root when nothing is selected). The same seed always generates the same level.`,ko:`결정적 ${r.toUpperCase()} 환경 생성기 — 생성된 서브트리는 선택한 노드 아래에(선택이 없으면 루트에) 들어갑니다. 같은 시드는 항상 같은 레벨을 만듭니다.`}),t.appendChild(i);let a=AA($D({en:`generator`,ko:`생성기`})),o=document.createElement(`select`);o.id=`generate-name`;for(let{name:e}of SA){let t=document.createElement(`option`);t.value=e,t.textContent=e,o.appendChild(t)}a.appendChild(o),t.appendChild(a);let s=document.createElement(`p`);s.className=`gen-desc`,t.appendChild(s);let c=document.createElement(`div`);c.id=`generate-params`,t.appendChild(c);let l=AA(`seed`);wA=document.createElement(`input`),wA.type=`number`,wA.step=`1`,wA.value=String(xA());let u=document.createElement(`button`);u.type=`button`,u.className=`ghost`,u.textContent=`↻`,u.title=$D({en:`New random seed`,ko:`새 랜덤 시드`}),u.addEventListener(`click`,()=>{wA&&(wA.value=String(xA()))});let d=document.createElement(`div`);d.className=`gen-seed-row`,d.append(wA,u),l.appendChild(d),t.appendChild(l);let f=()=>{let e=SA.find(e=>e.name===o.value)??SA[0];if(e){s.textContent=e.meta.description,c.textContent=``,CA=new Map;for(let[t,n]of Object.entries(e.meta.params)){let e=AA(t),r;if(n.type===`boolean`)r=document.createElement(`input`),r.type=`checkbox`,r.checked=n.default===!0;else if(n.type===`string`&&n.options){r=document.createElement(`select`);for(let e of n.options){let t=document.createElement(`option`);t.value=e,t.textContent=e,r.appendChild(t)}r.value=String(n.default)}else r=document.createElement(`input`),r.type=n.type===`number`?`number`:`text`,n.type===`number`&&(r.step=`any`,n.min!==void 0&&(r.min=String(n.min)),n.max!==void 0&&(r.max=String(n.max))),r.value=String(n.default),(n.min!==void 0||n.max!==void 0)&&(r.title=`${n.min??``}–${n.max??``}`);CA.set(t,r),e.appendChild(r),c.appendChild(e)}}};o.addEventListener(`change`,f),f()}function kA(e){let t=document.querySelector(`#generate-status`),n=document.querySelector(`#generate-name`),r=SA.find(e=>e.name===n?.value);if(!r||!wA)return;let i={seed:bA({type:`number`,default:xA(),min:0},wA.value)};for(let[e,t]of CA){let n=r.meta.params[e];n&&(i[e]=bA(n,t.type===`checkbox`?t.checked:t.value))}let a;try{a=vA(r.name,i)}catch(e){t&&(t.textContent=e instanceof Error?e.message:String(e));return}let o=e.insertNode(a,e.selection??[]);if(o===null){t&&(t.textContent=$D({en:`Insert failed — see the error banner.`,ko:`삽입 실패 — 에러 배너를 확인하세요.`}));return}e.select(o),wA&&(wA.value=String(xA())),EA()}function AA(e){let t=document.createElement(`label`);t.className=`field`;let n=document.createElement(`span`);return n.textContent=e,t.appendChild(n),t}var jA={groups:{title:{en:`groups — tag nodes for queries`,ko:`groups — 조회용 태그`},body:{en:`Free-form tags. Game code finds nodes with tree-wide queries like getNodesInGroup("coins"), and triggers can filter by group (e.g. only react to "player"). Type a name and press Enter.`,ko:`자유 형식 태그입니다. 게임 코드가 getNodesInGroup("coins")처럼 트리 전체에서 노드를 찾고, 트리거는 그룹으로 거릅니다(예: "player"에만 반응). 이름을 입력하고 Enter를 누르세요.`},example:`triggerEnter → if (other.isInGroup("player")) collect()`},script:{title:{en:`script — attach YOUR game logic`,ko:`script — 게임 로직 연결`},body:{en:`A Behavior is a TypeScript class living in YOUR game code, linked by name. The editor stores the link; the class itself must be registered in the game before loadScene. Props here are passed to the behavior instance.`,ko:`Behavior는 게임 코드에 있는 TypeScript 클래스이며 이름으로 연결됩니다. 에디터는 연결만 저장하고, 클래스 자체는 loadScene 전에 게임에서 등록되어야 합니다. 여기의 props가 비헤이비어 인스턴스로 전달됩니다.`},example:`registerBehavior('CoinCounter', CoinCounter) // in your main.ts`,copy:{label:`copy behavior boilerplate`,text:`import { Behavior, registerBehavior } from 'incanto';
|
|
7353
|
+
`;function bD(){let e=new Rs({vertexShader:vD,fragmentShader:yD,depthTest:!1,depthWrite:!1,uniforms:{tColor:{value:null},tDepth:{value:null},uInvViewProj:{value:new G},uCameraPos:{value:new H},uWaterLevel:{value:0},uTime:{value:0},uCausticColor:{value:new K(`#cdeeff`)},uCausticIntensity:{value:.55},uCausticScale:{value:.32},uMaxDist:{value:22}}}),t=new ho(new Os(2,2),e);return t.frustumCulled=!1,{mesh:t,uniforms:e.uniforms}}var xD=class{viewOverride=null;overrideCam=new Nc(60,1,.05,5e3);lastCamera=null;lastSize={w:1,h:1};webgl;threeScene=new Wi;environment=new oD(this.threeScene);engine;disconnect;canvas;assets;ownsAssets;loadedAssetScenes=new WeakSet;compiledScene=null;syncScratch=lD();renderCtx=null;causticsTarget=null;causticsScene=null;causticsUniforms=null;cloudsTarget=null;cloudsHalfTarget=null;cloudsBlurTarget=null;cloudsScene=null;cloudsUniforms=null;cloudsBlurScene=null;cloudsBlurUniforms=null;cloudsCompositeScene=null;cloudsCompositeUniforms=null;constructor(e){this.canvas=e.canvas,this.engine=e.engine,this.ownsAssets=!e.assets,this.assets=e.assets??new Hv;let t=qe(e.engine.scene?.environment,{antialias:!0,pixelRatio:Math.min(globalThis.devicePixelRatio??1,2)},globalThis.devicePixelRatio??1,{pixelRatio:e.pixelRatio});this.webgl=new Ff({canvas:e.canvas,antialias:t.antialias}),this.webgl.setPixelRatio(t.pixelRatio),this.webgl.shadowMap.enabled=!0,this.webgl.shadowMap.type=1,this.webgl.toneMapping=4,this.webgl.toneMappingExposure=1,this.debugLines=new cs(new Ha,new Xo({color:`#00ff6e`,depthTest:!1})),this.debugLines.frustumCulled=!1,this.debugLines.renderOrder=9999,this.debugLines.visible=!1,this.threeScene.add(this.debugLines),this.disconnect=this.engine.updated.connect(()=>this.render())}debugLines;syncDebugLines(){let e=null;for(let t of Kf(`3d`))if(e=t.debugLines(),e)break;this.debugLines.visible=e!==null,e&&this.debugLines.geometry.setAttribute(`position`,new q(e,3))}render(){let e=this.engine.scene;if(!e)return;this.syncDebugLines(),e.assets&&!this.loadedAssetScenes.has(e)&&(this.assets.load(e.assets),this.loadedAssetScenes.add(e)),this.environment.apply(e.environment,this.webgl);let{activeCamera:t,renderHooks:n,sunLight:r}=uD(e.root,this.threeScene,this.assets,{sunDirection:this.environment.sunDirection,alpha:this.engine.interpolationAlpha},this.syncScratch),i=t;if(this.viewOverride){let[e,t,n]=this.viewOverride.position,[r,a,o]=this.viewOverride.target;this.overrideCam.position.set(e,t,n),this.overrideCam.lookAt(CD.set(r,a,o)),i=this.overrideCam}if(!i)return;this.lastCamera=i,i.updateWorldMatrix(!0,!1);let a=i.getWorldPosition(OD),o=r?r._ensureObject3D():null,s=!!o&&r.shadowFollowsCamera===!0;this.environment.applySunLight(o,s?a:null),this.compiledScene!==e&&(this.compiledScene=e,this.webgl.compile(this.threeScene,i));let c=this.canvas.clientWidth||this.canvas.width,l=this.canvas.clientHeight||this.canvas.height,u=this.webgl.getSize(SD);(u.x!==c||u.y!==l)&&this.webgl.setSize(c,l,!1),this.lastSize={w:c,h:l};let d=l===0?1:c/l;i.aspect!==d&&(i.aspect=d,i.updateProjectionMatrix()),this.renderCtx||={gl:this.webgl,scene:this.threeScene,camera:i};let f=this.renderCtx;f.camera=i,o?(o.updateWorldMatrix(!0,!1),o.target.updateWorldMatrix(!0,!1),f.sunDir=TD.setFromMatrixPosition(o.matrixWorld).sub(ED.setFromMatrixPosition(o.target.matrixWorld)).normalize()):f.sunDir=null;for(let e=0;e<n.length;e++)n[e]?._onRender3D(f);let p=null;for(let e=0;e<n.length;e++){let t=n[e].underwaterAt?.(a.x,a.y,a.z);if(t){p=t;break}}this.environment.applyUnderwater(p);let m=this.environment.clouds;p?.caustics.enabled?this.renderWithCaustics(i,p):m&&!p?this.renderWithClouds(i,m,o):this.webgl.render(this.threeScene,i)}renderWithClouds(e,t,n){let r=this.webgl.getDrawingBufferSize(SD),i=Math.max(1,r.x),a=Math.max(1,r.y),o=Math.max(1,Math.ceil(i/3)),s=Math.max(1,Math.ceil(a/3));if(this.cloudsTarget&&(this.cloudsTarget.width!==i||this.cloudsTarget.height!==a)&&(this.cloudsTarget.depthTexture?.dispose(),this.cloudsTarget.dispose(),this.cloudsTarget=null,this.cloudsHalfTarget?.dispose(),this.cloudsHalfTarget=null,this.cloudsBlurTarget?.dispose(),this.cloudsBlurTarget=null),!this.cloudsTarget){let e=new ys(i,a);e.type=Ft,this.cloudsTarget=new ai(i,a,{depthTexture:e,depthBuffer:!0})}if(this.cloudsHalfTarget||(this.cloudsHalfTarget=new ai(o,s,{depthBuffer:!1}),this.cloudsBlurTarget=new ai(o,s,{depthBuffer:!1})),!this.cloudsScene){let{mesh:e,uniforms:t}=DE();this.cloudsScene=new Wi,this.cloudsScene.add(e),this.cloudsUniforms=t}if(!this.cloudsBlurScene){let{mesh:e,uniforms:t}=EE();this.cloudsBlurScene=new Wi,this.cloudsBlurScene.add(e),this.cloudsBlurUniforms=t}if(!this.cloudsCompositeScene){let{mesh:e,uniforms:t}=OE();this.cloudsCompositeScene=new Wi,this.cloudsCompositeScene.add(e),this.cloudsCompositeUniforms=t}this.webgl.setRenderTarget(this.cloudsTarget),this.webgl.render(this.threeScene,e);let c=this.cloudsUniforms;c.tDepth.value=this.cloudsTarget.depthTexture,c.uInvViewProj.value.multiplyMatrices(e.projectionMatrix,e.matrixWorldInverse).invert(),e.getWorldPosition(c.uCameraPos.value),c.uTime.value=(globalThis.performance?.now()??0)*.001*t.speed;let l=this.environment.sunDirection??[.4,.8,.3];c.uSunDir.value.set(l[0],l[1],l[2]).normalize(),n&&c.uSunColor.value.copy(n.color),c.uCloudColor.value.set(t.color),c.uShadeColor.value.set(t.shadeColor);let u=this.environment.sceneFog;u?(c.uHorizonColor.value.copy(u.color),c.uFarFade.value=u.far):(c.uHorizonColor.value.set(`#cdd9e6`),c.uFarFade.value=6e3),c.uBase.value=t.base,c.uTop.value=t.top,c.uCoverage.value=t.coverage,c.uDensity.value=t.density,c.uScale.value=t.scale,c.uWind.value.set(1,.35),this.webgl.setRenderTarget(this.cloudsHalfTarget),this.webgl.render(this.cloudsScene,e);let d=this.cloudsBlurUniforms,f=this.cloudsBlurScene;d.tTex.value=this.cloudsHalfTarget.texture,d.uDir.value.set(1/o,0),this.webgl.setRenderTarget(this.cloudsBlurTarget),this.webgl.render(f,e),d.tTex.value=this.cloudsBlurTarget.texture,d.uDir.value.set(0,1/s),this.webgl.setRenderTarget(this.cloudsHalfTarget),this.webgl.render(f,e),this.webgl.setRenderTarget(null);let p=this.cloudsCompositeUniforms;p.tScene.value=this.cloudsTarget.texture,p.tClouds.value=this.cloudsHalfTarget.texture,this.webgl.render(this.cloudsCompositeScene,e)}renderWithCaustics(e,t){let n=this.webgl.getDrawingBufferSize(SD),r=Math.max(1,n.x),i=Math.max(1,n.y);if(this.causticsTarget&&(this.causticsTarget.width!==r||this.causticsTarget.height!==i)&&(this.causticsTarget.depthTexture?.dispose(),this.causticsTarget.dispose(),this.causticsTarget=null),!this.causticsTarget){let e=new ys(r,i);e.type=Ft,this.causticsTarget=new ai(r,i,{depthTexture:e,depthBuffer:!0})}if(!this.causticsScene){let{mesh:e,uniforms:t}=bD();this.causticsScene=new Wi,this.causticsScene.add(e),this.causticsUniforms=t}this.webgl.setRenderTarget(this.causticsTarget),this.webgl.render(this.threeScene,e),this.webgl.setRenderTarget(null);let a=this.causticsUniforms;a.tColor.value=this.causticsTarget.texture,a.tDepth.value=this.causticsTarget.depthTexture,a.uInvViewProj.value.multiplyMatrices(e.projectionMatrix,e.matrixWorldInverse).invert(),e.getWorldPosition(a.uCameraPos.value),a.uWaterLevel.value=t.surfaceY,a.uTime.value=(globalThis.performance?.now()??0)*.001*t.caustics.speed,a.uCausticColor.value.set(t.caustics.color),a.uCausticIntensity.value=t.caustics.intensity,a.uCausticScale.value=t.caustics.scale,a.uMaxDist.value=t.visibility,this.webgl.render(this.causticsScene,e)}screenFromWorld(e,t,n){let r=this.lastCamera;return r?(wD.set(e,t,n).project(r),{x:(wD.x+1)/2*this.lastSize.w,y:(1-wD.y)/2*this.lastSize.h,behind:wD.z>1}):{x:0,y:0,behind:!0}}pick(e,t){let n=this.lastCamera;if(!n)return null;AD.set(e/this.lastSize.w*2-1,-(t/this.lastSize.h*2-1)),kD.setFromCamera(AD,n);let r=kD.intersectObjects(this.threeScene.children,!0);for(let e of r){let t=e.object;for(;t&&!t.userData.incantoNode;)t=t.parent;let n=t?.userData.incantoNode;if(n)return n}return null}stats(){let e=this.webgl.info;return{triangles:e.render.triangles,drawCalls:e.render.calls,geometries:e.memory.geometries,textures:e.memory.textures}}cameraBasis(){let e=this.lastCamera,t=e?e.getWorldQuaternion(DD):DD.identity();return{right:new H(1,0,0).applyQuaternion(t),up:new H(0,1,0).applyQuaternion(t),forward:new H(0,0,-1).applyQuaternion(t)}}dispose(){this.disconnect(),this.threeScene.traverse(e=>{let t=e;if(!(!t.isMesh||t.userData?.incantoModelShared))if(t.geometry?.dispose(),Array.isArray(t.material))for(let e of t.material)e.dispose();else t.material?.dispose()}),this.causticsTarget?.depthTexture?.dispose(),this.causticsTarget?.dispose();let e=this.causticsScene?.children[0];e?.geometry?.dispose(),e?.material?.dispose(),this.cloudsTarget?.depthTexture?.dispose(),this.cloudsTarget?.dispose(),this.cloudsHalfTarget?.dispose(),this.cloudsBlurTarget?.dispose();for(let e of[this.cloudsScene,this.cloudsBlurScene,this.cloudsCompositeScene]){let t=e?.children[0];t?.geometry?.dispose(),t?.material?.dispose()}this.ownsAssets&&this.assets.dispose(),this.environment.dispose(),this.webgl.dispose()}},SD=new B,CD=new H,wD=new H,TD=new H,ED=new H,DD=new V,OD=new H,kD=new pl,AD=new B,jD=e({DEFAULT_TERRAIN_TEXTURE_BASE:()=>xy,WATER_MAX_RIPPLES:()=>8,enablePhysics3D:()=>By}),MD=new WeakMap,ND=class e{account;roomId;roomState=new t;allUserStates=new t;userJoined=new t;userLeft=new t;globalState=new t;globalMyState=new t;asset=new t;latestUserStates={};latestRoomState={};latestGlobalState={};latestGlobalMyState={};latestAsset={};server;engine;throttleMs;subs=[];messageSignals=new Map;collectionSignals=new Map;latestCollections=new Map;globalMessageSignals=new Map;globalCollectionSignals=new Map;latestGlobalCollections=new Map;scenes=new Map;lastSent=new Map;sendAccumulator=0;detachReplication=null;lastOwner=null;boundScene=null;static get(e){return MD.get(e)??null}static async create(t,n={}){await MD.get(t)?.dispose();let r=n.transport??await RD(n.config);await r.connect();let i=n.room??t.scene?.multiplayer?.room??`auto`,a=new e(t,r,await r.remoteFunction(`joinRoom`,[i===`auto`?void 0:i],{needResponse:!0}),n.throttleMs??50);return MD.set(t,a),a}constructor(e,t,n,r){this.engine=e,this.server=t,this.roomId=n,this.account=t.account,this.throttleMs=Math.max(30,r),this.boundScene=e.scene,this.subs.push(t.subscribeRoomState(n,e=>{this.latestRoomState=e,this.roomState.emit(e)}),t.subscribeRoomAllUserStates(n,e=>{this.latestUserStates=e,this.allUserStates.emit(e)}),t.onRoomUserJoin(n,e=>this.userJoined.emit(e)),t.onRoomUserLeave(n,e=>this.userLeft.emit(e))),t.subscribeGlobalState&&this.subs.push(t.subscribeGlobalState(e=>{this.latestGlobalState=e,this.globalState.emit(e)})),t.subscribeGlobalMyState&&this.subs.push(t.subscribeGlobalMyState(e=>{this.latestGlobalMyState=e,this.globalMyState.emit(e)})),t.subscribeAsset&&this.subs.push(t.subscribeAsset(this.account,e=>{this.latestAsset=e,this.asset.emit(e)})),this.detachReplication=e.fixedUpdated.connect(e=>this.replicate(e))}message(e){let n=this.messageSignals.get(e);return n||(n=new t,this.messageSignals.set(e,n),this.subs.push(this.server.onRoomMessage(this.roomId,e,e=>n?.emit(e)))),n}collection(e){let n=this.collectionSignals.get(e);return n||(n=new t,this.collectionSignals.set(e,n),this.subs.push(this.server.subscribeRoomCollection(this.roomId,e,t=>{this.latestCollections.set(e,t),n?.emit(t)}))),n}latestCollection(e){return this.collection(e),this.latestCollections.get(e)??{}}globalMessage(e){let n=this.globalMessageSignals.get(e);if(!n){n=new t,this.globalMessageSignals.set(e,n);let r=this.server.onGlobalMessage?.(e,e=>n?.emit(e));r&&this.subs.push(r)}return n}globalCollection(e){let n=this.globalCollectionSignals.get(e);if(!n){n=new t,this.globalCollectionSignals.set(e,n);let r=this.server.subscribeGlobalCollection?.(e,t=>{this.latestGlobalCollections.set(e,t),n?.emit(t)});r&&this.subs.push(r)}return n}latestGlobalCollection(e){return this.globalCollection(e),this.latestGlobalCollections.get(e)??{}}setMyState(e){return this.server.remoteFunction(`setMyState`,[this.roomId,e],{throttle:this.throttleMs,throttleKey:`incanto:myState`})}patchRoomState(e){return this.server.remoteFunction(`patchRoomState`,[this.roomId,e])}addEntity(e,t){return this.server.remoteFunction(`addEntity`,[this.roomId,e,t])}updateEntity(e,t,n){return this.server.remoteFunction(`updateEntity`,[this.roomId,e,t,n])}removeEntity(e,t){return this.server.remoteFunction(`removeEntity`,[this.roomId,e,t])}sendEvent(e,t){return this.server.remoteFunction(`sendEvent`,[this.roomId,e,t])}call(e,...t){return this.server.remoteFunction(e,[this.roomId,...t],{needResponse:!0})}registerScene(e,t){this.scenes.set(e,t)}resolveScene(e){let t=this.scenes.get(e);if(!t)throw new y(`UNRESOLVED_INSTANCE`,`No scene registered for '${e}'. Registered: [${[...this.scenes.keys()].join(`, `)}]. Call manager.registerScene('${e}', sceneJson).`);return t}async dispose(){this.detachReplication?.();for(let e of this.subs)e();await this.server.remoteFunction(`leaveRoom`,[this.roomId]),MD.delete(this.engine)}replicate(e){let t=this.engine.scene;if(!t||t!==this.boundScene)return;this.sendAccumulator+=e*1e3;let n=PD(t.root,!0);if(!n)return;n!==this.lastOwner&&(this.lastOwner=n,this.lastSent.clear());let r=n.network,i=Math.max(30,typeof r.throttleMs==`number`?r.throttleMs:this.throttleMs),a=Array.isArray(r.sync)?r.sync:[],o={};for(let e of a){let t=ID(n,e);t!==void 0&&(se(this.lastSent.get(e)??null,t)||(o[e]=j(t)))}if(!(this.sendAccumulator<i)&&Object.keys(o).length!==0){for(let[e,t]of Object.entries(o))this.lastSent.set(e,j(t));this.sendAccumulator=0,this.setMyState({sync:o})}}};function PD(e,t=!1){let n=[];if(FD(e,n),t&&n.length>1)throw new y(`BAD_FORMAT`,`Multiple network owner nodes found (${n.map(e=>e.getPath()).join(`, `)}). Exactly ONE node per player may declare network.mode 'owner' — replicate spawned entities through collections instead.`);return n[0]??null}function FD(e,t){e.network?.mode===`owner`&&t.push(e);for(let n of e.children)FD(n,t)}function ID(e,t){let n=t.lastIndexOf(`.`),r=n===-1?e:e.getNodeOrNull(t.slice(0,n))??void 0,i=n===-1?t:t.slice(n+1);if(r)return r[i]}function LD(e,t,n){for(let[r,i]of Object.entries(t)){let t=r.lastIndexOf(`.`),a=t===-1?e:e.getNodeOrNull(r.slice(0,t)),o=t===-1?r:r.slice(t+1);a&&n(a,o,i)}}async function RD(e){let{createAgent8Server:t}=await Xf(async()=>{let{createAgent8Server:e}=await import(`./agent8-CXBTOrkj.js`);return{createAgent8Server:e}},[],import.meta.url);return t(e)}var zD=class extends Ae{static typeName=`NetworkSpawner`;static signals=[`spawned`,`despawned`];static props={source:{default:`users`},scene:{default:``},interpolate:{default:!0}};source=`users`;scene=``;interpolate=!0;spawned=new Map;positionTargets=new Map;failedKeys=new Set;update(e){let t=this.tree?.engine;if(!t)return;let n=ND.get(t);if(!n||this.scene===``)return;let r=this.currentEntries(n);for(let[e,t]of Object.entries(r)){let r=this.spawned.get(e);if(!r){if(this.failedKeys.has(e))continue;try{r=ot(n.resolveScene(this.scene).root)}catch(t){throw this.failedKeys.add(e),t}r.name=BD(e),this.addChild(r),this.spawned.set(e,r),this.emit(`spawned`,r,e)}let i=t.sync;i&&LD(r,i,(e,t,n)=>this.setProp(e,t,n))}for(let[e,t]of[...this.spawned.entries()])e in r||(this.spawned.delete(e),this.positionTargets.delete(t),this.emit(`despawned`,t,e),t.free());this.interpolate&&this.stepInterpolation(e)}currentEntries(e){if(this.source===`users`){let t={};for(let[n,r]of Object.entries(e.latestUserStates))n!==e.account&&(t[n]=r);return t}return this.source.startsWith(`collection:`)?e.latestCollection(this.source.slice(11)):{}}setProp(e,t,n){if(this.interpolate&&t===`position`&&Array.isArray(n)){this.positionTargets.set(e,n);return}e[t]=n}stepInterpolation(e){let t=Math.min(1,e*8);for(let[e,n]of this.positionTargets){let r=e.position;Array.isArray(r)&&(e.position=r.map((e,r)=>e+((n[r]??e)-e)*t))}}};function BD(e){return e.replace(/[/%]/g,`_`)||`remote`}function VD(){Ke(),M(zD)}var HD=80,UD=` `;function WD(e){return`${GD(e,0)}\n`}function GD(e,t){if(typeof e!=`object`||!e)return JSON.stringify(e);let n=KD(e);if(t*2+n.length<=HD)return n;let r=UD.repeat(t+1),i=UD.repeat(t);if(Array.isArray(e))return e.length===0?`[]`:`[\n${e.map(e=>r+GD(e,t+1)).join(`,
|
|
7354
|
+
`)}\n${i}]`;let a=Object.entries(e);return a.length===0?`{}`:`{\n${a.map(([e,n])=>`${r}${JSON.stringify(e)}: ${GD(n,t+1)}`).join(`,
|
|
7355
|
+
`)}\n${i}}`}function KD(e){if(typeof e!=`object`||!e)return JSON.stringify(e);if(Array.isArray(e))return e.length===0?`[]`:`[${e.map(KD).join(`, `)}]`;let t=Object.entries(e);return t.length===0?`{}`:`{ ${t.map(([e,t])=>`${JSON.stringify(e)}: ${KD(t)}`).join(`, `)} }`}async function qD(e){if(!e.ok)throw Error(`HTTP ${e.status} — ${await e.text()}`);return e}async function JD(){return await(await qD(await fetch(`/api/meta`))).json()}async function YD(){return await(await qD(await fetch(`/api/scenes`))).json()}async function XD(e){return await(await qD(await fetch(`/api/scenes`,{method:`POST`,body:JSON.stringify({path:e})}))).json()}function ZD(e){return e?`/api/scene?file=${encodeURIComponent(e)}`:`/api/scene`}async function QD(e){return await(await qD(await fetch(ZD(e)))).json()}async function $D(e,t){await qD(await fetch(ZD(t),{method:`PUT`,body:WD(e)}))}var eO=localStorage.getItem(`incanto-editor-lang`)??`en`,tO=new Set;function nO(){return eO}function rO(e){eO=e,localStorage.setItem(`incanto-editor-lang`,e);for(let e of tO)e()}function iO(e){tO.add(e)}function aO(e){return e[eO]}var oO={node:{paths:[`M5 5h14v14H5z`]},move:{paths:[`M12 3v18M3 12h18`,`M8 7l4-4 4 4M8 17l4 4 4-4M7 8l-4 4 4 4M17 8l4 4-4 4`]},image:{paths:[`M4 5h16v14H4z`,`M4 15l5-5 4 4 3-3 4 4`],circles:[[9,9,1.6]]},film:{paths:[`M4 4h16v16H4z`,`M4 9h16M4 15h16`,`M9 4v16M15 4v16`]},video:{paths:[`M3 7h12v10H3z`,`M15 10l6-3v10l-6-3`]},text:{paths:[`M5 7V5h14v2`,`M12 5v14`,`M9 19h6`]},layers:{paths:[`M12 3 3 8l9 5 9-5-9-5z`,`M3 12l9 5 9-5`,`M3 16l9 5 9-5`]},square:{paths:[`M5 5h14v14H5z`,`M5 12h14`]},ball:{paths:[`M12 8v0`],circles:[[12,12,8],[12,12,1.4]]},person:{paths:[`M5 21v-1a7 7 0 0 1 14 0v1`],circles:[[12,7,4]]},area:{paths:[`M5 5h14v14H5z`],dashed:!0},gamepad:{paths:[`M7 9h-0M6 12h4M8 10v4`,`M4 8h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2z`],circles:[[16,11,1],[18.5,13.5,1]]},clock:{paths:[`M12 7v5l3 2`],circles:[[12,12,9]]},cube:{paths:[`M21 16V8l-9-5-9 5v8l9 5 9-5z`,`M3.3 7.5 12 12.5l8.7-5`,`M12 22V12.5`]},sun:{paths:[`M12 2v3M12 19v3M2 12h3M19 12h3M4.9 4.9l2.1 2.1M17 17l2.1 2.1M19.1 4.9 17 7M7 17l-2.1 2.1`],circles:[[12,12,4]]},bulb:{paths:[`M9 18h6M10 21h4`,`M8 13a6 6 0 1 1 8 0c-1 1-1.5 2-1.5 3h-5c0-1-.5-2-1.5-3z`]},globe:{paths:[`M2 12h20`,`M12 2a15 15 0 0 1 0 20a15 15 0 0 1 0-20z`],circles:[[12,12,10]]},link:{paths:[`M10 14a5 5 0 0 0 7 0l3-3a5 5 0 0 0-7-7l-1 1`,`M14 10a5 5 0 0 0-7 0l-3 3a5 5 0 0 0 7 7l1-1`]},speaker:{paths:[`M11 5 6 9H3v6h3l5 4V5z`,`M15.5 8.5a5 5 0 0 1 0 7M18.5 5.5a9 9 0 0 1 0 13`]},swatch:{paths:[`M4 4h16v16H4z`,`M4 4l16 16`]},sparkles:{paths:[`M12 4v5M12 15v5M5 12h5M14 12h5`],circles:[[6,5,1],[18,19,1]]},waves:{paths:[`M2 8c3-3 5 3 8 0s5 3 8 0`,`M2 14c3-3 5 3 8 0s5 3 8 0`,`M2 20c3-3 5 3 8 0s5 3 8 0`]},plant:{paths:[`M12 21v-8`,`M12 13c0-4-3-6-7-6 0 4 3 6 7 6z`,`M12 11c0-4 3-6 7-6 0 4-3 6-7 6z`]},flower:{paths:[`M12 21v-7`],circles:[[12,9,2],[12,4.5,2.2],[16.3,7.5,2.2],[14.7,12.6,2.2],[9.3,12.6,2.2],[7.7,7.5,2.2]]},voxels:{paths:[`M4 14h8v8H4z`,`M12 14h8v8h-8z`,`M8 6h8v8H8z`]}},sO={Node:`node`,Timer:`clock`,AudioPlayer:`speaker`,ColorRect2D:`swatch`,Particles2D:`sparkles`,Particles3D:`sparkles`,Water3D:`waves`,Foliage3D:`plant`,Flowers3D:`flower`,VoxelGrid3D:`voxels`,ModelInstance3D:`person`,CharacterController3D:`gamepad`,Node2D:`move`,Sprite2D:`image`,AnimatedSprite2D:`film`,Camera2D:`video`,Label:`text`,UILayer:`layers`,StaticBody2D:`square`,RigidBody2D:`ball`,CharacterBody2D:`person`,Area2D:`area`,CharacterController2D:`gamepad`,Node3D:`move`,MeshInstance3D:`cube`,Camera3D:`video`,DirectionalLight3D:`sun`,OmniLight3D:`bulb`,StaticBody3D:`square`,RigidBody3D:`ball`,CharacterBody3D:`person`,Area3D:`area`,NetworkSpawner:`globe`};function cO(e){let t=oO[sO[e??``]??(e?`node`:`link`)],n=document.createElementNS(`http://www.w3.org/2000/svg`,`svg`);n.setAttribute(`viewBox`,`0 0 24 24`),n.setAttribute(`width`,`13`),n.setAttribute(`height`,`13`),n.setAttribute(`fill`,`none`),n.setAttribute(`stroke`,`currentColor`),n.setAttribute(`stroke-width`,`2`),n.setAttribute(`stroke-linecap`,`round`),n.setAttribute(`stroke-linejoin`,`round`),t.dashed&&n.setAttribute(`stroke-dasharray`,`3 2.4`);for(let e of t.paths){let t=document.createElementNS(`http://www.w3.org/2000/svg`,`path`);t.setAttribute(`d`,e),n.appendChild(t)}for(let[e,r,i]of t.circles??[]){let t=document.createElementNS(`http://www.w3.org/2000/svg`,`circle`);t.setAttribute(`cx`,String(e)),t.setAttribute(`cy`,String(r)),t.setAttribute(`r`,String(i)),n.appendChild(t)}return n}function lO(){let e=document.createElementNS(`http://www.w3.org/2000/svg`,`svg`);e.setAttribute(`viewBox`,`0 0 24 24`),e.setAttribute(`width`,`12`),e.setAttribute(`height`,`12`),e.setAttribute(`fill`,`none`),e.setAttribute(`stroke`,`currentColor`),e.setAttribute(`stroke-width`,`2.4`),e.setAttribute(`aria-hidden`,`true`);let t=document.createElementNS(`http://www.w3.org/2000/svg`,`path`);return t.setAttribute(`d`,`m6 9 6 6 6-6`),e.appendChild(t),e}var uO=[{type:`texture`,label:`image (texture)`,metaHints:[`filter`]},{type:`spritesheet`,label:`spritesheet`,metaHints:[`filter`,`frameWidth`,`frameHeight`]},{type:`model`,label:`3D model (GLB/VRM)`,metaHints:[]},{type:`animation`,label:`animation (GLB clips)`,metaHints:[`clip`]}],dO=new Set,fO=null,pO=null;function mO(e,t,n){let r=document.createElement(`input`);r.className=`rename-input`,r.value=e;let i=()=>{let i=r.value.trim().replace(/\//g,``);i&&i!==e?t(i):n()};r.addEventListener(`keydown`,e=>{e.stopPropagation(),e.key===`/`&&e.preventDefault(),e.key===`Enter`&&i(),e.key===`Escape`&&n()});for(let e of[`click`,`pointerdown`,`dblclick`,`mousedown`])r.addEventListener(e,e=>e.stopPropagation());return r.addEventListener(`blur`,i),queueMicrotask(()=>{r.focus(),r.select()}),r}function hO(e,t){let n={path:``,name:``,folders:new Map,assets:[]},r=e=>{let t=n;if(e===``)return t;for(let n of e.split(`/`)){let e=t.folders.get(n);e||(e={path:t.path?`${t.path}/${n}`:n,name:n,folders:new Map,assets:[]},t.folders.set(n,e)),t=e}return t};for(let t of e){let e=t.lastIndexOf(`/`);r(e===-1?``:t.slice(0,e)).assets.push(t)}for(let e of t)r(e);return n}function gO(e){let t=e.assets.length;for(let n of e.folders.values())t+=gO(n);return t}function _O(e,t){e.textContent=``;let n=t.working.assets??{},r=Object.keys(n);if(r.length===0&&t.pendingGroups.size===0&&!t.addingAsset&&!t.addingGroup){let t=document.createElement(`div`);t.className=`muted-note explorer-empty`,t.textContent=`no assets yet — + adds textures, models, animations`,e.appendChild(t);return}let i=(e,n)=>{e.addEventListener(`dragover`,t=>{t.preventDefault(),t.stopPropagation(),e.classList.add(`drop-target`)}),e.addEventListener(`dragleave`,()=>e.classList.remove(`drop-target`)),e.addEventListener(`drop`,r=>{r.preventDefault(),r.stopPropagation(),e.classList.remove(`drop-target`);let i=r.dataTransfer?.getData(`text/incanto-asset`);i&&t.moveAsset(i,n);let a=r.dataTransfer?.getData(`text/incanto-group`);a&&t.moveGroup(a,n)})};i(e,``);let a=(e,r)=>{let i=n[e],a=e.includes(`/`)?e.slice(e.lastIndexOf(`/`)+1):e,o=document.createElement(`div`);o.className=`tree-row asset-row${t.selectedAsset===e?` selected`:``}`,o.draggable=!0,o.addEventListener(`dragstart`,t=>{t.dataTransfer?.setData(`text/incanto-asset`,e)});let s=document.createElement(`span`);if(s.className=`tree-icon`,s.appendChild(kO(i.type??``)),s.addEventListener(`click`,e=>{e.stopPropagation(),AO(s,i.type??``)}),fO===e){let n=e.includes(`/`)?e.slice(0,e.lastIndexOf(`/`)+1):``;o.appendChild(mO(a,r=>{fO=null,t.renameAssetKey(e,n+r)},()=>{fO=null,t.selectAsset(t.selectedAsset)})),o.draggable=!1,o.insertBefore(s,o.firstChild),r.appendChild(o);return}let c=document.createElement(`span`);c.className=`tree-name asset-key`,c.textContent=`$${a}`,c.title=`$${e}`,c.addEventListener(`dblclick`,n=>{n.stopPropagation(),fO=e,t.selectAsset(e)}),o.append(s,c),o.addEventListener(`click`,()=>t.selectAsset(e)),r.appendChild(o)},o=(e,n)=>{let r=dO.has(e.path),s=document.createElement(`div`);s.className=`asset-folder${r?` collapsed`:``}${t.selectedGroup===e.path?` selected`:``}`,s.draggable=!0,s.addEventListener(`dragstart`,t=>{t.stopPropagation(),t.dataTransfer?.setData(`text/incanto-group`,e.path)});let c=document.createElement(`span`);c.className=`chev`,c.appendChild(lO());let l=document.createElementNS(`http://www.w3.org/2000/svg`,`svg`);l.setAttribute(`viewBox`,`0 0 24 24`),l.setAttribute(`width`,`12`),l.setAttribute(`height`,`12`),l.setAttribute(`fill`,`none`),l.setAttribute(`stroke`,`currentColor`),l.setAttribute(`stroke-width`,`1.8`),l.setAttribute(`aria-hidden`,`true`);let u=document.createElementNS(`http://www.w3.org/2000/svg`,`path`);if(u.setAttribute(`d`,`M3 6a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z`),l.appendChild(u),pO===e.path){let i=e.path.includes(`/`)?e.path.slice(0,e.path.lastIndexOf(`/`)+1):``;if(s.append(c,l,mO(e.name,n=>{pO=null,t.renameGroup(e.path,i+n)},()=>{pO=null,t.selectGroup(t.selectedGroup)})),s.draggable=!1,n.appendChild(s),!r){let t=document.createElement(`div`);t.className=`tree-children`;for(let n of e.folders.values())o(n,t);for(let n of e.assets)a(n,t);t.children.length>0&&n.appendChild(t)}return}let d=document.createElement(`span`);d.textContent=e.name,d.addEventListener(`dblclick`,n=>{n.stopPropagation(),pO=e.path,t.selectGroup(e.path)});let f=document.createElement(`span`);if(f.className=`count`,f.textContent=`(${gO(e)})`,s.append(c,l,d,f),s.addEventListener(`click`,()=>{dO.has(e.path)?dO.delete(e.path):dO.add(e.path),t.selectGroup(e.path)}),i(s,e.path),n.appendChild(s),!r){let t=document.createElement(`div`);t.className=`tree-children`;for(let n of e.folders.values())o(n,t);for(let n of e.assets)a(n,t);t.children.length>0&&n.appendChild(t)}},s=hO(r,t.pendingGroups);for(let t of s.assets)a(t,e);for(let t of s.folders.values())o(t,e)}function vO(e,t,n){if(t.selection!==null)return!1;if(t.addingGroup){e.appendChild(PO(t.newGroupParent?`NEW GROUP IN ${t.newGroupParent}/`:`NEW GROUP`));let n=document.createElement(`input`);n.placeholder=t.newGroupParent?`heroes`:`characters (or a/b to nest)`,n.className=`mono`,e.appendChild(wO(`name`,n));let r=document.createElement(`button`);r.type=`button`,r.className=`primary`,r.textContent=`add`;let i=()=>{let e=n.value.trim().replace(/^\/+|\/+$/g,``);e&&(t.cancelAddForms(),t.addGroup(t.newGroupParent?`${t.newGroupParent}/${e}`:e))};r.addEventListener(`click`,i),n.addEventListener(`keydown`,e=>{e.key===`Enter`&&i()});let a=document.createElement(`div`);return a.className=`pop-actions`,a.appendChild(r),e.appendChild(a),setTimeout(()=>n.focus(),0),!0}if(t.addingAsset)return SO(e,t);if(t.selectedGroup!==null){let r=t.selectedGroup,i=t.groupCount(r);e.appendChild(PO(`GROUP (${i} asset${i===1?``:`s`})`));let a=r.includes(`/`)?r.slice(0,r.lastIndexOf(`/`)+1):``,o=r.includes(`/`)?r.slice(r.lastIndexOf(`/`)+1):r;e.appendChild(TO(`name`,o,e=>{let n=e.trim().replace(/\/+/g,``);n&&n!==o&&t.renameGroup(r,a+n)}));let s=document.createElement(`button`);return s.type=`button`,s.className=`ghost danger`,s.textContent=`delete group`,s.addEventListener(`click`,()=>{if(i===0){t.deleteGroup(r);return}n(`'${r}/' contains ${i} asset${i===1?``:`s`} — deleting the group deletes them too.`,`delete group & assets`,()=>t.deleteGroup(r))}),e.appendChild(s),!0}let r=t.selectedAsset;if(r===null)return!1;let i=t.working.assets??{};if(!(r in i))return!1;e.appendChild(PO(`ASSET — ${String(i[r].type??`?`)}`)),e.appendChild(jO(r)),e.appendChild(MO(t,r)),e.appendChild(yO(t,i,r));let a=document.createElement(`button`);return a.type=`button`,a.className=`ghost danger`,a.textContent=`delete asset`,a.addEventListener(`click`,()=>{t.selectAsset(null),t.mutate(()=>{delete i[r],Object.keys(i).length===0&&delete t.working.assets})}),e.appendChild(a),!0}function yO(e,t,n){let r=t[n],i=document.createElement(`div`);i.className=`asset-editor`;let a=n.includes(`/`)?n.slice(n.lastIndexOf(`/`)+1):n,o=n.includes(`/`)?n.slice(0,n.lastIndexOf(`/`)+1):``,s=TO(`name`,a,t=>{let r=t.trim().replace(/^\$/,``).replace(/\//g,``);r&&r!==a&&e.renameAssetKey(n,o+r)});NO(s.querySelector(`input`)),i.appendChild(s),i.appendChild(TO(`url`,String(r.url??``),t=>{e.mutate(()=>{r.url=t.trim()})}));let c=document.createElement(`div`);c.className=`muted-note`,c.textContent=`meta (key · value)`,i.appendChild(c);for(let[t,n]of Object.entries(r))t===`type`||t===`url`||i.appendChild(bO(e,r,t,n));return i.appendChild(xO(e,r)),i}function bO(e,t,n,r){let i=document.createElement(`div`);i.className=`meta-row`;let a=document.createElement(`input`);a.value=n,a.className=`mono`;let o=document.createElement(`input`);o.value=typeof r==`string`?r:JSON.stringify(r),o.className=`mono`;let s=()=>{let r=a.value.trim();e.mutate(()=>{delete t[n],r&&(t[r]=EO(o.value))})};a.addEventListener(`change`,s),o.addEventListener(`change`,s);let c=document.createElement(`button`);return c.type=`button`,c.className=`linklike danger-link`,c.textContent=`✕`,c.addEventListener(`click`,()=>{e.mutate(()=>{delete t[n]})}),i.append(a,o,c),i}function xO(e,t){let n=document.createElement(`div`);n.className=`meta-row`;let r=document.createElement(`input`);r.placeholder=`filter…`,r.className=`mono`;let i=document.createElement(`input`);i.placeholder=`nearest`,i.className=`mono`;let a=()=>{let n=r.value.trim();!n||i.value.trim()===``||e.mutate(()=>{t[n]=EO(i.value)})};return r.addEventListener(`change`,a),i.addEventListener(`change`,a),n.append(r,i,document.createElement(`span`)),n}function SO(e,t){return e.appendChild(PO(`NEW ASSET`)),e.appendChild(CO(t)),!0}function CO(e){let t=document.createElement(`div`);t.className=`asset-editor`;let n=document.createElement(`select`);for(let e of uO){let t=document.createElement(`option`);t.value=e.type,t.textContent=e.label,n.appendChild(t)}t.appendChild(wO(`type`,n));let r=document.createElement(`input`);r.placeholder=`(optional) characters`,r.className=`mono`,r.value=e.newAssetGroup;let i=`asset-groups-list`;r.setAttribute(`list`,i);let a=document.createElement(`datalist`);a.id=i;let o=new Set;for(let t of Object.keys(e.working.assets??{})){let e=t.lastIndexOf(`/`);e!==-1&&o.add(t.slice(0,e))}for(let t of e.pendingGroups)o.add(t);for(let e of o){let t=document.createElement(`option`);t.value=e,a.appendChild(t)}t.appendChild(wO(`group`,r)),t.appendChild(a);let s=document.createElement(`input`);s.placeholder=`coin`,s.className=`mono`,NO(s),t.appendChild(wO(`key`,s));let c=document.createElement(`input`);c.placeholder=`/textures/coin.png · https://… · data:…`,c.className=`mono`,t.appendChild(wO(`url`,c));let l=document.createElement(`textarea`);l.rows=2,l.placeholder=`or paste a JSON object: { "type": "model", "url": "/m.glb" }`,t.appendChild(l);let u=document.createElement(`button`);u.type=`button`,u.className=`primary`,u.textContent=`add`,u.addEventListener(`click`,()=>{let t=s.value.trim().replace(/^\$/,``);if(!t)return;let i=r.value.trim().replace(/\/+$/,``),a=i?`${i}/${t}`:t,o=null,u=l.value.trim();if(u)try{o=JSON.parse(u)}catch{l.classList.add(`invalid`);return}else c.value.trim()&&(o={type:n.value,url:c.value.trim()});o&&(e.cancelAddForms(),e.selectedAsset=a,e.mutate(()=>{e.working.assets||(e.working.assets={});let t=e.working.assets;t[a]=o}))});let d=document.createElement(`div`);return d.className=`pop-actions`,d.appendChild(u),t.appendChild(d),t}function wO(e,t){let n=document.createElement(`label`);n.className=`field`;let r=document.createElement(`span`);return r.textContent=e,n.append(r,t),n}function TO(e,t,n){let r=document.createElement(`input`);return r.value=t,r.className=`mono`,r.addEventListener(`change`,()=>n(r.value)),wO(e,r)}function EO(e){let t=e.trim();if(t===`true`)return!0;if(t===`false`)return!1;if(t!==``&&Number.isFinite(Number(t)))return Number(t);if(t.startsWith(`{`)||t.startsWith(`[`))try{return JSON.parse(t)}catch{return e}return e}var DO={texture:`M3 5h18v14H3z M3 15l5-5 4 4 3-3 6 6 M8.5 9.5h.01`,spritesheet:`M3 4h18v16H3z M9 4v16 M15 4v16 M3 12h18`,model:`M12 2l9 5v10l-9 5-9-5V7z M12 12l9-5 M12 12L3 7 M12 12v10`,animation:`M12 2a10 10 0 1 0 10 10 M22 12l-3-3m3 3l3-3 M12 7v5l3 3`},OO={texture:{en:`image (texture) — Sprite2D.texture references it as "$key".`,ko:`이미지(텍스처) — Sprite2D.texture에서 "$키"로 참조합니다.`},spritesheet:{en:`spritesheet — AnimatedSprite2D slices it into named frame animations.`,ko:`스프라이트시트 — AnimatedSprite2D가 이름 붙은 프레임 애니메이션으로 자릅니다.`},model:{en:`3D model (GLB/glTF/VRM) — ModelInstance3D.model references it as "$key".`,ko:`3D 모델(GLB/glTF/VRM) — ModelInstance3D.model에서 "$키"로 참조합니다.`},animation:{en:`animation clips (GLB, in memory — drawn nowhere) — any model can play them; mixamo retargets onto VRM.`,ko:`애니메이션 클립(GLB, 메모리 전용 — 그려지지 않음) — 어떤 모델이든 재생 가능, mixamo는 VRM에 자동 리타게팅.`}};function kO(e){let t=document.createElementNS(`http://www.w3.org/2000/svg`,`svg`);t.setAttribute(`viewBox`,`0 0 24 24`),t.setAttribute(`width`,`13`),t.setAttribute(`height`,`13`),t.setAttribute(`fill`,`none`),t.setAttribute(`stroke`,`currentColor`),t.setAttribute(`stroke-width`,`1.8`),t.setAttribute(`aria-hidden`,`true`);let n=document.createElementNS(`http://www.w3.org/2000/svg`,`path`);return n.setAttribute(`d`,DO[e]??`M4 4h16v16H4z`),t.appendChild(n),t.classList.add(`asset-icon-${e}`),t}function AO(e,t){document.querySelector(`.balloon`)?.remove();let n=OO[t];if(!n)return;let r=document.createElement(`div`);r.className=`balloon floating`;let i=document.createElement(`div`);i.className=`balloon-title`,i.textContent=t;let a=document.createElement(`span`);a.textContent=aO(n),r.append(i,a),document.body.appendChild(r);let o=e.getBoundingClientRect();r.style.left=`${o.right+8}px`,r.style.top=`${Math.max(8,o.top-8)}px`;let s=()=>{r.remove(),document.removeEventListener(`pointerdown`,s,!0)};setTimeout(()=>document.addEventListener(`pointerdown`,s,!0),0)}function jO(e){let t=document.createElement(`div`);t.className=`field uid-line`;let n=document.createElement(`span`);n.textContent=`key`;let r=document.createElement(`div`);r.className=`uid-value`;let i=document.createElement(`code`);i.textContent=`$${e}`;let a=document.createElement(`button`);return a.type=`button`,a.className=`uid-copy`,a.title=`Copy key`,a.innerHTML=`<svg aria-hidden="true" width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>`,a.addEventListener(`click`,()=>{navigator.clipboard?.writeText(`$${e}`),a.classList.add(`copied`),setTimeout(()=>a.classList.remove(`copied`),600)}),r.append(i,a),t.append(n,r),t}function MO(e,t){let n=t.includes(`/`)?t.slice(0,t.lastIndexOf(`/`)):``,r=document.createElement(`select`),i=new Set([``]);for(let t of Object.keys(e.working.assets??{})){let e=t.split(`/`);for(let t=1;t<e.length;t++)i.add(e.slice(0,t).join(`/`))}for(let t of e.pendingGroups)i.add(t);for(let e of[...i].sort()){let t=document.createElement(`option`);t.value=e,t.textContent=e===``?`(root)`:`${e}/`,e===n&&(t.selected=!0),r.appendChild(t)}r.addEventListener(`change`,()=>{e.moveAsset(t,r.value)});let a=document.createElement(`label`);a.className=`field`;let o=document.createElement(`span`);return o.textContent=`group`,a.append(o,r),a}function NO(e){e&&(e.addEventListener(`keydown`,e=>{e.key===`/`&&e.preventDefault()}),e.addEventListener(`input`,()=>{e.value.includes(`/`)&&(e.value=e.value.replace(/\//g,``))}))}function PO(e){let t=document.createElement(`div`);t.className=`section-title`,t.textContent=e;let n=document.createElement(`span`);return n.className=`rule`,t.appendChild(n),t}var FO=window.parent!==window,IO=new URLSearchParams(window.location.search).get(`parentOrigin`),LO=!1;function RO(e){if(FO){if(!IO){LO||(LO=!0,console.warn(`incanto-editor: embedded without ?parentOrigin=<origin> — postMessage disabled`));return}window.parent.postMessage(e,IO)}}var zO={ready(e,t,n){RO({type:`incanto-editor:ready`,input:e,output:t,version:n})},open(e,t){RO({type:`incanto-editor:open`,input:e,output:t})},change(e){RO({type:`incanto-editor:change`,dirty:e})},save(e,t,n){RO({type:`incanto-editor:save`,input:e,output:t,data:n})},error(e){RO({type:`incanto-editor:error`,message:e})}},BO=[[`Core`,e=>!e.endsWith(`2D`)&&!e.endsWith(`3D`)&&e!==`Label`&&e!==`UILayer`&&e!==`NetworkSpawner`],[`2D`,e=>e.endsWith(`2D`)&&!/Body|Area|Controller/.test(e)||e===`Label`||e===`UILayer`],[`2D Physics`,e=>e.endsWith(`2D`)&&/Body|Area|Controller/.test(e)],[`3D`,e=>e.endsWith(`3D`)&&!/Body|Area|Controller/.test(e)],[`3D Physics`,e=>e.endsWith(`3D`)&&/Body|Area|Controller/.test(e)],[`Network`,e=>e===`NetworkSpawner`]],VO=[...BO,[`Other`,e=>BO.every(([,t])=>!t(e))]],HO={title:{en:`How Incanto scenes work`,ko:`Incanto 씬은 어떻게 동작하나`},sections:[{heading:{en:`Everything is a node in a tree`,ko:`모든 것은 트리 위의 노드`},text:{en:`A scene is ONE tree of typed nodes (Godot-style). Each node has a name (unique among its siblings), a type that defines its props and behavior, optional children, and an optional scene-wide-unique uid. The whole structure lives in a *.scene.json file — there is no hidden state: what you see in this editor IS the file.`,ko:`씬은 타입을 가진 노드들의 단일 트리입니다(Godot 방식). 각 노드는 이름(형제 간 유일), props와 동작을 결정하는 타입, 선택적 자식들, 그리고 선택적인 씬 전역 유일 uid를 가집니다. 전체 구조가 *.scene.json 파일 하나에 들어있고 숨겨진 상태는 없습니다 — 이 에디터에서 보는 것이 곧 파일 그 자체입니다.`}},{heading:{en:`Props are delta-only`,ko:`Props는 변경분만 기록`},text:{en:`Every prop has a default defined by the engine. The JSON only stores values that DIFFER from the default — so files stay small and diffs stay meaningful. This editor follows the same rule: set a value back to its default and it disappears from the file.`,ko:`모든 prop에는 엔진이 정의한 기본값이 있습니다. JSON에는 기본값과 다른 값만 기록되어 파일이 작고 diff가 의미를 가집니다. 이 에디터도 같은 규칙을 따릅니다 — 값을 기본값으로 되돌리면 파일에서 사라집니다.`}},{heading:{en:`Addressing: paths, names, uid, groups`,ko:`노드 찾기: 경로·이름·uid·그룹`},text:{en:`Code reaches nodes four ways: a path like "Player/Skin" (relative) or "%Unique" (marked-unique name), getNodesByName("Enemy") which returns a LIST (names repeat across the tree), getNodeByUid("n_x1y2") which returns exactly one node and survives moves/renames, and groups — free tags for queries like "every coin".`,ko:`코드는 네 가지 방법으로 노드에 접근합니다: "Player/Skin" 같은 경로(상대) 또는 "%Unique"(유일 표시 이름), 트리 전체에서 같은 이름을 모두 찾는 getNodesByName("Enemy") — 이름은 중복될 수 있어 리스트가 돌아옵니다 —, 정확히 한 노드를 돌려주고 이동/개명에도 살아남는 getNodeByUid("n_x1y2"), 그리고 "모든 코인"처럼 묶어 조회하는 자유 태그 groups.`}},{heading:{en:`Signals connect, behaviors act`,ko:`시그널로 잇고 비헤이비어로 움직인다`},text:{en:`Nodes emit signals (an Area2D fires triggerEnter, a Timer fires timeout). The scene's "connections" wire a signal to a handler — declaratively, in JSON. Game logic itself is a Behavior: a TypeScript class in YOUR game, attached by name via "script". The editor shows and edits these links but the code lives in the game.`,ko:`노드는 시그널을 발산합니다(Area2D의 triggerEnter, Timer의 timeout). 씬의 "connections"가 시그널을 핸들러에 선언적으로(JSON으로) 연결합니다. 게임 로직 자체는 Behavior — 게임 쪽 TypeScript 클래스이며 "script"에 이름으로 연결됩니다. 에디터는 이 연결을 보여주고 편집하지만 코드는 게임에 있습니다.`}},{heading:{en:`Assets are declared, then referenced`,ko:`에셋은 선언하고 $키로 참조`},text:{en:`The scene header declares assets (textures, spritesheets, 3D models, animation clips) under a key; nodes reference them as "$key". Animations are data too: an {type:"animation"} asset loads GLB clips into memory and any ModelInstance3D can play them — Mixamo clips retarget onto VRM avatars automatically.`,ko:`씬 헤더에서 에셋(텍스처·스프라이트시트·3D 모델·애니메이션 클립)을 키로 선언하고, 노드는 "$key"로 참조합니다. 애니메이션도 데이터입니다 — {type:"animation"} 에셋이 GLB 클립을 메모리에 올리고 어떤 ModelInstance3D든 재생할 수 있으며, Mixamo 클립은 VRM 아바타에 자동 리타게팅됩니다.`}}]},Z=(e,t,...n)=>({type:e,summary:t,body:n}),UO=[{id:`core`,label:{en:`Core`,ko:`Core`},intro:{en:`Dimension-free building blocks: plain containers and the game clock. They render nothing themselves.`,ko:`차원과 무관한 기본 블록 — 순수 컨테이너와 게임 시계입니다. 스스로는 아무것도 그리지 않습니다.`},nodes:[Z(`Node`,{en:`A plain container with no transform.`,ko:`변환 없이 자식을 묶는 순수 컨테이너.`},{en:`Use it to group related nodes (all coins, all UI) without affecting their positions. Lifecycle, signals, groups, script — everything works; it just has no visual or spatial meaning of its own.`,ko:`위치에 영향을 주지 않으면서 관련 노드(코인 전부, UI 전부)를 묶을 때 씁니다. 라이프사이클·시그널·그룹·스크립트가 모두 동작하며, 시각적·공간적 의미만 없습니다.`}),Z(`Timer`,{en:"A serializable countdown that emits `timeout`.",ko:"`timeout` 시그널을 쏘는 직렬화 가능한 카운트다운."},{en:`Set waitTime (seconds), optionally autostart or oneShot, and connect its timeout signal to any handler. Because it is a node, the whole timing setup lives in the scene file — agents can read and tune it.`,ko:`waitTime(초)을 정하고 autostart·oneShot을 선택한 뒤 timeout 시그널을 핸들러에 연결하세요. 노드이기 때문에 타이밍 설정 전체가 씬 파일에 남아 에이전트가 읽고 조정할 수 있습니다.`}),Z(`AudioPlayer`,{en:`Plays a sound: zero-asset procedural SFX, BGM loops, or one-shot effects.`,ko:`소리 재생 — 무에셋 절차적 효과음, 배경음 루프, 단발 효과음.`},{en:`Set preset (coin/jump/hurt/explosion/…) for an instant zero-asset SFX (pitch/seed vary it), or leave it "custom" and set src (URL or "$assetKey"). volume 0..1; bus routes through engine.audio (sfx|music) for global volume/mute; loop for music; autoplay starts on ready (browsers may hold it until the first user gesture). Call play()/stop() from game code. Dimension-free — 2D and 3D alike.`,ko:`preset(coin/jump/hurt/explosion/…)을 설정하면 즉시 무에셋 효과음이 납니다(pitch·seed로 변형). 아니면 "custom"으로 두고 src(URL 또는 "$에셋키")를 쓰세요. volume은 0..1, bus는 engine.audio(sfx|music)를 통해 전역 볼륨/음소거에 연결, 음악은 loop, autoplay는 준비되면 재생합니다(브라우저가 첫 입력까지 보류 가능). 게임 코드에서 play()/stop()을 부르세요. 2D·3D 어디서나 동작합니다.`})]},{id:`2d`,label:{en:`2D`,ko:`2D`},intro:{en:`The 2D world is y-down pixels: 1 unit = 1 px, (0,0) top-left, rotation in clockwise degrees. Rendering needs a current Camera2D.`,ko:`2D 세계는 y-아래 픽셀 좌표입니다: 1유닛=1px, (0,0)은 좌상단, 회전은 시계방향 도(degree). 렌더링에는 current 카메라(Camera2D)가 필요합니다.`},nodes:[Z(`Node2D`,{en:`The 2D transform container (position/rotation/scale).`,ko:`2D 변환 컨테이너(position/rotation/scale).`},{en:`Children inherit its transform — move the parent, everything follows. renderOrder orders drawing; visible hides the subtree.`,ko:`자식은 변환을 상속합니다 — 부모를 움직이면 전부 따라옵니다. renderOrder로 그리기 순서를, visible로 서브트리 표시를 제어합니다.`}),Z(`Sprite2D`,{en:`A textured quad.`,ko:`텍스처 사각형(스프라이트).`},{en:`texture is "$assetKey" from the scene assets. anchor [0.5,0.5] centers; flipX/flipY mirror; tint multiplies color; opacity fades. Size comes from the texture × scale.`,ko:`texture는 씬 에셋의 "$키"입니다. anchor [0.5,0.5]면 중앙 기준, flipX/flipY로 반전, tint로 색 곱, opacity로 투명도. 크기는 텍스처 × scale로 정해집니다.`}),Z(`ColorRect2D`,{en:`A flat colored rectangle — no texture needed.`,ko:`단색 사각형 — 텍스처 불필요.`},{en:`size [w,h] px, color, opacity, anchor. Backgrounds, platforms, walls, UI panels, generated-level tiles (maze2d/dungeon2d emit these) — blocky art without any asset.`,ko:`size [w,h] px, color, opacity, anchor. 배경·플랫폼·벽·UI 패널·생성 레벨 타일(maze2d/dungeon2d가 이걸 만듭니다) — 에셋 없이 만드는 블록 그래픽입니다.`}),Z(`AnimatedSprite2D`,{en:`Spritesheet animation player.`,ko:`스프라이트시트 애니메이션 플레이어.`},{en:`Point sheet at a "$sheet" asset, define animations as named frame ranges with fps/loop in JSON, set autoplay. Emits animationFinished(name) for one-shot chains.`,ko:`sheet에 "$시트" 에셋을 연결하고, JSON에 fps/loop를 가진 이름별 프레임 구간으로 animations를 정의한 뒤 autoplay를 지정하세요. 단발 애니메이션 연결을 위해 animationFinished(name)를 발산합니다.`}),Z(`Camera2D`,{en:`The 2D view: follow, zoom, limits.`,ko:`2D 시점 — 추적·줌·경계.`},{en:`position is the view CENTER. follow tracks a node path with smoothing (0..1, higher = snappier). limits [minX,minY,maxX,maxY] clamps the view inside a world region. current: true makes it THE camera.`,ko:`position은 화면의 중심입니다. follow가 노드 경로를 smoothing(0..1, 높을수록 빠릿)으로 추적합니다. limits [minX,minY,maxX,maxY]가 시점을 월드 영역 안에 가둡니다. current: true인 카메라가 실제 시점이 됩니다.`}),Z(`Particles2D`,{en:`A 2D particle emitter (fire, sparks, smoke…).`,ko:`2D 파티클 이미터(불·스파크·연기…).`},{en:`Start from a preset (fire/smoke/sparks/…) then override any prop: rate, lifetime/speed ranges, direction + spread, gravity, size/color/alpha start→end, additive blend. burst > 0 with emitting: false makes a one-shot that emits finished. Animates LIVE in this editor.`,ko:`preset(fire/smoke/sparks/…)에서 시작해 어떤 prop이든 덮어쓰세요: rate, lifetime/speed 구간, direction+spread, gravity, size/color/alpha 시작→끝, additive blend. emitting: false에 burst > 0이면 단발 발사 후 finished를 발산합니다. 이 에디터에서 라이브로 움직입니다.`}),Z(`Label`,{en:`Text rendered to a quad.`,ko:`텍스트를 그리는 노드.`},{en:`text/fontSize/color/font/align — rendered via CanvasTexture, so any system font string works. Inside a UILayer it pins to the screen (HUD).`,ko:`text/fontSize/color/font/align — CanvasTexture로 그려져 시스템 폰트 문자열을 그대로 쓸 수 있습니다. UILayer 아래에 두면 화면에 고정됩니다(HUD).`}),Z(`UILayer`,{en:`A screen-space subtree (HUD).`,ko:`화면 고정 서브트리(HUD).`},{en:`Everything under it ignores the camera: positions are screen pixels from the top-left. Score counters, prompts, menus go here.`,ko:`이 아래의 모든 것은 카메라를 무시합니다 — 좌상단 기준 화면 픽셀 좌표입니다. 점수, 안내 문구, 메뉴를 여기에 둡니다.`})]},{id:`2d-physics`,label:{en:`2D Physics`,ko:`2D 물리`},intro:{en:`Rapier-backed. Colliders are PROPS ({shape, …}) — the green dashed wireframes in this editor. Physics runs when the game calls enablePhysics2D (and in play mode). Gravity lives on the scene row.`,ko:`Rapier 기반입니다. 콜라이더는 prop({shape, …})이며 에디터의 초록 점선이 그것입니다. 물리는 게임이 enablePhysics2D를 부를 때(그리고 플레이 모드에서) 돌고, 중력은 씬 행에 있습니다.`},nodes:[Z(`StaticBody2D`,{en:`Immovable collision: ground, walls, platforms.`,ko:`움직이지 않는 충돌체 — 바닥·벽·플랫폼.`},{en:`Other bodies collide with it; it never moves. Cheapest body type — use it for all level geometry.`,ko:`다른 바디가 부딪히지만 자신은 절대 움직이지 않습니다. 가장 저렴한 바디 — 레벨 지형 전부에 쓰세요.`}),Z(`RigidBody2D`,{en:`Fully simulated: falls, bounces, pushes.`,ko:`완전 시뮬레이션 — 떨어지고 튕기고 밀립니다.`},{en:`Gravity and collisions drive it. Set linearVelocity to launch. For player characters prefer CharacterBody2D (direct control).`,ko:`중력과 충돌이 움직임을 결정합니다. linearVelocity로 발사하세요. 플레이어 캐릭터에는 직접 제어가 가능한 CharacterBody2D를 권합니다.`}),Z(`CharacterBody2D`,{en:`Kinematic character: you set velocity, it slides.`,ko:`키네마틱 캐릭터 — 속도를 주면 미끄러지듯 이동.`},{en:`moveAndSlide() resolves collisions without physics pushing back; isOnFloor() gates jumps. Sensors (Area2D) do not block it but still fire triggers.`,ko:`moveAndSlide()가 밀려나지 않으면서 충돌을 처리하고 isOnFloor()로 점프를 판정합니다. 센서(Area2D)는 길을 막지 않으면서 트리거를 발사합니다.`}),Z(`Area2D`,{en:`A sensor: overlap triggers, no collision response.`,ko:`센서 — 겹침 감지만, 충돌 반응 없음.`},{en:`Fires triggerEnter(other)/triggerExit(other). Coins, checkpoints, damage zones. Filter with other.isInGroup("player").`,ko:`triggerEnter(other)/triggerExit(other)를 발산합니다. 코인·체크포인트·데미지 존에 쓰고, other.isInGroup("player")로 거릅니다.`}),Z(`CharacterController2D`,{en:`Zero-code platformer/top-down movement.`,ko:`코드 없는 플랫포머/탑다운 이동.`},{en:`Put it UNDER a CharacterBody2D. mode "platformer" (gravity + jump via jumpAction) or "topDown" (free 2-axis). Reads the scene input map actions (moveAction/jumpAction). maxSpeed/jumpHeight are intent-level numbers.`,ko:`CharacterBody2D의 자식으로 두세요. mode는 "platformer"(중력+jumpAction 점프) 또는 "topDown"(자유 2축)이며 씬 입력 맵의 액션(moveAction/jumpAction)을 읽습니다. maxSpeed/jumpHeight는 의도 그대로의 숫자입니다.`})]},{id:`3d`,label:{en:`3D`,ko:`3D`},intro:{en:`Meters, y-up, rotations in degrees. Standard materials need LIGHT — add a DirectionalLight3D or scene ambient, or you get silhouettes.`,ko:`미터 단위, y-위, 회전은 도(degree)입니다. 표준 머티리얼은 빛이 필요합니다 — DirectionalLight3D나 씬 ambient가 없으면 실루엣만 보입니다.`},nodes:[Z(`Node3D`,{en:`The 3D transform container.`,ko:`3D 변환 컨테이너.`},{en:`position [x,y,z] in meters, rotation [x,y,z] in degrees, scale per axis. Children inherit.`,ko:`position [x,y,z] 미터, rotation [x,y,z] 도, scale은 축별. 자식이 상속합니다.`}),Z(`MeshInstance3D`,{en:`A primitive mesh + material.`,ko:`프리미티브 메시 + 머티리얼.`},{en:`mesh: box/sphere/plane/cylinder…, size per primitive, material {color, roughness, metalness, emissive, emissiveIntensity, wireframe, map, normalMap, repeat} (this editor gives it swatches, sliders, texture-URL fields, and a repeat [u,v] tiling vector). map/normalMap are texture URLs; repeat only takes effect with a map. castShadow/receiveShadow per node.`,ko:`mesh: box/sphere/plane/cylinder…, 프리미티브별 size, material {color, roughness, metalness, emissive, emissiveIntensity, wireframe, map, normalMap, repeat}(에디터가 스와치·슬라이더·텍스처 URL 입력·repeat [u,v] 타일링 벡터 제공). map/normalMap은 텍스처 URL, repeat는 map이 있을 때만 적용. 그림자는 castShadow/receiveShadow.`}),Z(`ModelInstance3D`,{en:`A GLB/glTF/VRM model file.`,ko:`GLB/glTF/VRM 모델 파일.`},{en:'model: "$assetKey" or URL. targetHeight scales the model to stand N units tall (run `npx incanto-model file.glb` to read its real size first). animation plays an embedded clip name OR an "$animation" asset — Mixamo clips retarget onto VRM humanoids automatically. One node per VRM asset (the avatar runtime mounts live).',ko:'model은 "$에셋키" 또는 URL입니다. targetHeight가 모델을 N유닛 높이로 맞춥니다(먼저 `npx incanto-model 파일.glb`로 실제 크기를 확인하세요). animation은 내장 클립 이름 또는 "$애니메이션" 에셋을 재생하며, Mixamo 클립은 VRM 휴머노이드에 자동 리타게팅됩니다. VRM 에셋은 노드 하나만 쓸 수 있습니다(아바타 런타임이 라이브로 마운트됨).'}),Z(`Sprite3D`,{en:`A 2D image as a camera-facing billboard (the 2.5D look).`,ko:`카메라를 향하는 2D 빌보드 스프라이트(2.5D 룩).`},{en:`A textured quad that turns to face the camera inside the 3D world (Octopath / Don't Starve / MapleStory-in-3D). texture is an image URL; size is [w,h] in METERS. billboard: "y" stays upright and yaws toward the camera (characters/props), "full" faces it completely (items/FX), "none" is fixed to the node rotation. anchor [0.5,0] plants the feet on the ground. pixelArt nearest-filters for crisp pixels; alphaTest is a hard cutout that depth-sorts against 3D geometry (a tree occludes it); flipX mirrors left/right; tint/opacity recolor.`,ko:`3D 월드 안에서 카메라를 향해 도는 텍스처 쿼드(Octopath/Don't Starve, 3D 속 메이플스토리 룩). texture는 이미지 URL, size는 [너비,높이] 미터. billboard: "y"는 세로로 선 채 카메라로 yaw(캐릭터/사물), "full"은 완전히 향함(아이템/FX), "none"은 노드 회전에 고정. anchor [0.5,0]은 발을 바닥에 둡니다. pixelArt는 nearest 필터로 픽셀을 또렷하게, alphaTest는 3D 지형과 깊이정렬되는 하드 컷아웃(나무가 가림), flipX는 좌우 반전, tint/opacity로 색·투명도.`}),Z(`AnimatedSprite3D`,{en:`A billboard sprite that plays spritesheet animations.`,ko:`스프라이트시트 애니를 재생하는 빌보드 스프라이트.`},{en:`Everything Sprite3D does, plus frame animation. sheet is a spritesheet image URL whose cells are frameWidth×frameHeight; animations maps a name to {frames, fps, loop} where frames is an inclusive [start,end] range or an explicit list; autoplay starts one on load. Game code calls play("walk") / stop(); a non-looping clip clamps on its last frame and emits animationFinished(name) — chain attack→idle from it.`,ko:`Sprite3D의 모든 기능 + 프레임 애니. sheet는 frameWidth×frameHeight 칸을 가진 스프라이트시트 URL, animations는 이름→{frames, fps, loop}(frames는 포함 [시작,끝] 범위 또는 명시 목록), autoplay는 로드 시 하나를 시작. 게임 코드에서 play("walk")/stop() 호출, 비반복 클립은 마지막 프레임에 머물며 animationFinished(name)를 emit(attack→idle 연결).`}),Z(`Camera3D`,{en:`The 3D view.`,ko:`3D 시점.`},{en:`Perspective camera; fov in degrees; current: true selects it. Use the editor's "game cam" (0) to preview exactly what it renders.`,ko:`원근 카메라이며 fov는 도 단위, current: true가 실제 시점이 됩니다. 에디터의 "game cam"(0)으로 정확한 렌더 결과를 미리 보세요.`}),Z(`DirectionalLight3D`,{en:`Sun-like light from a direction.`,ko:`태양광 — 방향에서 평행하게.`},{en:`Position sets the direction toward the origin. intensity/color; pair with scene ambient for soft fill.`,ko:`위치가 원점을 향한 방향을 정합니다. intensity/color를 조절하고 부드러운 채움광은 씬 ambient와 함께 쓰세요.`}),Z(`OmniLight3D`,{en:`A point light radiating everywhere.`,ko:`점광 — 사방으로 퍼지는 빛.`},{en:`Lamps, torches, glows. range limits reach; intensity/color shape the falloff.`,ko:`램프·횃불·발광체. range로 도달 거리를, intensity/color로 감쇠를 만듭니다.`}),Z(`Particles3D`,{en:`A 3D particle emitter (fire, magic, weather…).`,ko:`3D 파티클 이미터(불·마법·날씨…).`},{en:`The same preset + override model as Particles2D, in meters with [x,y,z] gravity. Camera-facing billboard quads, instanced — hundreds are cheap. Animates LIVE in this editor.`,ko:`Particles2D와 같은 preset+덮어쓰기 모델을 미터 단위와 [x,y,z] 중력으로. 카메라를 향하는 빌보드 쿼드를 인스턴싱해 수백 개도 가볍습니다. 이 에디터에서 라이브로 움직입니다.`}),Z(`Terrain3D`,{en:`Procedural heightfield terrain with biome texture splatting.`,ko:`바이옴 텍스처 스플래팅이 적용된 절차적 하이트필드 지형.`},{en:`Seeded simplex hills displaced once on the CPU; theme (island/alpine/plains/desert/custom) picks the 4 blended biome textures by height and slope. heightAt(x,z) answers the surface height anywhere. For physics, parent it under a StaticBody3D with collider {shape:'heightfield'}.`,ko:`시드 기반 심플렉스 언덕을 CPU에서 한 번 변위합니다. theme(island/alpine/plains/desert/custom)이 높이·경사로 블렌딩되는 바이옴 텍스처 4장을 고릅니다. heightAt(x,z)로 어디서든 지표 높이를 얻고, 물리는 StaticBody3D 아래에 두고 collider {shape:'heightfield'}를 쓰세요.`}),Z(`Water3D`,{en:`A shader-water surface with splash signals.`,ko:`스플래시 신호를 내는 셰이더 물 표면.`},{en:`size [w,d] meters on XZ. quality 'fancy' (default) = FBM wave shader with a trough/surface/peak ramp (colors), CubeCamera reflections (reflection/reflectionInterval) and opt-in shoreline foam; 'simple' = the cheap sine material (color). Bodies crossing the surface emit entered/exited(body) and raise ripples (interaction). Volumetric gameplay still wants an Area3D. Waves animate LIVE in this editor.`,ko:`XZ 평면에 size [w,d] 미터. quality 'fancy'(기본)는 FBM 파도 셰이더 — trough/surface/peak 램프(colors), CubeCamera 반사(reflection/reflectionInterval), 옵트인 해안 거품(foam). 'simple'은 저사양용 사인파 머티리얼(color). 바디가 수면을 지나면 entered/exited(body) 신호와 물결이 일어납니다(interaction). 부피 기반 게임플레이엔 여전히 Area3D를 쓰세요. 이 에디터에서 파도가 라이브로 움직입니다.`}),Z(`Foliage3D`,{en:`An instanced grass/flower carpet that sways.`,ko:`바람에 흔들리는 인스턴스 풀밭/꽃밭.`},{en:`kind grass/flowers/reeds scattered over area [w,d] at density (capped by maxInstances — instanced, tens of thousands of blades cheap). Grass defaults to style 'mesh': every instance is a REAL tapered blade curved by a bezier vertex shader — Voronoi-clump hue/lean, groundColor soil roots (match the terrain), sunDirection tip sheen, 2-octave rolling wind, fadeStart/fadeEnd camera LOD, and (interaction) blade-bending around moving bodies. style 'blades' keeps the 8-SDF-blades-per-quad ported shader, 'simple' the legacy quads. colorA/colorB tint bottom→top, sway sets the wind, seed makes the scatter reproducible.`,ko:`kind grass/flowers/reeds를 area [w,d]에 density로 흩뿌립니다(maxInstances 상한 — 인스턴싱이라 수만 가닥도 가벼움). grass는 기본 style 'mesh': 인스턴스 하나하나가 베지어 버텍스 셰이더로 휘어지는 진짜 잎 메시 — 보로노이 클럼프 색/기울기, groundColor 흙빛 뿌리(지형 색에 맞추세요), sunDirection 잎끝 광택, 2옥타브 굽이치는 바람, fadeStart/fadeEnd 카메라 LOD, (interaction) 움직이는 바디 주변 풀 눕힘까지. style 'blades'는 쿼드당 8가닥 SDF 셰이더, 'simple'은 기존 쿼드 룩. colorA/colorB가 아래→위 색, sway가 바람 세기, seed가 배치를 재현 가능하게 합니다.`}),Z(`Flowers3D`,{en:`Instanced flower PLANTS — stems, leaves, multi-petal heads.`,ko:`인스턴스 꽃밭 — 줄기·잎·여러 장 꽃잎의 진짜 꽃 식물.`},{en:`Real procedural flower plants (curved stem, 2-3 leaves, a 5-8 petal head around a contrasting center disc, 1-3 blooms per plant) scattered over area [w,d]. density is the vibe dial: 'lush' / 'sparse' (default) / 'none', or a number in plants/m². varieties picks a subset of daisy/cosmos/bellflower ([] = all three); palette sets the head colors ([] = white/yellow/violet). clustering 0-1 gathers plants into Voronoi patches that bloom one species + color together; sway bobs the heads in a gentle wind; seed makes the field reproducible. ≤3 varieties → ≤6 draw calls.`,ko:`진짜 프로시저럴 꽃 식물(휘어진 줄기, 잎 2-3장, 대비되는 중심 원반을 두른 꽃잎 5-8장 머리, 포기당 꽃 1-3송이)을 area [w,d]에 흩뿌립니다. density가 바이브 다이얼: 'lush'(풍성하게) / 'sparse'(듬성듬성, 기본) / 'none'(없게) 또는 m²당 개수. varieties는 daisy/cosmos/bellflower 부분집합([] = 셋 다), palette는 꽃 색([] = 흰/노랑/보라). clustering 0-1이 보로노이 패치로 모아 한 패치가 같은 종·같은 색으로 피고, sway가 바람에 머리를 끄덕이며, seed로 배치가 재현됩니다. 품종 ≤3 → 드로우 콜 ≤6.`}),Z(`Tree3D`,{en:`Procedural ez-tree groves — branchy trunks, textured leaves.`,ko:`프로시저럴 ez-tree 나무 — 가지 달린 줄기와 텍스처 잎까지.`},{en:`type conifer/broadleaf/dead picks the recipe family; tier is the cost dial: simple = primitive low-poly, medium = light forest presets (≤3k tris/tree), high = full ez-tree presets (8–20k, hero trees). count > 1 scatters a forest patch over area [w,d] — up to 3 seed variants, each branches + leaves InstancedMesh (≤6 draw calls); count × tris/tree is budget-checked at load. seed makes every branch and leaf reproducible; height jitters ±20% per instance; leaves sway in a simplex wind.`,ko:`type conifer/broadleaf/dead가 수종을, tier가 비용을 정합니다: simple = 기존 로우폴리, medium = 가벼운 forest 프리셋(나무당 ≤3k tris), high = 풀 ez-tree 프리셋(8–20k, 주인공 나무). count > 1이면 area [w,d]에 숲 패치를 흩뿌립니다 — 시드 변형 최대 3종, 변형마다 가지+잎 InstancedMesh(드로우 콜 ≤6), count × tris는 로드 시 예산 검사. seed로 가지와 잎이 전부 재현되고 height는 인스턴스마다 ±20% 지터링, 잎은 심플렉스 바람에 흔들립니다.`}),Z(`VoxelGrid3D`,{en:`A Minecraft-style block grid in ONE node.`,ko:`마인크래프트식 블록 그리드 — 노드 하나로.`},{en:`voxels is a list of [x,y,z,palette] integer cells (terrain/island generators emit these). Greedy-meshed and instanced — large worlds stay one draw batch. Game code edits blocks via setBlock/getBlock; emits blocksChanged.`,ko:`voxels는 [x,y,z,팔레트] 정수 셀 목록입니다(terrain/island 생성기가 만들어 냅니다). 그리디 메싱+인스턴싱으로 큰 월드도 드로우 배치 하나를 유지합니다. 게임 코드는 setBlock/getBlock으로 수정하고 blocksChanged가 발산됩니다.`})]},{id:`3d-physics`,label:{en:`3D Physics`,ko:`3D 물리`},intro:{en:`Same model as 2D, in meters with y-up gravity ([0,-9.81,0] default). Colliders: box {size}, sphere {radius}, capsule {radius,height}.`,ko:`2D와 같은 모델을 미터·y-위 중력([0,-9.81,0] 기본)으로. 콜라이더는 box {size}, sphere {radius}, capsule {radius,height}.`},nodes:[Z(`StaticBody3D`,{en:`Immovable 3D collision.`,ko:`움직이지 않는 3D 충돌체.`},{en:`Floors, walls, level geometry.`,ko:`바닥·벽·레벨 지형.`}),Z(`RigidBody3D`,{en:`Simulated 3D body.`,ko:`시뮬레이션되는 3D 바디.`},{en:`Crates, balls, debris — gravity and impacts drive it.`,ko:`상자·공·파편 — 중력과 충격이 움직입니다.`}),Z(`CharacterBody3D`,{en:`Kinematic 3D character.`,ko:`키네마틱 3D 캐릭터.`},{en:`moveAndSlide with up = +y; isOnFloor for jumps.`,ko:`+y를 위로 moveAndSlide, 점프 판정은 isOnFloor.`}),Z(`Area3D`,{en:`3D overlap sensor.`,ko:`3D 겹침 센서.`},{en:`triggerEnter/Exit — pickups, zones, goals.`,ko:`triggerEnter/Exit — 아이템·존·골인 지점.`}),Z(`CharacterController3D`,{en:`Zero-code 3D movement + camera rig.`,ko:`코드 없는 3D 이동 + 카메라 리그.`},{en:`Put it UNDER a CharacterBody3D. view: thirdPerson (orbit + zoom), firstPerson (eyeHeight, mouseLook), sideView or flightView. Reads moveAction/jumpAction/sprintAction from the scene input map; skinPath turns the visual child to face travel. Intent-level numbers: maxSpeed, jumpVelocity, camDistance.`,ko:`CharacterBody3D의 자식으로 두세요. view는 thirdPerson(궤도+줌), firstPerson(eyeHeight, mouseLook), sideView, flightView 중 하나입니다. 씬 입력 맵의 moveAction/jumpAction/sprintAction을 읽고 skinPath의 비주얼 자식을 진행 방향으로 돌립니다. maxSpeed·jumpVelocity·camDistance 같은 의도 수준의 숫자만 만집니다.`})]},{id:`network`,label:{en:`Network`,ko:`네트워크`},intro:{en:`Multiplayer is transport-agnostic: the engine speaks one NetworkTransport interface (built-in offline Loopback + an @agent8/gameserver adapter). The scene declares replication; one owner node per player broadcasts its sync keys.`,ko:`멀티플레이어는 트랜스포트 불가지론입니다 — 엔진은 NetworkTransport 인터페이스 하나만 사용합니다(내장 오프라인 Loopback + @agent8/gameserver 어댑터). 복제는 씬이 선언하며, 플레이어당 하나의 owner 노드가 sync 키를 송출합니다.`},nodes:[Z(`NetworkSpawner`,{en:`Spawns a registered scene per remote player/entity.`,ko:`원격 플레이어/엔티티마다 등록된 씬을 생성.`},{en:`source "users" mirrors every OTHER account in the room (self skipped); "collection:<id>" mirrors a room collection. Replicated sync patches apply to each instance; position interpolates. Emits spawned/despawned.`,ko:`source "users"는 방의 다른 모든 계정을 미러링하고(자신 제외) "collection:<id>"는 방 컬렉션을 미러링합니다. 복제 sync 패치가 인스턴스에 적용되고 position은 보간됩니다. spawned/despawned를 발산합니다.`})]}],WO=`overview`,GO=null;function KO(){document.querySelector(`#docs`)?.removeAttribute(`hidden`),YO()}function qO(){document.querySelector(`#docs`)?.setAttribute(`hidden`,``)}function JO(){let e=document.querySelector(`#docs`);e&&(e.addEventListener(`pointerdown`,t=>{t.target===e&&qO()}),document.querySelector(`#docs-close`)?.addEventListener(`click`,qO))}function YO(){let e=document.querySelector(`#docs-tabs`),t=document.querySelector(`#docs-body`);if(!e||!t)return;e.textContent=``;let n=(t,n)=>{let r=document.createElement(`button`);r.type=`button`,r.className=`docs-tab${WO===t?` active`:``}`,r.textContent=n,r.addEventListener(`click`,()=>{WO=t,GO=null,YO()}),e.appendChild(r)};n(`overview`,aO({en:`Overview`,ko:`개요`}));for(let e of UO)n(e.id,aO(e.label));if(t.textContent=``,WO===`overview`){XO(t);return}let r=UO.find(e=>e.id===WO);r&&ZO(t,r)}function XO(e){let t=document.createElement(`h2`);t.textContent=aO(HO.title),e.appendChild(t);for(let t of HO.sections){let n=document.createElement(`h3`);n.textContent=aO(t.heading);let r=document.createElement(`p`);r.textContent=aO(t.text),e.append(n,r)}}function ZO(e,t){let n=document.createElement(`p`);n.className=`docs-intro`,n.textContent=aO(t.intro),e.appendChild(n);for(let n of t.nodes){let t=document.createElement(`div`);t.className=`docs-node${GO===n.type?` open`:``}`;let r=document.createElement(`button`);r.type=`button`,r.className=`docs-node-head`;let i=document.createElement(`span`);i.className=`tree-icon`,i.appendChild(cO(n.type));let a=document.createElement(`strong`);a.textContent=n.type;let o=document.createElement(`span`);if(o.className=`docs-summary`,o.textContent=aO(n.summary),r.append(i,a,o),r.addEventListener(`click`,()=>{GO=GO===n.type?null:n.type,YO()}),t.appendChild(r),GO===n.type){let e=document.createElement(`div`);e.className=`docs-detail`;for(let t of n.body){let n=document.createElement(`p`);n.textContent=aO(t),e.appendChild(n)}e.appendChild(QO(n.type)),t.appendChild(e)}e.appendChild(t)}}function QO(e){let t=document.createElement(`table`);t.className=`docs-props`;let n=document.createElement(`tr`);for(let e of[aO({en:`prop`,ko:`prop`}),aO({en:`default`,ko:`기본값`})]){let t=document.createElement(`th`);t.textContent=e,n.appendChild(t)}t.appendChild(n);try{let n=fe(e);for(let[e,r]of Object.entries(n)){let n=document.createElement(`tr`),i=document.createElement(`td`);i.className=`mono`,i.textContent=e;let a=document.createElement(`td`);a.className=`mono`,a.textContent=JSON.stringify(r.default),n.append(i,a),t.appendChild(n)}}catch{}return t}function Q(e){return Math.round(e*100)/100}function $O(e,t,n,r,i){return{name:e,type:`StaticBody3D`,props:{collider:{shape:`box`,size:t},position:n,...i?{rotation:i}:{}},children:[{name:`Skin`,type:`MeshInstance3D`,props:{mesh:`box`,size:t,...r}}]}}function ek(e,t,n,r,i,a){let o=Q(r.range(i[0],i[1])),s=Q(r.range(.5,.7));return{name:`Rock${e}`,type:`MeshInstance3D`,props:{mesh:`sphere`,size:[1,1,1],position:[t,Q(o*s*.6),n],rotation:[0,r.int(0,359),0],scale:[o,Q(o*s),Q(o*r.range(.8,1.1))],material:{color:a??tk(r),roughness:1},castShadow:!0}}}function tk(e){let t=Math.round(e.range(110,160)).toString(16).padStart(2,`0`);return`#${t}${t}${t}`}function nk(e){return[{name:`Sun`,type:`DirectionalLight3D`,props:{position:[Q(e*.8),Q(e*1.5),Q(e*.6)],intensity:1,castShadow:!0,shadowArea:Q(e*1.2)}},{name:`FillLight`,type:`DirectionalLight3D`,props:{position:[Q(-e*.8),Q(e*.8),Q(-e*.6)],intensity:.4,color:`#b9d4ff`}}]}function rk(e,t){return e===void 0?t:typeof e==`number`?[e,e]:e}function ik(e,t,n){return Math.min(Math.max(e,t),n)}var ak=[`boxes`,`ruins`,`garden`],ok=.5,sk=.1,ck=2,lk={boxes:{floor:`#3f3f3f`,wall:`#55504a`,obstacles:[`#b0413e`,`#5b8266`,`#3e6990`,`#a26b38`,`#6d5a96`,`#878787`]},ruins:{floor:`#7d766b`,wall:`#8a8378`,obstacles:[`#8a8378`,`#979085`,`#a39a8d`,`#7b746a`]},garden:{floor:`#4d7c3a`,wall:`#2f6b2f`,obstacles:[`#2f6b2f`,`#3a7a38`,`#356e33`]}};function uk(e){let{seed:t,width:n=30,depth:r=30,wallHeight:i=3,obstacles:a=8,theme:o=`boxes`}=e;if(!ak.includes(o))throw new y(`BAD_FORMAT`,`generateArena theme must be one of [${ak.join(`, `)}], got '${o}'.`,{prop:`theme`,validOptions:[...ak]});let s=lk[o],c=new b(t),l=[$O(`Floor`,[n,sk,r],[0,-.1/2,0],{material:{color:s.floor,roughness:1},receiveShadow:!0})],u=i/2,d=[n+ok*2,i,ok],f=[ok,i,r],p={material:{color:s.wall,roughness:.9},receiveShadow:!0};l.push($O(`Wall1`,d,[0,u,-(r+ok)/2],p),$O(`Wall2`,d,[0,u,(r+ok)/2],p),$O(`Wall3`,f,[-(n+ok)/2,u,0],p),$O(`Wall4`,f,[(n+ok)/2,u,0],p)),o===`ruins`?l.push(...fk(c,s,n,r,i,a)):l.push(...dk(c,s,n,r,i,a,o)),o===`garden`&&l.push(...pk(c,n,r));let m=Math.max(n,r);return l.push({name:`Sun`,type:`DirectionalLight3D`,props:{position:[Q(m*.8),Q(m*1.5),Q(m*.6)],intensity:1,castShadow:!0,shadowArea:Q(m*1.2)}},{name:`FillLight`,type:`DirectionalLight3D`,props:{position:[Q(-m*.8),Q(m*.8),Q(-m*.6)],intensity:.4,color:`#b9d4ff`}},{name:`Lamp`,type:`OmniLight3D`,props:{position:[0,Q(i+2),0],intensity:.5,color:`#fff3d6`,range:Q(m)}}),{name:`Arena`,type:`Node3D`,children:l}}function dk(e,t,n,r,i,a,o){let s=[],c=o===`garden`?Math.min(n,r)*.16:0;for(let o=1;o<=a;o++){let a=[Q(e.range(.8,2.6)),Q(e.range(.8,Math.max(1.2,i*.8))),Q(e.range(.8,2.6))],l=Q(e.range(-(n/2-ck),n/2-ck)),u=Q(e.range(-(r/2-ck),r/2-ck));if(c>0&&Math.hypot(l,u)<c+1.5){let t=Math.max(Math.hypot(l,u),.001);l=Q(l/t*(c+1.5+e.range(0,2))),u=Q(u/t*(c+1.5+e.range(0,2)))}let d=e.int(0,359);s.push($O(`Obstacle${o}`,a,[l,Q(a[1]/2),u],{material:{color:e.pick(t.obstacles),roughness:.8},castShadow:!0},[0,d,0]))}return s}function fk(e,t,n,r,i,a){let o=[];if(a<=0)return o;let s=Math.max(1,Math.round(Math.sqrt(a/2))),c=Math.ceil(a/s),l=n-ck*2,u=r-ck*2,d=0;for(let n=0;n<s&&d<a;n++){let r=Q(s===1?0:-u/2+n/(s-1)*u);for(let n=0;n<c&&d<a;n++){d++;let a=Q((c===1?0:-l/2+n/(c-1)*l)+e.range(-.4,.4)),s=Q(e.next()>.35?e.range(i*.7,i*1.2):e.range(.4,.9)),u=Q(e.range(.8,1.2));o.push($O(`Obstacle${d}`,[u,s,u],[a,Q(s/2),Q(r+e.range(-.4,.4))],{material:{color:e.pick(t.obstacles),roughness:.95},castShadow:!0},[0,e.int(-8,8),0]))}}return o}function pk(e,t,n){let r=[],i=Math.min(t,n)*.16,a=r=>{let a=Math.min(t,n)/2-r/2-1,o=Q(e.range(-a,a)),s=Q(e.range(-a,a)),c=Math.max(Math.hypot(o,s),.001);return c<i+r/2&&(o=Q(o/c*(i+r/2+.5)),s=Q(s/c*(i+r/2+.5))),[o,s]};for(let i=1;i<=3;i++){let o=Q(Math.min(t,n)*e.range(.18,.26)),[s,c]=a(o);r.push({name:`Grass${i}`,type:`Foliage3D`,props:{kind:`grass`,area:[o,o],density:10,seed:e.int(1,1e9),position:[s,0,c]}})}for(let i=1;i<=2;i++){let o=Q(Math.min(t,n)*e.range(.12,.18)),[s,c]=a(o);r.push({name:`FlowerBed${i}`,type:`Flowers3D`,props:{density:`lush`,clustering:.3,area:[o,o],seed:e.int(1,1e9),position:[s,0,c]}})}return r.push({name:`Pool`,type:`Water3D`,props:{size:[Q(i*2),Q(i*2)],position:[0,.3,0],waveHeight:.04}}),r}var mk=32,hk=[4,8],gk=`#332f3a`,_k=`#6b6357`;function vk(e){let{seed:t,rooms:n=5}=e,[r,i]=rk(e.size,[960,720]),a=Math.max(8,Math.floor(r/mk)),o=Math.max(8,Math.floor(i/mk)),s=new b(t),c=[];for(let e=0;e<n*12&&c.length<n;e++){let e=s.int(hk[0],hk[1]),t=s.int(hk[0],hk[1]),n={x:s.int(1,Math.max(1,a-e-1)),y:s.int(1,Math.max(1,o-t-1)),w:e,h:t};c.some(e=>yk(e,n,1))||c.push(n)}let l=new Set,u=e=>{for(let t=e.y;t<e.y+e.h;t++)for(let n=e.x;n<e.x+e.w;n++)l.add(`${n},${t}`)};for(let e of c)u(e);let d=[];for(let e=1;e<c.length;e++){let[t,n]=bk(c[e-1]),[r,i]=bk(c[e]),a={x:Math.min(t,r),y:n,w:Math.abs(t-r)+1,h:1},o={x:r,y:Math.min(n,i),w:1,h:Math.abs(n-i)+1};for(let[t,n]of[[`H`,a],[`V`,o]])u(n),(n.w>1||n.h>1)&&d.push({name:`Corridor${e}${t}`,rect:n})}let f=new Set;for(let e of l){let[t,n]=e.split(`,`).map(Number);for(let e=-1;e<=1;e++)for(let r=-1;r<=1;r++){let i=`${t+r},${n+e}`;l.has(i)||f.add(i)}}let p=-(a*mk)/2,m=-(o*mk)/2,h=(e,t,n)=>({name:e,type:`ColorRect2D`,props:{position:[p+(t.x+t.w/2)*mk,m+(t.y+t.h/2)*mk],size:[t.w*mk,t.h*mk],color:n}}),g=c.map((e,t)=>h(`Room${t+1}`,e,gk));for(let e of d)g.push(h(e.name,e.rect,gk));let _=0;for(let e=-1;e<=o;e++){let t=-1;for(;t<=a;){if(!f.has(`${t},${e}`)){t++;continue}let n=1;for(;t+n<=a&&f.has(`${t+n},${e}`);)n++;_++;let r=[n*mk,mk];g.push({name:`Wall${_}`,type:`StaticBody2D`,props:{position:[p+(t+n/2)*mk,m+(e+.5)*mk],collider:{shape:`rect`,size:r}},children:[{name:`Skin`,type:`ColorRect2D`,props:{size:r,color:_k}}]}),t+=n}}return{name:`Dungeon`,type:`Node2D`,children:g}}function yk(e,t,n){return e.x-n<t.x+t.w&&e.x+e.w+n>t.x&&e.y-n<t.y+t.h&&e.y+e.h+n>t.y}function bk(e){return[Math.floor(e.x+e.w/2),Math.floor(e.y+e.h/2)]}var xk=[[0,-1],[1,0],[0,1],[-1,0]];function Sk(e,t,n){let r=2*t+1,i=2*n+1,a=Array.from({length:i},()=>Array(r).fill(!1)),o=(e,t)=>{a[t][e]=!0};o(1,1);let s=new Set([`0,0`]),c=[[0,0]];for(;c.length>0;){let[r,i]=c[c.length-1],a=[];for(let[e,o]of xk){let c=r+e,l=i+o;c>=0&&c<t&&l>=0&&l<n&&!s.has(`${c},${l}`)&&a.push([c,l])}if(a.length===0){c.pop();continue}let[l,u]=e.pick(a);s.add(`${l},${u}`),o(2*l+1,2*u+1),o(r+l+1,i+u+1),c.push([l,u])}return o(0,1),o(2*t,2*n-1),{cols:t,rows:n,cells:a}}var Ck=[`stone`,`hedge`,`canyon`],wk=.1,Tk=`https://agent8-games.verse8.io/assets/3D/default/textures/wall`,Ek=`https://agent8-games.verse8.io/assets/3D/default/textures/terrain`,Dk={stone:{wall:{color:`#e8e2d8`,map:`${Tk}/blocks.png`,normalMap:`${Tk}/blocks_normal.png`,tile:2,roughness:.95},floor:{color:`#99938a`,map:`${Ek}/stone.png`,normalMap:`${Ek}/stone_normal.png`,tile:3,roughness:1},cap:`#6b5848`,pillar:`#cfc8bb`,path:`#665f55`,sun:{color:`#c9d6ea`,intensity:.75,height:.5},fill:`#9fb4cc`,mood:{sky:{elevationDeg:10,azimuthDeg:150,turbidity:16,rayleigh:3.2},fog:{near:.5,far:3.5,color:`#86909c`},exposure:.82,ambient:{color:`#c9d4e2`,intensity:.12}}},hedge:{wall:{color:`#55a83e`,map:`${Ek}/grass.png`,normalMap:`${Ek}/grass_normal.png`,tile:1.4,roughness:1},floor:{color:`#86b06d`,map:`${Ek}/grass.png`,normalMap:`${Ek}/grass_normal.png`,tile:3,roughness:1},pillar:`#3d7531`,path:`#7d6845`,sun:{color:`#e9eee6`,intensity:.7,height:1},fill:`#b9c8b4`,mood:{sky:{elevationDeg:35,azimuthDeg:150,turbidity:18,rayleigh:4.2},fog:{near:.8,far:5,color:`#aab8a6`},exposure:.88,ambient:{color:`#dde5d8`,intensity:.15}}},canyon:{wall:{color:`#f0b070`,map:`${Ek}/stone.png`,normalMap:`${Ek}/stone_normal.png`,tile:2.4,roughness:1},floor:{color:`#e3c193`,map:`${Ek}/sand.png`,normalMap:`${Ek}/sand_normal.png`,tile:3.5,roughness:1},pillar:`#d8a868`,path:`#a98a58`,sun:{color:`#ffb572`,intensity:1.15,height:.35},fill:`#caa37e`,mood:{sky:{elevationDeg:9,azimuthDeg:230,turbidity:9,rayleigh:3.5},fog:{near:.7,far:4.5,color:`#c79c6e`},exposure:.92,ambient:{color:`#ffdcb6`,intensity:.13}}}};function Ok(e,t,n){return{color:e.color,roughness:e.roughness,map:e.map,...e.normalMap?{normalMap:e.normalMap}:{},repeat:[Q(t/e.tile),Q(n/e.tile)]}}var kk=10;function Ak(e){let{seed:t,width:n=8,depth:r=8,cellSize:i=2,wallHeight:a=2.5,theme:o=`stone`}=e;if(!Ck.includes(o))throw new y(`BAD_FORMAT`,`generateMaze theme must be one of [${Ck.join(`, `)}], got '${o}'.`,{prop:`theme`,validOptions:[...Ck]});let s=Dk[o],c=new b(t),l=Sk(c,n,r),u=2*n+1,d=2*r+1,f=Q(u*i),p=Q(d*i),m=(e,t)=>[Q((e+.5)*i-f/2),Q((t+.5)*i-p/2)],h=[$O(`Floor`,[f,wk,p],[0,-.1/2,0],{material:Ok(s.floor,f,p),receiveShadow:!0})],g=[],_=0;for(let e=0;e<d;e++){let t=0;for(;t<u;){if(l.cells[e]?.[t]){t++;continue}let n=1;for(;t+n<u&&!l.cells[e]?.[t+n];)n++;g.push({gx:t,gz:e,run:n}),_++;let r=Q(n*i);h.push($O(`Wall${_}`,[r,a,i],[Q((t+n/2)*i-f/2),Q(a/2),Q((e+.5)*i-p/2)],{material:Ok(s.wall,r,a),castShadow:!0,receiveShadow:!0})),t+=n}}return s.cap&&h.push(...jk(g,i,a,f,p,s.cap)),h.push(...Mk(c,l,i,a,m,s)),o===`hedge`?(h.push(...Ik(c,g,i,a,m)),h.push(...Lk(c,l,i,m,n,r))):o===`canyon`&&h.push(...Rk(c,g,i,a,m)),h.push(...Pk(l,i,a,m,s)),h.push(...nk(Math.max(f,p)).map((e,t)=>{if(t!==0)return{...e,props:{...e.props,color:s.fill}};let n=e.props?.position;return{...e,props:{...e.props,color:s.sun.color,intensity:s.sun.intensity,position:[n[0]??0,Q((n[1]??0)*s.sun.height),n[2]??0]}}})),{name:`Maze`,type:`Node3D`,children:h}}function jk(e,t,n,r,i,a){return e.map((e,o)=>({name:`Cap${o+1}`,type:`MeshInstance3D`,props:{mesh:`box`,size:[Q(e.run*t+.16),.12,Q(t+.16)],position:[Q((e.gx+e.run/2)*t-r/2),Q(n+.06),Q((e.gz+.5)*t-i/2)],material:{color:a,roughness:1},castShadow:!0,receiveShadow:!0}}))}function Mk(e,t,n,r,i,a){let o=[],s=(e,n)=>t.cells[n]?.[e]===!1;for(let e=2;e<t.cells.length-1;e+=2)for(let n=2;n<(t.cells[e]?.length??0)-1;n+=2)s(n,e)&&Number(s(n-1,e))+Number(s(n+1,e))+Number(s(n,e-1))+Number(s(n,e+1))>=3&&o.push([n,e]);let c=Math.min(kk,o.length),l=[],u=new Set,d=Q(n*1.2),f=Q(r*1.12);for(let t=1;t<=c;t++){let n=e.int(0,o.length-1);for(;u.has(n);)n=(n+1)%o.length;u.add(n);let[r,s]=o[n],[c,p]=i(r,s);l.push(Nk(`Pillar${t}`,c,p,d,f,a))}return l}function Nk(e,t,n,r,i,a){return{name:e,type:`MeshInstance3D`,props:{mesh:`box`,size:[r,i,r],position:[t,Q(i/2),n],material:{...Ok(a.wall,r,i),color:a.pillar},castShadow:!0,receiveShadow:!0}}}function Pk(e,t,n,r,i){let a=2*e.cols,o=2*e.rows-1,s=Q(t*1.1),c=Q(n*1.25),l=[[0,0],[0,2],[a,o-1],[a,o+1]].map(([e,t],n)=>{let[a,o]=r(e,t);return Nk(`Gate${n+1}`,a,o,s,c,i)});for(let[e,n,s]of[[`EntrancePath`,0,1],[`ExitPath`,a,o]]){let[a,o]=r(n,s);l.push({name:e,type:`MeshInstance3D`,props:{mesh:`box`,size:[Q(t*.96),.04,Q(t*.96)],position:[a,.02,o],material:{...Ok(i.floor,t,t),color:i.path},receiveShadow:!0}})}return l}var Fk=10;function Ik(e,t,n,r,i){return[...t].filter(e=>e.run>=2).sort((e,t)=>t.run-e.run||e.gz-t.gz||e.gx-t.gx).slice(0,Fk).map((t,a)=>{let[,o]=i(t.gx,t.gz),[s]=i(t.gx,t.gz),[c]=i(t.gx+t.run-1,t.gz);return{name:`HedgeTop${a+1}`,type:`Foliage3D`,props:{kind:`grass`,style:`tufts`,area:[Q(t.run*n*.92),Q(n*.7)],density:14,height:.35,sway:.4,colorA:`#2f5e26`,colorB:`#5d8a3c`,seed:e.int(1,1e9),position:[Q((s+c)/2),r,o]}}})}function Lk(e,t,n,r,i,a){let o=[];for(let e=0;e<t.cells.length;e++)for(let n=0;n<(t.cells[e]?.length??0);n++)t.cells[e]?.[n]&&o.push([n,e]);let s=Math.min(o.length,Math.max(3,Math.floor(i*a/12))),c=[],l=new Set;for(let t=1;t<=s&&l.size<o.length;t++){let i=e.int(0,o.length-1);for(;l.has(i);)i=(i+1)%o.length;l.add(i);let[a,s]=o[i],[u,d]=r(a,s),f=Q(n*.8);c.push({name:`Grass${t}`,type:`Foliage3D`,props:{kind:`grass`,area:[f,f],density:8,seed:e.int(1,1e9),position:[u,0,d]}})}return c}function Rk(e,t,n,r,i){let a=[];if(t.length===0)return a;let o=Math.min(8,t.length);for(let s=1;s<=o;s++){let o=e.pick(t),[c,l]=i(o.gx+e.int(0,o.run-1),o.gz),u=ek(s,c,l,e,[.3,Q(n*.35)],`#8f7355`),d=u.props?.position;d[1]=Q(d[1]+r),a.push(u)}return a}var zk=`#23222b`,Bk=`#5f6672`;function Vk(e){let{seed:t,cols:n=10,rows:r=8,cellPx:i=64}=e,a=Sk(new b(t),n,r),o=2*n+1,s=2*r+1,c=Q(o*i),l=Q(s*i),u=[{name:`Floor`,type:`ColorRect2D`,props:{size:[c,l],color:zk}}],d=0;for(let e=0;e<s;e++){let t=0;for(;t<o;){if(a.cells[e]?.[t]){t++;continue}let n=1;for(;t+n<o&&!a.cells[e]?.[t+n];)n++;d++;let r=[Q(n*i),i];u.push({name:`Wall${d}`,type:`StaticBody2D`,props:{position:[Q((t+n/2)*i-c/2),Q((e+.5)*i-l/2)],collider:{shape:`rect`,size:r}},children:[{name:`Skin`,type:`ColorRect2D`,props:{size:r,color:Bk}}]}),t+=n}}return{name:`Maze2D`,type:`Node2D`,children:u}}var Hk=16,Uk=[`#5b8266`,`#3e6990`,`#a26b38`,`#6d5a96`,`#b0413e`];function Wk(e){let{seed:t,count:n=10,width:r=[80,160],gapX:i=[40,120],stepY:a=[-80,40],start:o=[0,300]}=e,s=new b(t),c=[],l=Q(s.range(r[0],r[1])),u=o[0],d=o[1];for(let e=1;e<=n&&(c.push(Gk(e,u,d,l,s.pick(Uk))),e!==n);e++){let e=Q(s.range(r[0],r[1])),t=Q(s.range(i[0],i[1]));u=Q(u+l/2+t+e/2),d=Q(d+s.range(a[0],a[1])),l=e}return{name:`Platforms`,type:`Node2D`,children:c}}function Gk(e,t,n,r,i){return{name:`Platform${e}`,type:`StaticBody2D`,props:{position:[t,n],collider:{shape:`rect`,size:[r,Hk]}},children:[{name:`Skin`,type:`ColorRect2D`,props:{size:[r,Hk],color:i}}]}}var Kk=[3,5],qk=3,Jk={color:`#ffffff`,roughness:1,emissive:`#ffffff`,emissiveIntensity:.25};function Yk(e){let{seed:t,count:n=8,altitude:r=18}=e,[i,a]=rk(e.area,[60,60]),o=new b(t),s=[];for(let e=1;e<=n;e++){let t=o.int(Kk[0],Kk[1]),n=[];for(let e=1;e<=t;e++){let r=Q(o.range(1,2.2));n.push({name:`Puff${e}`,type:`MeshInstance3D`,props:{mesh:`sphere`,size:[1,1,1],position:[Q((e-(t+1)/2)*o.range(1,1.6)),Q(o.range(-.3,.3)),Q(o.range(-.6,.6))],scale:[Q(r*o.range(1.1,1.6)),Q(r*.55),r],material:Jk}})}s.push({name:`Cloud${e}`,type:`Node3D`,props:{position:[Q(o.range(-i/2,i/2)),Q(r+o.range(-3,qk)),Q(o.range(-a/2,a/2))]},children:n})}return{name:`Clouds`,type:`Node3D`,children:s}}var Xk=[`island`,`alpine`,`plains`,`desert`,`meadow`,`forest`,`savanna`,`snow`,`wetland`,`volcanic`],Zk=128,Qk=20,$k=.8,eA=.12,tA={island:{splat:`island`,maxHeight:4.5,sun:`#fff4d6`,fill:`#b9d4ff`,sky:{elevationDeg:38,azimuthDeg:145,turbidity:2.6,rayleigh:1.1},fog:{near:1,far:4},iblIntensity:.72},alpine:{splat:`alpine`,maxHeight:8,roughness:.65,detail:5,sun:`#f4f7ff`,fill:`#c9d8f2`,sky:{elevationDeg:45,azimuthDeg:35,turbidity:1.4,rayleigh:1.3},fog:{near:1.8,far:6.5},sunIntensity:1.1,exposure:.92,iblIntensity:.72},plains:{splat:`plains`,maxHeight:4,sun:`#fff2cf`,fill:`#bcd3ef`,sky:{elevationDeg:36,azimuthDeg:140,turbidity:2.4,rayleigh:1},fog:{near:.9,far:4},iblIntensity:.58},desert:{splat:`desert`,maxHeight:5,sun:`#ffe3b3`,fill:`#e8c9a6`,sky:{elevationDeg:42,azimuthDeg:160,turbidity:7,rayleigh:.6},fog:{near:.8,far:3.2,color:`#e8d3ae`},sunIntensity:1.35,iblIntensity:.75},meadow:{splat:`grassland`,maxHeight:1.2,sun:`#fff8e2`,fill:`#bfe0c9`,sky:{elevationDeg:55,azimuthDeg:125,turbidity:2.4,rayleigh:.95},fog:{near:1,far:4.4},sunIntensity:2.6,exposure:1.12,iblIntensity:.62},forest:{splat:`forest`,maxHeight:2.5,sun:`#ffdca8`,fill:`#a9c8b4`,sky:{elevationDeg:29,azimuthDeg:120,turbidity:5.5,rayleigh:1},fog:{near:.22,far:1.8,color:`#9fb494`},sunIntensity:2.6,ambient:.26,iblIntensity:.55},savanna:{splat:`savanna`,maxHeight:3,sun:`#ffdca0`,fill:`#e6d2a4`,sky:{elevationDeg:34,azimuthDeg:150,turbidity:5,rayleigh:.7},fog:{near:1,far:4.2,color:`#e3cf9f`},sunIntensity:2,exposure:1.05,iblIntensity:.6},snow:{splat:`snow`,maxHeight:2.2,sun:`#dfe9ff`,fill:`#c2d2f0`,sky:{elevationDeg:22,azimuthDeg:35,turbidity:1.6,rayleigh:1.6},fog:{near:1.2,far:5,color:`#dbe6f5`},sunIntensity:1,exposure:.9,iblIntensity:.72},wetland:{splat:`wetland`,maxHeight:1.4,sun:`#d6ddc8`,fill:`#9fb29a`,sky:{elevationDeg:24,azimuthDeg:115,turbidity:9,rayleigh:1.1},fog:{near:.5,far:2,color:`#92a288`},sunIntensity:1.5,exposure:.94,ambient:.24,iblIntensity:.66},volcanic:{splat:`volcanic`,maxHeight:4,roughness:.7,sun:`#ff8a4a`,fill:`#7a4a3a`,sky:{elevationDeg:14,azimuthDeg:135,turbidity:10,rayleigh:.3},fog:{near:.18,far:1.4,color:`#2a211c`},sunIntensity:1.7,exposure:.86,ambient:.22,iblIntensity:.5}};function nA(e){let{seed:t,theme:n=`island`,size:r=200,water:i=!1}=e,a=tA[n];if(!a)throw new y(`BAD_FORMAT`,`generateTerrain theme must be one of [${Xk.join(`, `)}], got '${n}'.`,{prop:`theme`,validOptions:[...Xk]});let o=e.maxHeight||a.maxHeight,s=new b(t),c=n===`island`,l=e=>sy({width:r,depth:r,segsX:Zk,segsZ:Zk,maxHeight:e,seed:t,...a.roughness===void 0?{}:{roughness:a.roughness},...a.detail===void 0?{}:{detail:a.detail},islandEdge:c}),u=l(o);if(c)for(let e=0;e<3;e++){let e=iA(u)-Qk,t=u.maxHeight-u.minHeight,n=u.minHeight+eA*t;if(e+$k<=n)break;let r=Qk-$k,i=iA(u)-u.minHeight-eA*t;o=Q(o*Math.min(r/i*.95,.9)),u=l(o)}let d=[{name:`Ground`,type:`StaticBody3D`,props:{collider:{shape:`heightfield`}},children:[{name:`Surface`,type:`Terrain3D`,props:{size:[r,r],maxHeight:o,seed:t,theme:a.splat,...a.roughness===void 0?{}:{roughness:a.roughness},...a.detail===void 0?{}:{detail:a.detail}}}]}],f=n===`wetland`;if(c)d.push({name:`Sea`,type:`Water3D`,props:{size:[r*8,r*8],position:[0,rA(u),0],opacity:1}});else if(f){let e=u.maxHeight-u.minHeight||1,t=Q(u.minHeight+.22*e);d.push({name:`Swamp`,type:`Water3D`,props:{size:[r,r],position:[0,t,0],waveHeight:.02,quality:`simple`,color:`#3a4a30`,opacity:.95}})}else if(i){let e=Q(u.minHeight+.1*(u.maxHeight-u.minHeight));d.push({name:`Lake`,type:`Water3D`,props:{size:[r,r],position:[0,e,0],waveHeight:.04}})}return d.push(...bA(n,s,u,r)),c&&d.push(Yk({seed:s.int(1,1e9),count:6,area:r,altitude:Math.round(u.maxHeight+12)})),d.push(...nk(r).map((e,t)=>xA(e,t===0?a.sun:a.fill,t===0?a.sunIntensity??1.7:void 0))),{name:`Terrain`,type:`Node3D`,children:d}}function rA(e){let t=iA(e)-Qk,n=e.minHeight+eA*(e.maxHeight-e.minHeight);return Q(Math.max(Math.min(t+$k,n),t+.55))}function iA(e){let t=-1/0,n=e.width/2,r=e.depth/2;for(let i=0;i<=Zk;i++){let a=-n+i/Zk*e.width,o=-r+i/Zk*e.depth;t=Math.max(t,e.baseHeight(a,-r),e.baseHeight(a,r),e.baseHeight(-n,o),e.baseHeight(n,o))}return t}function aA(e,t,n){let r=Math.min(t.width/2-n.margin,n.within??1/0),i=t.maxHeight-t.minHeight||1,a=[];for(let o=0;o<n.count*30&&a.length<n.count;o++){let o=Q(e.range(-r,r)),s=Q(e.range(-r,r));if(n.clearing&&Math.hypot(o,s)<n.clearing||n.within&&Math.hypot(o,s)>n.within)continue;let c=t.heightAt(o,s),l=(c-t.minHeight)/i;l<n.band[0]||l>n.band[1]||t.slopeAt(o,s)>n.maxSlope||a.push({x:o,z:s,y:c})}return a}var oA=[{canopy:`#2f5d44`,trunk:`#6e4a32`},{canopy:`#356a4c`,trunk:`#71503a`},{canopy:`#2c5740`,trunk:`#5f4530`},{canopy:`#3a6b4a`,trunk:`#6a4c34`}],sA=[{canopy:`#4a7c3f`,trunk:`#7a5a3a`},{canopy:`#56883c`,trunk:`#806044`},{canopy:`#7a9d3e`,trunk:`#9a9488`},{canopy:`#86a346`,trunk:`#a39c8e`},{canopy:`#b8862f`,trunk:`#7e5e38`},{canopy:`#a8702c`,trunk:`#74552f`}],cA={canopy:`#9aa052`,trunk:`#8a6a44`},lA={canopy:`#39513f`,trunk:`#5a5650`};function uA(e,t){return e===`conifer`?t.pick(oA):e===`broadleaf`?t.pick(sA):null}function dA(e,t,n,r,i){let a=i?.height??[4.5,7],o=i?.sink??(i?.count===void 0?.05:.3),s={};i?.tier!==void 0&&(s.tier=i.tier),i?.count!==void 0&&i.area!==void 0&&(s.count=i.count,s.area=[i.area,i.area]);let c=uA(r,n),l=i?.palette??c;return l&&(s.canopyColor=l.canopy,s.trunkColor=l.trunk),{name:e,type:`Tree3D`,props:{type:r,seed:n.int(1,1e9),height:Q(n.range(a[0],a[1])),position:[t.x,Q(t.y-o),t.z],...s}}}function fA(e,t,n,r,i,a=0,o){return{name:e,type:`Foliage3D`,props:{kind:`grass`,style:`tufts`,area:[r,r],density:i,height:o?.height??.3,...o?.colors?{colorA:o.colors[0],colorB:o.colors[1]}:{},seed:n.int(1,1e9),position:[t.x,Q(t.y+.02),t.z],...a>0?{flowers:a}:{}}}}function pA(e,t,n,r,i){return{name:e,type:`Foliage3D`,props:{kind:`reeds`,style:`simple`,area:[r,r],density:i,height:.9,colorA:`#2f4a26`,colorB:`#5a6f33`,seed:n.int(1,1e9),position:[t.x,Q(t.y+.02),t.z]}}}function mA(e,t,n,r,i){return{name:e,type:`Flowers3D`,props:{density:i,area:[r,r],seed:n.int(1,1e9),position:[t.x,Q(t.y+.02),t.z]}}}function hA(e,t,n,r){return e.map((e,i)=>{let a=ek(i+1,e.x,e.z,t,n,r?.(t)),o=a.props?.position;return o[1]=Q(o[1]+e.y),a})}function gA(e){let t=Math.round(e.range(104,128)),n=Math.round(t-e.range(10,18)),r=Math.round(t-e.range(20,30)),i=e=>e.toString(16).padStart(2,`0`);return`#${i(n)}${i(t)}${i(r)}`}function _A(e){let t=Math.round(e.range(196,224)),n=Math.min(255,t+Math.round(e.range(4,12))),r=e=>e.toString(16).padStart(2,`0`);return`#${r(t)}${r(t)}${r(n)}`}function vA(e){let t=Math.round(e.range(34,56)),n=Math.min(255,t+Math.round(e.range(2,8))),r=e=>e.toString(16).padStart(2,`0`);return`#${r(n)}${r(t)}${r(t)}`}var yA=[`#6f5b41`,`#7a644a`,`#665439`];function bA(e,t,n,r){let i=[];switch(e){case`island`:{let e=aA(t,n,{count:7,band:[.18,.6],maxSlope:.5,margin:26});i.push(...e.map((e,n)=>dA(`Tree${n+1}`,e,t,`conifer`)));break}case`alpine`:{let e=aA(t,n,{count:10,band:[.2,.5],maxSlope:.55,margin:6});i.push(...e.map((e,n)=>dA(`Tree${n+1}`,e,t,`conifer`)));let r=aA(t,n,{count:8,band:[.55,1],maxSlope:.9,margin:6});i.push(...hA(r,t,[.6,1.8]));break}case`plains`:{let e=aA(t,n,{count:8,band:[0,1],maxSlope:.4,margin:6});i.push(...e.map((e,n)=>dA(`Tree${n+1}`,e,t,`broadleaf`)));let r=aA(t,n,{count:6,band:[0,1],maxSlope:.5,margin:6});i.push(...hA(r,t,[.4,1.2]));break}case`desert`:{let e=aA(t,n,{count:6,band:[0,1],maxSlope:.45,margin:6});i.push(...e.map((e,n)=>dA(`Tree${n+1}`,e,t,`dead`)));let r=aA(t,n,{count:8,band:[0,1],maxSlope:.6,margin:6});i.push(...hA(r,t,[.5,1.6]));break}case`meadow`:{let e=Q(r*.09),a=aA(t,n,{count:7,band:[0,1],maxSlope:.05,margin:e/2+8});i.push(...a.map((n,r)=>fA(`Carpet${r+1}`,n,t,e,30)));let o=Q(r*.07),s=aA(t,n,{count:3,band:[0,1],maxSlope:.05,margin:o/2+8});i.push(...s.map((e,n)=>mA(`Flowers${n+1}`,e,t,o,`sparse`)));let c=aA(t,n,{count:4,band:[0,1],maxSlope:.1,margin:12});i.push(...c.map((e,n)=>dA(`Grove${n+1}`,e,t,`broadleaf`,{count:3,area:6})));let l=aA(t,n,{count:6,band:[0,1],maxSlope:.2,margin:8});i.push(...hA(l,t,[.3,.9]));break}case`forest`:{let e=Q(r*.12),a=r/2-8,o=Be(t.int(1,1e9)),s=(e,t,n)=>{let r=o(e/70,t/70);return r>.12?`conifer`:r<-.12?`broadleaf`:n%2==0?`conifer`:`broadleaf`},c=aA(t,n,{count:40,band:[0,1],maxSlope:.18,margin:8,clearing:e+6}),l=c.map((e,t)=>s(e.x,e.z,t));i.push(...c.map((e,n)=>dA(`Grove${n+1}`,e,t,l[n],{count:12,area:13,height:[5.5,8]}))),c.forEach((e,n)=>{n%3==0&&i.push(dA(`Sapling${n+1}`,e,t,l[n],{count:4,area:19,height:[2.5,3.4],sink:.12}))});let u=aA(t,n,{count:6,band:[0,1],maxSlope:.2,margin:12,clearing:e+4});i.push(...u.map((e,n)=>dA(`Elder${n+1}`,e,t,s(e.x,e.z,n),{tier:`high`,height:[8.4,9.8]})));let d=aA(t,n,{count:3,band:[0,1],maxSlope:.18,margin:10,clearing:e+6});i.push(...d.map((e,n)=>dA(`Accent${n+1}`,e,t,`broadleaf`,{tier:`high`,count:3,area:7,height:[5.6,6.8]})));let f=aA(t,n,{count:10,band:[0,1],maxSlope:.14,margin:9,clearing:e+2});i.push(...f.map((e,n)=>dA(`Bush${n+1}`,e,t,`bush`,{count:4,area:10,height:[2.1,3.1],sink:.12})));let p=0;c.forEach((e,r)=>{if(r%3==2)return;let o=t.range(0,Math.PI*2),s=t.range(2,4.5),c=t.int(1,1e9),l=Q(ik(e.x+Math.cos(o)*s,-a,a)),u=Q(ik(e.z+Math.sin(o)*s,-a,a));n.slopeAt(l,u)>.09||(p++,i.push({name:`Fern${p}`,type:`Foliage3D`,props:{kind:`grass`,style:`tufts`,tuftStyle:`fern`,area:[8,8],density:9,height:.4,colorA:`#4a6b34`,colorB:`#82a258`,seed:c,position:[l,Q(n.heightAt(l,u)+.02),u]}}))});let m=Q(r*.08),h=aA(t,n,{count:5,band:[0,1],maxSlope:.06,margin:m/2+8});i.push(...h.map((e,n)=>fA(`Grass${n+1}`,e,t,m,20,0,{height:.24,colors:[`#4f7034`,`#85a154`]})));let g=u.length>0?t.int(3,5):0;for(let e=0;e<g;e++){let r=u[e%u.length],o=t.range(0,Math.PI*2),s=t.range(2.5,4.5),c=Q(ik(r.x+Math.cos(o)*s,-a,a)),l=Q(ik(r.z+Math.sin(o)*s,-a,a));i.push({name:`Log${e+1}`,type:`Tree3D`,props:{type:`dead`,seed:t.int(1,1e9),height:Q(t.range(4.2,5.6)),trunkColor:`#4a4236`,position:[c,Q(n.heightAt(c,l)+.12),l],rotation:[0,t.int(0,359),Q(t.range(81,97))]}})}aA(t,n,{count:5,band:[0,1],maxSlope:.25,margin:9,clearing:e}).forEach((e,n)=>{let r=Q(t.range(.16,.28)),a=Q(t.range(.3,.55));i.push({name:`Stump${n+1}`,type:`MeshInstance3D`,props:{mesh:`cylinder`,size:[r,a,r],position:[e.x,Q(e.y+a/2-.06),e.z],rotation:[0,t.int(0,359),0],material:{color:t.pick(yA),roughness:1},castShadow:!0}})});let _=aA(t,n,{count:8,band:[0,1],maxSlope:.2,margin:8});i.push(...hA(_,t,[.3,1],gA));let v=Q(e*.75),y=aA(t,n,{count:3,band:[0,1],maxSlope:.06,margin:8,within:e-Q(v/Math.SQRT2)});i.push(...y.map((e,n)=>fA(`Clearing${n+1}`,e,t,v,30,0,{colors:[`#5d8438`,`#a8bc60`]})));let b=Q(e*.45),x=aA(t,n,{count:2,band:[0,1],maxSlope:.06,margin:8,within:e-Q(b/Math.SQRT2)});i.push(...x.map((e,n)=>mA(`Flowers${n+1}`,e,t,b,`sparse`)));break}case`savanna`:{let e=aA(t,n,{count:7,band:[0,.85],maxSlope:.35,margin:8});i.push(...e.map((e,n)=>dA(`Tree${n+1}`,e,t,`broadleaf`,{height:[3.5,5],palette:cA})));let a=Q(r*.09),o=aA(t,n,{count:6,band:[0,1],maxSlope:.05,margin:a/2+8});i.push(...o.map((e,n)=>fA(`Carpet${n+1}`,e,t,a,26,0,{height:.34,colors:[`#9a8f43`,`#c9bd6a`]})));let s=aA(t,n,{count:7,band:[0,1],maxSlope:.5,margin:8});i.push(...hA(s,t,[.4,1.4]));let c=aA(t,n,{count:4,band:[0,1],maxSlope:.2,margin:9});i.push(...c.map((e,n)=>dA(`Scrub${n+1}`,e,t,`bush`,{count:3,area:8,height:[1.4,2.2]})));break}case`snow`:{let e=aA(t,n,{count:8,band:[0,.9],maxSlope:.4,margin:8});i.push(...e.map((e,n)=>dA(`Tree${n+1}`,e,t,`conifer`,{height:[4,6.5],palette:lA})));let r=aA(t,n,{count:10,band:[0,1],maxSlope:.7,margin:8});i.push(...hA(r,t,[.5,1.8],_A));break}case`wetland`:{let e=Q(r*.08),a=aA(t,n,{count:6,band:[0,.5],maxSlope:.06,margin:e/2+8});i.push(...a.map((n,r)=>pA(`Reeds${r+1}`,n,t,e,4)));let o=Q(r*.08),s=aA(t,n,{count:4,band:[.2,1],maxSlope:.05,margin:o/2+8});i.push(...s.map((e,n)=>fA(`Moss${n+1}`,e,t,o,22,0,{height:.22,colors:[`#3a5a2c`,`#6f8a48`]})));let c=aA(t,n,{count:6,band:[.25,1],maxSlope:.35,margin:9});i.push(...c.map((e,n)=>dA(`Snag${n+1}`,e,t,`dead`,{height:[4,6]})));let l=aA(t,n,{count:6,band:[.2,1],maxSlope:.2,margin:9});i.push(...l.map((e,n)=>dA(`Bush${n+1}`,e,t,`bush`,{count:3,area:7,height:[1.6,2.6]})));let u=aA(t,n,{count:5,band:[.2,1],maxSlope:.3,margin:8});i.push(...hA(u,t,[.3,1],gA));break}case`volcanic`:{let e=aA(t,n,{count:7,band:[0,1],maxSlope:.45,margin:8});i.push(...e.map((e,n)=>dA(`Tree${n+1}`,e,t,`dead`,{height:[4,6.5]})));let r=aA(t,n,{count:12,band:[0,1],maxSlope:.7,margin:8});i.push(...hA(r,t,[.4,1.8],vA)),aA(t,n,{count:3,band:[0,.6],maxSlope:.18,margin:12}).forEach((e,n)=>{t.int(1,1e9),i.push({name:`Smoke${n+1}`,type:`Particles3D`,props:{preset:`smoke`,position:[e.x,Q(e.y+.2),e.z],rate:8,maxParticles:64,colorStart:`#5a5048`,colorEnd:`#2a2422`,sizeStart:18,sizeEnd:44}}),i.push({name:`Embers${n+1}`,type:`Particles3D`,props:{position:[e.x,Q(e.y+.1),e.z],rate:10,maxParticles:48,lifetime:[.8,1.8],speed:[12,34],directionDeg:-90,spreadDeg:40,gravity:[0,-18],sizeStart:4,sizeEnd:1,colorStart:`#ffce6a`,colorEnd:`#d83a14`,blend:`add`}})});break}}return i}function xA(e,t,n){return{...e,props:{...e.props,color:t,...n===void 0?{}:{intensity:n}}}}var SA={arena:{description:`FPS stage: floor, 4 perimeter walls, obstacles, lights — themes: boxes (crates), ruins (broken stone rows), garden (hedges, grass, pool)`,dimension:`3d`,params:{theme:{type:`string`,default:`boxes`,options:[...ak]},width:{type:`number`,default:30,min:4},depth:{type:`number`,default:30,min:4},wallHeight:{type:`number`,default:3,min:.5},obstacles:{type:`number`,default:8,min:0}}},terrain:{description:`Heightfield world: StaticBody3D{heightfield} + Terrain3D + theme dressing (trees, rocks, grass, sea/clouds on island, broad swamp water on wetland, smoke/ember emitters on volcanic; maxHeight 0 = theme default; water adds a lake to non-island themes)`,dimension:`3d`,params:{theme:{type:`string`,default:`island`,options:[...Xk]},size:{type:`number`,default:200,min:40,max:400},maxHeight:{type:`number`,default:0,min:0},water:{type:`boolean`,default:!1}}},maze:{description:`Recursive-backtracker 3D maze, west→east — themes: stone, hedge (green + grass), canyon (sandstone + rim rocks)`,dimension:`3d`,params:{theme:{type:`string`,default:`stone`,options:[...Ck]},width:{type:`number`,default:8,min:2,max:40},depth:{type:`number`,default:8,min:2,max:40},cellSize:{type:`number`,default:2,min:.5},wallHeight:{type:`number`,default:2.5,min:.5}}},maze2d:{description:`The same maze algorithm as 2D ColorRect2D + StaticBody2D tiles`,dimension:`2d`,params:{cols:{type:`number`,default:10,min:2,max:40},rows:{type:`number`,default:8,min:2,max:40},cellPx:{type:`number`,default:64,min:8}}},dungeon2d:{description:`Roguelike rooms + L-corridors: floor rects, wall bodies (32px tiles)`,dimension:`2d`,params:{rooms:{type:`number`,default:5,min:1,max:20},size:{type:`number`,default:960,min:256}}},platforms2d:{description:`Left-to-right 2D platform course (tune ranges via the library)`,dimension:`2d`,params:{count:{type:`number`,default:10,min:1}}}},CA={arena:uk,terrain:nA,maze:Ak,maze2d:Vk,dungeon2d:vk,platforms2d:Wk};function wA(e,t){let n=CA[e];if(!n){let t=Object.keys(CA);throw new y(`BAD_FORMAT`,`Unknown generator '${e}'. Valid: [${t.join(`, `)}] — the old meadow/forest/island/rocks/clouds generators became themes (e.g. terrain theme: 'meadow', arena theme: 'garden'); scatter is library-only (needs item templates).`,{validOptions:t})}return n(t)}function TA(e){return Object.entries(SA).filter(([,t])=>t.dimension===e).map(([e,t])=>({name:e,meta:t}))}function EA(e,t){if(e.type===`boolean`)return t===!0||t===`true`;if(e.type===`number`){let n=typeof t==`boolean`?NaN:Number(t);return(t===``||!Number.isFinite(n))&&(n=e.default),e.min!==void 0&&(n=Math.max(e.min,n)),e.max!==void 0&&(n=Math.min(e.max,n)),n}let n=String(t);return e.options&&!e.options.includes(n)?e.default:n}function DA(){return Math.floor(Math.random()*1e6)}var OA=[],kA=new Map,AA=null;function jA(e){let t=document.querySelector(`#generate`);t&&(PA(e),t.removeAttribute(`hidden`))}function MA(){document.querySelector(`#generate`)?.setAttribute(`hidden`,``)}function NA(e){let t=document.querySelector(`#generate`);t&&(t.addEventListener(`pointerdown`,e=>{e.target===t&&MA()}),document.querySelector(`#generate-close`)?.addEventListener(`click`,MA),document.querySelector(`#generate-cancel`)?.addEventListener(`click`,MA),document.querySelector(`#generate-insert`)?.addEventListener(`click`,()=>FA(e)))}function PA(e){let t=document.querySelector(`#generate-body`),n=document.querySelector(`#generate-status`);if(!t)return;n&&(n.textContent=``),t.textContent=``;let r=e.working.dimension??`2d`;OA=TA(r);let i=document.createElement(`p`);i.className=`gen-hint`,i.textContent=aO({en:`Deterministic ${r.toUpperCase()} environment generators — the subtree inserts under the selected node (the root when nothing is selected). The same seed always generates the same level.`,ko:`결정적 ${r.toUpperCase()} 환경 생성기 — 생성된 서브트리는 선택한 노드 아래에(선택이 없으면 루트에) 들어갑니다. 같은 시드는 항상 같은 레벨을 만듭니다.`}),t.appendChild(i);let a=IA(aO({en:`generator`,ko:`생성기`})),o=document.createElement(`select`);o.id=`generate-name`;for(let{name:e}of OA){let t=document.createElement(`option`);t.value=e,t.textContent=e,o.appendChild(t)}a.appendChild(o),t.appendChild(a);let s=document.createElement(`p`);s.className=`gen-desc`,t.appendChild(s);let c=document.createElement(`div`);c.id=`generate-params`,t.appendChild(c);let l=IA(`seed`);AA=document.createElement(`input`),AA.type=`number`,AA.step=`1`,AA.value=String(DA());let u=document.createElement(`button`);u.type=`button`,u.className=`ghost`,u.textContent=`↻`,u.title=aO({en:`New random seed`,ko:`새 랜덤 시드`}),u.addEventListener(`click`,()=>{AA&&(AA.value=String(DA()))});let d=document.createElement(`div`);d.className=`gen-seed-row`,d.append(AA,u),l.appendChild(d),t.appendChild(l);let f=()=>{let e=OA.find(e=>e.name===o.value)??OA[0];if(e){s.textContent=e.meta.description,c.textContent=``,kA=new Map;for(let[t,n]of Object.entries(e.meta.params)){let e=IA(t),r;if(n.type===`boolean`)r=document.createElement(`input`),r.type=`checkbox`,r.checked=n.default===!0;else if(n.type===`string`&&n.options){r=document.createElement(`select`);for(let e of n.options){let t=document.createElement(`option`);t.value=e,t.textContent=e,r.appendChild(t)}r.value=String(n.default)}else r=document.createElement(`input`),r.type=n.type===`number`?`number`:`text`,n.type===`number`&&(r.step=`any`,n.min!==void 0&&(r.min=String(n.min)),n.max!==void 0&&(r.max=String(n.max))),r.value=String(n.default),(n.min!==void 0||n.max!==void 0)&&(r.title=`${n.min??``}–${n.max??``}`);kA.set(t,r),e.appendChild(r),c.appendChild(e)}}};o.addEventListener(`change`,f),f()}function FA(e){let t=document.querySelector(`#generate-status`),n=document.querySelector(`#generate-name`),r=OA.find(e=>e.name===n?.value);if(!r||!AA)return;let i={seed:EA({type:`number`,default:DA(),min:0},AA.value)};for(let[e,t]of kA){let n=r.meta.params[e];n&&(i[e]=EA(n,t.type===`checkbox`?t.checked:t.value))}let a;try{a=wA(r.name,i)}catch(e){t&&(t.textContent=e instanceof Error?e.message:String(e));return}let o=e.insertNode(a,e.selection??[]);if(o===null){t&&(t.textContent=aO({en:`Insert failed — see the error banner.`,ko:`삽입 실패 — 에러 배너를 확인하세요.`}));return}e.select(o),AA&&(AA.value=String(DA())),MA()}function IA(e){let t=document.createElement(`label`);t.className=`field`;let n=document.createElement(`span`);return n.textContent=e,t.appendChild(n),t}var LA={groups:{title:{en:`groups — tag nodes for queries`,ko:`groups — 조회용 태그`},body:{en:`Free-form tags. Game code finds nodes with tree-wide queries like getNodesInGroup("coins"), and triggers can filter by group (e.g. only react to "player"). Type a name and press Enter.`,ko:`자유 형식 태그입니다. 게임 코드가 getNodesInGroup("coins")처럼 트리 전체에서 노드를 찾고, 트리거는 그룹으로 거릅니다(예: "player"에만 반응). 이름을 입력하고 Enter를 누르세요.`},example:`triggerEnter → if (other.isInGroup("player")) collect()`},script:{title:{en:`script — attach YOUR game logic`,ko:`script — 게임 로직 연결`},body:{en:`A Behavior is a TypeScript class living in YOUR game code, linked by name. The editor stores the link; the class itself must be registered in the game before loadScene. Props here are passed to the behavior instance.`,ko:`Behavior는 게임 코드에 있는 TypeScript 클래스이며 이름으로 연결됩니다. 에디터는 연결만 저장하고, 클래스 자체는 loadScene 전에 게임에서 등록되어야 합니다. 여기의 props가 비헤이비어 인스턴스로 전달됩니다.`},example:`registerBehavior('CoinCounter', CoinCounter) // in your main.ts`,copy:{label:`copy behavior boilerplate`,text:`import { Behavior, registerBehavior } from 'incanto';
|
|
7356
7356
|
|
|
7357
7357
|
export class MyBehavior extends Behavior {
|
|
7358
7358
|
static props = { speed: { default: 100 } };
|
|
@@ -7362,4 +7362,4 @@ export class MyBehavior extends Behavior {
|
|
|
7362
7362
|
update(dt: number): void {}
|
|
7363
7363
|
}
|
|
7364
7364
|
registerBehavior('MyBehavior', MyBehavior); // before loadScene()
|
|
7365
|
-
`}},network:{title:{en:`network — replicate to other players`,ko:`network — 다른 플레이어에게 복제`},body:{en:`mode "owner" means THIS player owns the node and broadcasts the listed sync keys to everyone in the room (one owner node per player — usually your player character). Other players see it via a NetworkSpawner. Keys are relative to this node: "position", or "Skin.animation" for a child prop.`,ko:`mode "owner"는 이 플레이어가 노드를 소유하고 sync 키 목록을 방의 모두에게 송출한다는 뜻입니다(플레이어당 owner 노드 하나 — 보통 내 캐릭터). 다른 플레이어는 NetworkSpawner로 봅니다. 키는 이 노드 기준 상대 표기입니다: "position", 자식 prop은 "Skin.animation".`},example:`{ "mode": "owner", "sync": ["position"], "throttleMs": 50 }`},collider:{title:{en:`collider — the physics shape`,ko:`collider — 물리 모양`},body:{en:`Shapes: rect (size [w,h]), circle (radius), capsule (radius + height — good for characters). offset shifts the shape from the node position. The green dashed outline in the viewport shows exactly where it is.`,ko:`모양: rect(size [w,h]), circle(radius), capsule(radius+height — 캐릭터에 적합). offset이 노드 위치에서 모양을 이동시킵니다. 뷰포트의 초록 점선이 정확한 위치를 보여줍니다.`},example:`{ "shape": "capsule", "radius": 12, "height": 16 }`},physics:{title:{en:`physics — scene gravity`,ko:`physics — 씬 중력`},body:{en:`World gravity in px/s² (y-down: positive y pulls DOWN). [0, 1400] feels platformer-y; [0, 0] for top-down. Takes effect when the game calls enablePhysics2D — and in the editor’s play mode.`,ko:`월드 중력, px/s² 단위(y-아래: 양수 y가 아래로 당김). [0, 1400]이면 플랫포머 느낌, 탑다운은 [0, 0]. 게임이 enablePhysics2D를 부를 때 — 그리고 에디터 플레이 모드에서 — 적용됩니다.`},example:`"physics": { "gravity": [0, 1400] }`}},MA=null;function NA(e){document.addEventListener(`click`,t=>{let n=t.target,r=n.closest(`[data-help]`);if(!r){e.contains(n)||(e.hidden=!0);return}let i=jA[r.dataset.help??``];if(!i)return;if(!e.hidden&&MA===r){e.hidden=!0;return}MA=r,e.textContent=``;let a=document.createElement(`h4`);a.textContent=$D(i.title);let o=document.createElement(`div`);if(o.textContent=$D(i.body),e.append(a,o),i.example){let t=document.createElement(`pre`);t.textContent=i.example,e.appendChild(t)}if(i.copy){let t=document.createElement(`div`);t.className=`pop-actions`;let n=document.createElement(`button`);n.type=`button`,n.className=`linklike`,n.textContent=`⧉ ${i.copy.label}`,n.addEventListener(`click`,()=>{navigator.clipboard.writeText(i.copy?.text??``),n.textContent=`✓ copied`}),t.appendChild(n),e.appendChild(t)}e.hidden=!1;let s=r.getBoundingClientRect();e.style.left=`${Math.max(8,Math.min(innerWidth-300-8,s.left-300+20))}px`,e.style.top=`${Math.min(innerHeight-60,s.bottom+8)}px`})}function PA(e){let t=document.createElement(`button`);return t.type=`button`,t.className=`help-btn`,t.dataset.help=e,t.textContent=`?`,t.title=`What is this?`,t}var FA=JSON.parse('[{"name":"2dbasic","file":"characters/2dbasic.png","url":"incanto/assets/characters/2dbasic.png","kind":"character","bytes":30499,"description":"2dbasic sprite sheet image.anything,base character. (frame size 192x192)","animation":"characters/2dbasic.json","frameWidth":111,"frameHeight":83},{"name":"attacked","file":"audio/attacked.mp3","url":"incanto/assets/audio/attacked.mp3","kind":"audio","bytes":8757,"description":"Short hurt / took-damage SFX for the player or an enemy."},{"name":"bark_birch_color","file":"vegetation/bark/birch_color_1k.jpg","url":"https://agent8-games.verse8.io/assets/3D/default/textures/vegetation/bark/birch_color_1k.jpg","kind":"foliage","bytes":194186,"description":"Birch bark base color texture (1k), ambientcg Bark (CC0) packaged by ez-tree (MIT). Tree3D trunks/branches sample it by default from the agent8 CDN (the only sanctioned external host); the bundled `incanto/assets` copy (see this entry’s `file`) serves it offline."},{"name":"bark_birch_normal","file":"vegetation/bark/birch_normal_1k.jpg","url":"https://agent8-games.verse8.io/assets/3D/default/textures/vegetation/bark/birch_normal_1k.jpg","kind":"foliage","bytes":378046,"description":"Birch bark tangent-space normal texture (1k), ambientcg Bark (CC0) packaged by ez-tree (MIT). Tree3D trunks/branches sample it by default from the agent8 CDN (the only sanctioned external host); the bundled `incanto/assets` copy (see this entry’s `file`) serves it offline."},{"name":"bark_birch_roughness","file":"vegetation/bark/birch_roughness_1k.jpg","url":"https://agent8-games.verse8.io/assets/3D/default/textures/vegetation/bark/birch_roughness_1k.jpg","kind":"foliage","bytes":127387,"description":"Birch bark roughness texture (1k), ambientcg Bark (CC0) packaged by ez-tree (MIT). Tree3D trunks/branches sample it by default from the agent8 CDN (the only sanctioned external host); the bundled `incanto/assets` copy (see this entry’s `file`) serves it offline."},{"name":"bark_oak_color","file":"vegetation/bark/oak_color_1k.jpg","url":"https://agent8-games.verse8.io/assets/3D/default/textures/vegetation/bark/oak_color_1k.jpg","kind":"foliage","bytes":297877,"description":"Oak bark base color texture (1k), ambientcg Bark (CC0) packaged by ez-tree (MIT). Tree3D trunks/branches sample it by default from the agent8 CDN (the only sanctioned external host); the bundled `incanto/assets` copy (see this entry’s `file`) serves it offline."},{"name":"bark_oak_normal","file":"vegetation/bark/oak_normal_1k.jpg","url":"https://agent8-games.verse8.io/assets/3D/default/textures/vegetation/bark/oak_normal_1k.jpg","kind":"foliage","bytes":67610,"description":"Oak bark tangent-space normal texture (1k), ambientcg Bark (CC0) packaged by ez-tree (MIT). Tree3D trunks/branches sample it by default from the agent8 CDN (the only sanctioned external host); the bundled `incanto/assets` copy (see this entry’s `file`) serves it offline."},{"name":"bark_oak_roughness","file":"vegetation/bark/oak_roughness_1k.jpg","url":"https://agent8-games.verse8.io/assets/3D/default/textures/vegetation/bark/oak_roughness_1k.jpg","kind":"foliage","bytes":16648,"description":"Oak bark roughness texture (1k), ambientcg Bark (CC0) packaged by ez-tree (MIT). Tree3D trunks/branches sample it by default from the agent8 CDN (the only sanctioned external host); the bundled `incanto/assets` copy (see this entry’s `file`) serves it offline."},{"name":"bark_pine_color","file":"vegetation/bark/pine_color_1k.jpg","url":"https://agent8-games.verse8.io/assets/3D/default/textures/vegetation/bark/pine_color_1k.jpg","kind":"foliage","bytes":196361,"description":"Pine bark base color texture (1k), ambientcg Bark (CC0) packaged by ez-tree (MIT). Tree3D trunks/branches sample it by default from the agent8 CDN (the only sanctioned external host); the bundled `incanto/assets` copy (see this entry’s `file`) serves it offline."},{"name":"bark_pine_normal","file":"vegetation/bark/pine_normal_1k.jpg","url":"https://agent8-games.verse8.io/assets/3D/default/textures/vegetation/bark/pine_normal_1k.jpg","kind":"foliage","bytes":58237,"description":"Pine bark tangent-space normal texture (1k), ambientcg Bark (CC0) packaged by ez-tree (MIT). Tree3D trunks/branches sample it by default from the agent8 CDN (the only sanctioned external host); the bundled `incanto/assets` copy (see this entry’s `file`) serves it offline."},{"name":"bark_pine_roughness","file":"vegetation/bark/pine_roughness_1k.jpg","url":"https://agent8-games.verse8.io/assets/3D/default/textures/vegetation/bark/pine_roughness_1k.jpg","kind":"foliage","bytes":36136,"description":"Pine bark roughness texture (1k), ambientcg Bark (CC0) packaged by ez-tree (MIT). Tree3D trunks/branches sample it by default from the agent8 CDN (the only sanctioned external host); the bundled `incanto/assets` copy (see this entry’s `file`) serves it offline."},{"name":"box","file":"items/box.png","url":"incanto/assets/items/box.png","kind":"item","bytes":10861,"description":"Item box sprite for Dungeons and Dungeoners. Container sprite that holds random items or rewards when opened by player."},{"name":"buff_potion","file":"items/buff_potion.png","url":"incanto/assets/items/buff_potion.png","kind":"item","bytes":3809,"description":"Buff potion item sprite for Dungeons and Dungeoners. Consumable item that grants temporary stat boosts or positive effects to player character."},{"name":"coin","file":"items/coin.png","url":"incanto/assets/items/coin.png","kind":"item","bytes":1689,"description":"Gold coin collectible sprite for Dungeons and Dungeoners. Currency item with metallic sheen, used as in-game money or collectible reward."},{"name":"explosion","file":"audio/explosion.mp3","url":"incanto/assets/audio/explosion.mp3","kind":"audio","bytes":40124,"description":"Impactful explosion / large destructive hit SFX. Pair with the Particles2D \\"explosion\\" preset."},{"name":"floor00","file":"tiles/floor00.jpg","url":"incanto/assets/tiles/floor00.jpg","kind":"tile","bytes":28736,"description":"Basic floor tile texture for Dungeons and Dungeoners project, suitable for dungeon ground surfaces."},{"name":"gem","file":"items/gem.png","url":"incanto/assets/items/gem.png","kind":"item","bytes":8762,"description":"Gem collectible sprite for Dungeons and Dungeoners. Valuable gemstone item used as currency, crafting material, or quest objective."},{"name":"ghost","file":"characters/ghost.png","url":"incanto/assets/characters/ghost.png","kind":"character","bytes":22933,"description":"Ghost character with translucent appearance sprite sheet image (frame size 112x128)","animation":"characters/ghost.json","frameWidth":112,"frameHeight":128},{"name":"goblin","file":"characters/goblin.png","url":"incanto/assets/characters/goblin.png","kind":"character","bytes":57994,"description":"Medieval goblin with torch sprite sheet image (frame size 192x192)","animation":"characters/goblin.json","frameWidth":192,"frameHeight":192},{"name":"gold","file":"items/gold.png","url":"incanto/assets/items/gold.png","kind":"item","bytes":22313,"description":"Gold item sprite for Dungeons and Dungeoners. Gold pile or gold bar sprite representing valuable currency or treasure reward."},{"name":"gold-loot","file":"audio/gold_loot.mp3","url":"incanto/assets/audio/gold_loot.mp3","kind":"audio","bytes":7506,"description":"Metallic coin / gold pickup jingle — collecting currency."},{"name":"ground_dirt","file":"vegetation/ground/dirt_color.jpg","url":"https://agent8-games.verse8.io/assets/3D/default/textures/vegetation/ground/dirt_color.jpg","kind":"foliage","bytes":231383,"description":"Dense dirt/gravel ground texture (1024px) from the ez-tree demo app (MIT) — the reference meadow ground. Terrain3D grassland themes tile it for slope + noise-patch dirt by default; also bundled in `incanto/assets` (see `file`) for offline use."},{"name":"ground_dirt_normal","file":"vegetation/ground/dirt_normal.jpg","url":"https://agent8-games.verse8.io/assets/3D/default/textures/vegetation/ground/dirt_normal.jpg","kind":"foliage","bytes":123478,"description":"Dirt ground normal map (1024px) from the ez-tree demo app (MIT). Terrain3D grassland themes apply it across the whole ground band (demo parity); also bundled in `incanto/assets` (see `file`) for offline use."},{"name":"ground_grass","file":"vegetation/ground/grass.jpg","url":"https://agent8-games.verse8.io/assets/3D/default/textures/vegetation/ground/grass.jpg","kind":"foliage","bytes":322486,"description":"Mossy meadow grass ground texture (1024px) from the ez-tree demo app (MIT). Terrain3D grassland themes (meadow/forest generators) tile it as the grass splat layer by default; also bundled in `incanto/assets` (see `file`) for offline use."},{"name":"heal","file":"audio/heal.mp3","url":"incanto/assets/audio/heal.mp3","kind":"audio","bytes":29764,"description":"Soothing health-restore / heal spell SFX."},{"name":"hit-metal-bang","file":"audio/hit_metal_bang.mp3","url":"incanto/assets/audio/hit_metal_bang.mp3","kind":"audio","bytes":17972,"description":"Metallic impact — hitting armor, metal, or a blocked attack."},{"name":"hp_potion","file":"items/hp_potion.png","url":"incanto/assets/items/hp_potion.png","kind":"item","bytes":3513,"description":"Health potion item sprite for Dungeons and Dungeoners. Consumable healing item that restores player health points when used."},{"name":"ice-spear","file":"audio/ice_spear.mp3","url":"incanto/assets/audio/ice_spear.mp3","kind":"audio","bytes":21315,"description":"Sharp piercing ice projectile fire SFX."},{"name":"leaves_ash","file":"vegetation/ash_color.png","url":"https://agent8-games.verse8.io/assets/3D/default/textures/vegetation/ash_color.png","kind":"foliage","bytes":181423,"description":"Ash leaf-cluster alpha-cutout texture (1024px) from ez-tree by Dan Greenheck (MIT). Tree3D samples it for broadleaf/dead ash variants by default; copy it next to your game and set leafTexture to serve offline."},{"name":"leaves_aspen","file":"vegetation/aspen_color.png","url":"https://agent8-games.verse8.io/assets/3D/default/textures/vegetation/aspen_color.png","kind":"foliage","bytes":142116,"description":"Aspen leaf-cluster alpha-cutout texture (1024px) from ez-tree by Dan Greenheck (MIT). Tree3D samples it for high-tier broadleaf aspen variants; copy + leafTexture for offline serving."},{"name":"leaves_oak","file":"vegetation/oak_color.png","url":"https://agent8-games.verse8.io/assets/3D/default/textures/vegetation/oak_color.png","kind":"foliage","bytes":238115,"description":"Oak leaf-cluster alpha-cutout texture (1024px) from ez-tree by Dan Greenheck (MIT). Tree3D samples it for broadleaf oak variants by default; copy + leafTexture for offline serving."},{"name":"leaves_pine","file":"vegetation/pine_color.png","url":"https://agent8-games.verse8.io/assets/3D/default/textures/vegetation/pine_color.png","kind":"foliage","bytes":303684,"description":"Pine needle-cluster alpha-cutout texture (1024px) from ez-tree by Dan Greenheck (MIT). Tree3D samples it for conifer variants by default; copy + leafTexture for offline serving."},{"name":"locked_item_box","file":"items/locked_item_box.png","url":"incanto/assets/items/locked_item_box.png","kind":"item","bytes":11074,"description":"Locked item box sprite for Dungeons and Dungeoners. Container sprite requiring key or lockpick to open, containing valuable rewards."},{"name":"map","file":"items/map.png","url":"incanto/assets/items/map.png","kind":"item","bytes":3754,"description":"Map item sprite for Dungeons and Dungeoners. Navigation item revealing dungeon layout or providing exploration assistance."},{"name":"medieval-knight","file":"characters/medieval-knight.png","url":"incanto/assets/characters/medieval-knight.png","kind":"character","bytes":84367,"description":"(frame size 192x192) Using a medieval-themed SD(Super Deformed) knight sprite sheet image, you can apply idle, move, and attack animations, among others.","animation":"characters/medieval-knight.json","frameWidth":192,"frameHeight":192},{"name":"minecraft-tiles","file":"tiles/minecraft-tiles.png","url":"incanto/assets/tiles/minecraft-tiles.png","kind":"tile","bytes":10511,"description":"Minecraft-themed tiles sprite sheet image (frame size 16x16)"},{"name":"monster-died","file":"audio/monster_died.mp3","url":"incanto/assets/audio/monster_died.mp3","kind":"audio","bytes":16836,"description":"Enemy defeat / death SFX."},{"name":"resurrection_potion","file":"items/resurrection_potion.png","url":"incanto/assets/items/resurrection_potion.png","kind":"item","bytes":3471,"description":"Resurrection potion item sprite for Dungeons and Dungeoners. Rare consumable that revives fallen party members or prevents death."},{"name":"slash","file":"audio/slash.mp3","url":"incanto/assets/audio/slash.mp3","kind":"audio","bytes":10425,"description":"Quick sword swing / melee slash SFX."},{"name":"smite","file":"audio/smite.mp3","url":"incanto/assets/audio/smite.mp3","kind":"audio","bytes":12956,"description":"Powerful holy downward-strike SFX."},{"name":"spells-cast","file":"audio/spells_cast.mp3","url":"incanto/assets/audio/spells_cast.mp3","kind":"audio","bytes":22151,"description":"Generic magical spell-cast / chant SFX."},{"name":"super_box","file":"items/super_box.png","url":"incanto/assets/items/super_box.png","kind":"item","bytes":12184,"description":"Super item box sprite for Dungeons and Dungeoners. Premium container sprite containing rare or powerful items and equipment."},{"name":"swoosh","file":"effects/swoosh.png","url":"incanto/assets/effects/swoosh.png","kind":"effect","bytes":2599,"description":"Swoosh effect sprite for Dungeons and Dungeoners. Motion blur or attack trail effect used for melee attacks, sword slashes, or fast movement animations."},{"name":"trap","file":"items/trap.png","url":"incanto/assets/items/trap.png","kind":"item","bytes":3969,"description":"Trap object sprite for Dungeons and Dungeoners. Hazard sprite that damages players when triggered in dungeon exploration."},{"name":"ui-click","file":"audio/ui_click.wav","url":"incanto/assets/audio/ui_click.wav","kind":"audio","bytes":17332,"description":"Short tactile UI click for menus and buttons."},{"name":"walk","file":"audio/walk.mp3","url":"incanto/assets/audio/walk.mp3","kind":"audio","bytes":5642,"description":"Single footstep SFX for character movement (loop or one-shot)."},{"name":"wall00","file":"tiles/wall00.jpg","url":"incanto/assets/tiles/wall00.jpg","kind":"tile","bytes":44344,"description":"Basic wall tile texture for Dungeons and Dungeoners project, suitable for dungeon vertical boundaries."}]');function IA(e){return(e.kind===`foliage`||e.kind===`tile`)&&!e.animation}function LA(e,t,n){if(e.textContent=``,t.selection===null){zA(e,t,n);return}let r=t.nodeAt(t.selection);r&&GA(e,t,r,n)}var RA=[`input`,`multiplayer`];function zA(e,t,n){e.appendChild(gj(`scene`)),e.appendChild(bj(`name`,String(t.working.name??``),e=>{t.mutate(()=>{t.working.name=e})}));let r=document.createElement(`select`);for(let e of[`2d`,`3d`]){let n=document.createElement(`option`);n.value=e,n.textContent=e,(t.working.dimension??`2d`)===e&&(n.selected=!0),r.appendChild(n)}r.addEventListener(`change`,()=>{t.mutate(()=>{t.working.dimension=r.value})}),e.appendChild(vj(`dimension`,r)),e.appendChild(gj(`physics`,`physics`));let i=(t.working.physics??{}).gravity??(t.working.dimension===`3d`?[0,-9.81,0]:[0,980]),a=t.working.dimension===`3d`?[0,-9.81,0]:[0,980];e.appendChild(Sj(`gravity`,i,e=>{t.mutate(()=>{if(JSON.stringify(e)===JSON.stringify(a)){let e={...t.working.physics};delete e.gravity,Object.keys(e).length===0?delete t.working.physics:t.working.physics=e}else t.working.physics={...t.working.physics,gravity:e}})})),e.appendChild(gj(`environment`)),BA(e,t),e.appendChild(gj(`constants`)),uj(e,t,n),e.appendChild(gj(`advanced`));for(let n of RA)e.appendChild(Cj(n,t.working[n],e=>{t.mutate(()=>{e===void 0?delete t.working[n]:t.working[n]=e})}))}function BA(e,t){let n=t.working.environment??{},r=e=>{t.mutate(()=>{let n={...t.working.environment??{},...e};for(let e of Object.keys(n))n[e]===void 0&&delete n[e];Object.keys(n).length===0?delete t.working.environment:t.working.environment=n})};e.appendChild(VA(`background`,n.background??``,e=>{r({background:e===``?void 0:e})})),e.appendChild(VA(`ambient color`,n.ambient?.color??``,e=>{let t={...n.ambient,color:e===``?void 0:e};t.color===void 0&&delete t.color,r({ambient:Object.keys(t).length?t:void 0})})),e.appendChild(xj(`ambient power`,n.ambient?.intensity??0,e=>{r({ambient:{...n.ambient,intensity:e}})}));let i=n.rendering??{},a=e=>{let t={...i,...e};for(let e of Object.keys(t))t[e]===void 0&&delete t[e];r({rendering:Object.keys(t).length?t:void 0})},o=document.createElement(`input`);o.type=`checkbox`,o.checked=i.antialias!==!1,o.addEventListener(`change`,()=>{a({antialias:o.checked?void 0:!1})}),e.appendChild(vj(`antialias`,o));let s=document.createElement(`select`);for(let[e,t]of[[``,`1 — soft hiDPI upscale (default)`],[`device`,`device — crisp retina`],[`2`,`2`]]){let n=document.createElement(`option`);n.value=e,n.textContent=t,String(i.pixelRatio??``)===e&&(n.selected=!0),s.appendChild(n)}s.addEventListener(`change`,()=>{a({pixelRatio:s.value===``?void 0:s.value===`device`?`device`:Number(s.value)})}),e.appendChild(vj(`pixel ratio`,s))}function VA(e,t,n){let r=document.createElement(`div`);r.className=`color-row`;let i=document.createElement(`input`);i.type=`color`,i.value=/^#[0-9a-fA-F]{6}$/.test(t)?t:`#000000`;let a=document.createElement(`input`);return a.type=`text`,a.className=`mono`,a.placeholder=`unset`,a.value=t,i.addEventListener(`input`,()=>{a.value=i.value}),i.addEventListener(`change`,()=>n(i.value)),a.addEventListener(`change`,()=>n(a.value.trim())),r.append(i,a),vj(e,r)}function HA(e,t){let n=document.createElement(`div`);n.className=`subcard`;let r=document.createElement(`div`);r.className=`subcard-head`,r.textContent=`underwater`,n.appendChild(r);let i=t.props?.underwater,a=i!==!1,o=i&&typeof i==`object`&&!Array.isArray(i)?i:{},s=n=>{e.mutate(()=>{t.props||={},n===!0?delete t.props.underwater:t.props.underwater=n,Object.keys(t.props).length===0&&delete t.props})},c=(e,t)=>{let n={...o};t===void 0||t===``?delete n[e]:n[e]=t,s(Object.keys(n).length===0?!0:n)},l=document.createElement(`input`);if(l.type=`checkbox`,l.checked=a,l.addEventListener(`change`,()=>s(l.checked)),n.appendChild(vj(`enabled`,l)),a){n.appendChild(VA(`murk color`,String(o.color??``),e=>c(`color`,e))),n.appendChild(xj(`visibility (m)`,Number(o.visibility??22),e=>c(`visibility`,e===22?void 0:e),()=>c(`visibility`,void 0)));let e=document.createElement(`input`);e.type=`checkbox`,e.checked=(o.caustics??!0)!==!1,e.addEventListener(`change`,()=>c(`caustics`,e.checked?void 0:!1)),n.appendChild(vj(`caustics`,e))}return n}function UA(e,t){let n=document.createElement(`div`);n.className=`subcard`;let r=document.createElement(`div`);r.className=`subcard-head`,r.textContent=`material`,n.appendChild(r);let i=t.props?.material??{},a=(n,r)=>{let i={...t.props?.material??{}};r===void 0||r===``?delete i[n]:i[n]=r;let a=Object.keys(i).length===0?void 0:i;e.mutate(()=>{t.props||={},a===void 0?(delete t.props.material,Object.keys(t.props).length===0&&delete t.props):t.props.material=a},e.selection?{path:e.selection,key:`material`,value:a}:void 0)};n.appendChild(VA(`color`,String(i.color??``),e=>a(`color`,e))),n.appendChild(WA(`roughness`,Number(i.roughness??1),e=>a(`roughness`,e))),n.appendChild(WA(`metalness`,Number(i.metalness??0),e=>a(`metalness`,e))),n.appendChild(VA(`emissive`,String(i.emissive??``),e=>a(`emissive`,e))),n.appendChild(WA(`emissive power`,Number(i.emissiveIntensity??1),e=>a(`emissiveIntensity`,e===1?void 0:e),2)),n.appendChild(WA(`opacity`,Number(i.opacity??1),e=>a(`opacity`,e===1?void 0:e)));let o=document.createElement(`input`);o.type=`checkbox`,o.checked=i.wireframe===!0,o.addEventListener(`change`,()=>a(`wireframe`,o.checked||void 0)),n.appendChild(vj(`wireframe`,o));let s=document.createElement(`input`);s.type=`checkbox`,s.checked=i.flatShading===!0,s.addEventListener(`change`,()=>a(`flatShading`,s.checked||void 0)),n.appendChild(vj(`flatShading`,s)),n.appendChild(tj(`map (texture)`,String(i.map??``),nj(e=>IA(e)&&!e.name.includes(`normal`)&&!e.name.includes(`roughness`)),e=>a(`map`,e.trim()),{placeholder:`built-in or custom URL`})),n.appendChild(tj(`normal map`,String(i.normalMap??``),nj(e=>IA(e)&&e.name.includes(`normal`)),e=>a(`normalMap`,e.trim()),{placeholder:`built-in or custom URL`}));let c=!!(i.map||i.normalMap),l=Array.isArray(i.repeat)?i.repeat:[1,1],u=Sj(`repeat [u,v]`,[l[0]??1,l[1]??1],e=>{let[t,n]=e;!Number.isFinite(t)||!Number.isFinite(n)||t===1&&n===1||!t&&!n?a(`repeat`,void 0):a(`repeat`,[t,n])});return u.title=c?`Texture tiling across the mesh UVs.`:`Only takes effect with a map/normal map set (engine validates).`,n.appendChild(u),n}function WA(e,t,n,r=1){let i=document.createElement(`div`);i.className=`slider-row`;let a=document.createElement(`input`);a.type=`range`,a.min=`0`,a.max=String(r),a.step=`0.05`,a.value=String(Number.isFinite(t)?t:0);let o=document.createElement(`input`);return o.type=`number`,o.step=`0.05`,o.min=`0`,o.max=String(r),o.value=a.value,a.addEventListener(`input`,()=>{o.value=a.value}),a.addEventListener(`change`,()=>n(Number(a.value))),o.addEventListener(`change`,()=>{a.value=o.value,n(Number(o.value))}),i.append(a,o),vj(e,i)}function GA(e,t,n,r){if(e.appendChild(gj(n.type??`instance`)),e.appendChild(KA(n)),e.appendChild(bj(`name`,n.name??``,e=>{e.trim()!==``&&t.mutate(()=>{n.name=e.trim()})})),n.type){let i;try{i=fe(n.type)}catch{i={}}for(let[a,o]of Object.entries(i))if(a===`collider`)e.appendChild(XA(t,n,o.default));else if(a===`material`)e.appendChild(UA(t,n));else if(n.type===`ModelInstance3D`&&a===`model`&&r)e.appendChild($A(`model`,String(n.props?.model??``),r.modelRefs(),e=>ZA(t,n,`model`,e,``)));else if(n.type===`ModelInstance3D`&&a===`animation`&&r)e.appendChild($A(`animation`,String(n.props?.animation??``),r.animationsForSelection(),e=>ZA(t,n,`animation`,e,``)));else if(n.type===`Tree3D`&&a===`leafTexture`)e.appendChild(tj(`leaf texture`,String(n.props?.leafTexture??``),nj(e=>e.name.startsWith(`leaves_`)),e=>ZA(t,n,`leafTexture`,e.trim(),``),{placeholder:`default (per-type) or URL`}));else if(n.type===`Terrain3D`&&a===`textureBase`)e.appendChild(tj(`texture base`,String(n.props?.textureBase??o.default??``),ij,e=>ZA(t,n,`textureBase`,e.trim(),o.default),{placeholder:`splat texture base URL`}));else if(n.type===`AnimatedSprite2D`&&a===`sheet`||n.type===`Sprite2D`&&a===`texture`){let i=a===`sheet`;e.appendChild(aj(t,n,a,r,i))}else n.type===`AudioPlayer`&&a===`src`?e.appendChild(tj(`src (audio file)`,String(n.props?.src??``),rj(),e=>ZA(t,n,`src`,e.trim(),``),{placeholder:`built-in or custom URL — or use preset`,note:"For zero-asset SFX, set the `preset` prop instead of src."})):n.type===`Flowers3D`&&a===`varieties`?e.appendChild(oj(t,n)):n.type===`Water3D`&&a===`underwater`?e.appendChild(HA(t,n)):e.appendChild(fj(t,n,a,o.default,o.options))}e.appendChild(gj(`structure`)),e.appendChild(qA(t,n)),e.appendChild(JA(t,n)),e.appendChild(YA(t,n))}function KA(e){let t=document.createElement(`div`);t.className=`field uid-line`;let n=document.createElement(`span`);n.textContent=`uid`;let r=document.createElement(`div`);r.className=`uid-value`;let i=document.createElement(`code`);i.textContent=String(e.uid??``);let a=document.createElement(`button`);return a.type=`button`,a.className=`uid-copy`,a.title=`Copy uid`,a.innerHTML=`<svg aria-hidden="true" width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>`,a.addEventListener(`click`,()=>{navigator.clipboard?.writeText(String(e.uid??``)),a.classList.add(`copied`),a.innerHTML=`✓`,setTimeout(()=>{a.classList.remove(`copied`),a.innerHTML=`<svg aria-hidden="true" width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>`},800)}),r.append(i,a),t.append(n,r),t}function qA(e,t){let n=document.createElement(`label`);n.className=`field wide`;let r=document.createElement(`span`);r.append(`groups `,PA(`groups`));let i=document.createElement(`div`);i.className=`chips`;for(let n of t.groups??[])i.appendChild(_j(n,()=>{e.mutate(()=>{t.groups=(t.groups??[]).filter(e=>e!==n),t.groups.length===0&&delete t.groups})}));let a=document.createElement(`input`);return a.placeholder=(t.groups?.length??0)===0?`add a tag… (e.g. coins)`:``,a.addEventListener(`keydown`,n=>{if(n.key!==`Enter`)return;let r=a.value.trim();r&&e.mutate(()=>{t.groups=[...t.groups??[],r]})}),i.appendChild(a),i.addEventListener(`click`,()=>a.focus()),n.append(r,i),n}function JA(e,t){let n=document.createElement(`div`);n.className=`subcard`;let r=document.createElement(`div`);r.className=`subcard-head`,r.append(`script `,PA(`script`));let i=document.createElement(`span`);i.className=`spacer`,r.appendChild(i);let a=t.script;if(a){let n=document.createElement(`button`);n.type=`button`,n.className=`linklike`,n.textContent=`detach`,n.addEventListener(`click`,()=>{e.mutate(()=>{delete t.script})}),r.appendChild(n)}if(n.appendChild(r),!a){let r=document.createElement(`div`);r.className=`muted-note`,r.textContent=`No behavior attached. Behaviors are TypeScript classes in your game.`;let i=document.createElement(`button`);return i.type=`button`,i.className=`ghost`,i.textContent=`+ attach behavior`,i.addEventListener(`click`,()=>{e.mutate(()=>{t.script={name:`MyBehavior`}})}),n.append(r,i),n}n.appendChild(bj(`name`,a.name??``,n=>{e.mutate(()=>{t.script.name=n.trim()})})),n.appendChild(Cj(`props`,a.props,n=>{e.mutate(()=>{n===void 0?delete t.script.props:t.script.props=n})}));let o=document.createElement(`div`);return o.className=`muted-note`,o.textContent=`? has copy-paste boilerplate for the game side.`,n.appendChild(o),n}function YA(e,t){let n=document.createElement(`div`);n.className=`subcard`;let r=document.createElement(`div`);r.className=`subcard-head`,r.append(`network `,PA(`network`)),n.appendChild(r);let i=t.network,a=document.createElement(`select`);for(let[e,t]of[[``,`not replicated`],[`owner`,`owner — this player broadcasts it`]]){let n=document.createElement(`option`);n.value=e,n.textContent=t,(i?.mode??``)===e&&(n.selected=!0),a.appendChild(n)}if(a.addEventListener(`change`,()=>{e.mutate(()=>{a.value===``?delete t.network:t.network={mode:a.value,sync:i?.sync??[`position`]}})}),n.appendChild(vj(`mode`,a)),i?.mode===`owner`){let r=document.createElement(`div`);r.className=`chips`;for(let n of i.sync??[])r.appendChild(_j(n,()=>{e.mutate(()=>{t.network.sync=(i.sync??[]).filter(e=>e!==n)})}));let a=document.createElement(`input`);a.placeholder=`position · Skin.animation…`,a.addEventListener(`keydown`,n=>{if(n.key!==`Enter`)return;let r=a.value.trim();r&&e.mutate(()=>{t.network.sync=[...i.sync??[],r]})}),r.appendChild(a);let o=document.createElement(`label`);o.className=`field wide`;let s=document.createElement(`span`);s.textContent=`sync keys`,o.append(s,r),n.appendChild(o),n.appendChild(xj(`throttle ms`,i.throttleMs??50,n=>{e.mutate(()=>{t.network.throttleMs=n})}))}return n}function XA(e,t,n){let r=document.createElement(`div`);r.className=`subcard`;let i=document.createElement(`div`);i.className=`subcard-head`,i.append(`collider `,PA(`collider`)),r.appendChild(i);let a=t.props?.collider??{},o=n=>{e.mutate(()=>{t.props||={};let e=t.props;Object.keys(n).length===0?(delete e.collider,Object.keys(e).length===0&&delete t.props):e.collider=n})},s=(t.type??``).endsWith(`3D`),c=s?[[``,`none`],[`box`,`box — crates, floors`],[`sphere`,`sphere — balls, pickups`],[`capsule`,`capsule — characters`]]:[[``,`none`],[`rect`,`rect — boxes, platforms`],[`circle`,`circle — coins, balls`],[`capsule`,`capsule — characters`]],l=document.createElement(`select`);for(let[e,t]of c){let n=document.createElement(`option`);n.value=e,n.textContent=t,(a.shape??``)===e&&(n.selected=!0),l.appendChild(n)}return l.addEventListener(`change`,()=>{l.value===``?o({}):l.value===`rect`?o({shape:`rect`,size:a.size??[32,32]}):l.value===`box`?o({shape:`box`,size:a.size??[1,1,1]}):l.value===`circle`?o({shape:`circle`,radius:a.radius??16}):l.value===`sphere`?o({shape:`sphere`,radius:a.radius??.5}):o(s?{shape:`capsule`,radius:a.radius??.4,height:a.height??1}:{shape:`capsule`,radius:a.radius??12,height:a.height??16})}),r.appendChild(vj(`shape`,l)),a.shape===`rect`||a.shape===`box`?r.appendChild(Sj(`size`,a.size??(s?[1,1,1]:[32,32]),e=>o({...a,size:e}))):a.shape===`circle`||a.shape===`sphere`?r.appendChild(xj(`radius`,a.radius??(s?.5:16),e=>o({...a,radius:e}))):a.shape===`capsule`&&(r.appendChild(xj(`radius`,a.radius??12,e=>o({...a,radius:e}))),r.appendChild(xj(`height`,a.height??16,e=>o({...a,height:e})))),a.shape&&r.appendChild(Sj(`offset`,a.offset??(s?[0,0,0]:[0,0]),e=>o({...a,offset:e}))),r}function ZA(e,t,n,r,i){e.mutate(()=>{t.props||={},JSON.stringify(r)===JSON.stringify(i)?(delete t.props[n],Object.keys(t.props).length===0&&delete t.props):t.props[n]=r})}var QA=0;function $A(e,t,n,r){let i=document.createElement(`input`);i.type=`text`,i.value=t;let a=`suggest-${QA++}`;i.setAttribute(`list`,a);let o=document.createElement(`datalist`);o.id=a;for(let e of n){let t=document.createElement(`option`);t.value=e,o.appendChild(t)}i.addEventListener(`change`,()=>r(i.value.trim()));let s=vj(e,i);return s.appendChild(o),s}var ej=0;function tj(e,t,n,r,i={}){let a=document.createElement(`label`);a.className=`field wide asset-field`;let o=document.createElement(`span`);o.textContent=e,a.appendChild(o);let s=document.createElement(`input`);s.type=`text`,s.value=t,i.placeholder&&(s.placeholder=i.placeholder),s.addEventListener(`change`,()=>r(s.value.trim()));let c=document.createElement(`button`);c.type=`button`,c.className=`asset-toggle`,c.title=`Browse built-in assets`,c.textContent=`▾`;let l=document.createElement(`div`);l.className=`asset-control`,l.append(s,c),a.appendChild(l);let u=document.createElement(`div`);u.className=`asset-panel`,u.hidden=!0;let d=document.createElement(`input`);d.type=`text`,d.className=`asset-search`,d.placeholder=`search ${n.length} built-in${n.length===1?``:`s`}…`;let f=document.createElement(`div`);f.className=`asset-options`,u.append(d,f),a.appendChild(u);let p=e=>{f.textContent=``;let t=e.trim().toLowerCase(),i=n.filter(e=>t===``||e.label.toLowerCase().includes(t)||(e.hint?.toLowerCase().includes(t)??!1)||e.value.toLowerCase().includes(t));if(i.length===0){let e=document.createElement(`div`);e.className=`asset-empty`,e.textContent=`no match — type a custom URL above`,f.appendChild(e);return}for(let e of i){let t=document.createElement(`button`);t.type=`button`,t.className=`asset-option`;let n=document.createElement(`strong`);if(n.textContent=e.label,t.appendChild(n),e.hint){let n=document.createElement(`small`);n.textContent=e.hint,t.appendChild(n)}t.addEventListener(`click`,()=>{s.value=e.value,u.hidden=!0,r(e.value)}),f.appendChild(t)}};if(c.addEventListener(`click`,()=>{u.hidden=!u.hidden,u.hidden||(p(d.value),d.focus())}),d.addEventListener(`input`,()=>p(d.value)),d.addEventListener(`keydown`,e=>{e.key===`Escape`&&(u.hidden=!0)}),i.note){let e=document.createElement(`div`);e.className=`muted-note asset-note`,e.textContent=i.note,a.appendChild(e)}return s.id=`asset-input-${ej++}`,a.htmlFor=s.id,a}function nj(e){return FA.filter(e).map(e=>({label:e.name,value:e.url,hint:`${e.kind} · ${e.description.slice(0,70)}`}))}function rj(){return FA.filter(e=>e.kind===`audio`).map(e=>({label:e.name,value:e.url,hint:`audio · ${e.description.slice(0,70)}`}))}var ij=[{label:`agent8 default terrain`,value:`https://agent8-games.verse8.io/assets/3D/default/textures/terrain`,hint:`sand/grass/stone/snow splat set (Terrain3D default)`}];function aj(e,t,n,r,i){let a=String(t.props?.[n]??``),o=i?[`spritesheet`]:[`texture`,`spritesheet`],s=(r?.assetRefs?.(o)??[]).map(e=>({label:e,value:e,hint:`scene asset`})),c=FA.filter(e=>i?!!e.animation:e.kind===`character`||e.kind===`item`||e.kind===`tile`);for(let e of c)s.push({label:`built-in: ${e.name}`,value:`$${e.name}`,hint:`${e.kind} · creates a scene asset (${e.animation?`spritesheet`:`texture`})`});return tj(i?`sheet`:`texture`,a,s,i=>{let a=i.startsWith(`$`)&&c.find(e=>`$${e.name}`===i);if(a&&r?.addAsset){let i={type:a.animation?`spritesheet`:`texture`,url:a.url};if(a.animation){let e=a.frameWidth,t=a.frameHeight;e&&(i.frameWidth=e),t&&(i.frameHeight=t)}ZA(e,t,n,`$${r.addAsset(a.name,i).replace(/^\$/,``)}`,``);return}ZA(e,t,n,i.trim(),``)},{placeholder:`$assetKey`,note:"Picks a $asset ref. Built-ins create the scene asset; packaged sprites need `incanto-assets copy` (or a bundler import) to serve at runtime."})}function oj(e,t){let n=document.createElement(`label`);n.className=`field wide`;let r=document.createElement(`span`);r.textContent=`varieties`,n.appendChild(r);let i=new Set(Array.isArray(t.props?.varieties)?t.props.varieties:[]),a=document.createElement(`div`);a.className=`chips varieties`;let o=()=>{let n=qb.filter(e=>i.has(e));ZA(e,t,`varieties`,n.length===0||n.length===qb.length?[]:n,[])};for(let e of qb){let t=document.createElement(`label`);t.className=`variety-opt`;let n=document.createElement(`input`);n.type=`checkbox`,n.checked=i.size===0||i.has(e),n.addEventListener(`change`,()=>{if(i.size===0)for(let e of qb)i.add(e);n.checked?i.add(e):i.delete(e),o()});let r=document.createElement(`span`);r.textContent=e,t.append(n,r),a.appendChild(t)}n.appendChild(a);let s=document.createElement(`div`);return s.className=`muted-note`,s.textContent=`All (or none) = the default mix of all three.`,n.appendChild(s),n}var sj=[[`number`,()=>0],[`text`,()=>``],[`boolean`,()=>!1],[`color`,()=>`#ffffff`],[`vec2`,()=>[0,0]],[`vec3`,()=>[0,0,0]]];function cj(e,t){let n=0,r=e=>{if(Ye(e)){e[`@const`]===t&&(n+=1);return}if(Array.isArray(e))for(let t of e)r(t);else if(e&&typeof e==`object`)for(let t of Object.values(e))r(t)};for(let[t,n]of Object.entries(e))t!==`constants`&&r(n);return n}function lj(e,t,n){let r=e=>{if(Ye(e))return e[`@const`]===t?JSON.parse(JSON.stringify(n??null)):e;if(Array.isArray(e))return e.map(r);if(e&&typeof e==`object`){let t=e;for(let e of Object.keys(t))t[e]=r(t[e]);return t}return e};for(let t of Object.keys(e))t!==`constants`&&(e[t]=r(e[t]))}function uj(e,t,n){let r=t.working.constants??{},i=Object.keys(r),a=(e,n)=>{t.mutate(()=>{let r={...t.working.constants??{}};r[e]=n,t.working.constants=r})},o=(e,n)=>{t.mutate(()=>{n&&lj(t.working,e,r[e]);let i={...t.working.constants??{}};delete i[e],Object.keys(i).length===0?delete t.working.constants:t.working.constants=i})},s=e=>{let r=cj(t.working,e);if(r>0&&n?.confirm){n.confirm(`"${e}" is used by ${r} prop${r===1?``:`s`}. Unlink them (inline its current value) and delete?`,`unlink & delete`,()=>o(e,!0));return}o(e,r>0)};if(i.length===0){let t=document.createElement(`div`);t.className=`hint`,t.textContent=`No constants yet. Add one, then bind props to it with the 🔗 picker.`,e.appendChild(t)}for(let t of i){let n=document.createElement(`div`);n.className=`const-row`,n.appendChild(dj(t,r[t],e=>a(t,e)));let i=document.createElement(`button`);i.type=`button`,i.className=`const-del`,i.textContent=`✕`,i.title=`delete constant "${t}"`,i.addEventListener(`click`,()=>s(t)),n.appendChild(i),e.appendChild(n)}let c=document.createElement(`div`);c.className=`const-add`;let l=document.createElement(`input`);l.type=`text`,l.placeholder=`new constant name`;let u=document.createElement(`select`);for(let[e]of sj){let t=document.createElement(`option`);t.value=e,t.textContent=e,u.appendChild(t)}let d=document.createElement(`button`);d.type=`button`,d.textContent=`+ add`,d.addEventListener(`click`,()=>{let e=l.value.trim();e&&(t.working.constants??{})[e]===void 0&&(a(e,(sj.find(([e])=>e===u.value)?.[1]??(()=>0))()),l.value=``)}),c.append(l,u,d),e.appendChild(c)}function dj(e,t,n){let r=oe(t);if(r===`number`)return xj(e,t,e=>n(e),()=>n(0));if(r===`boolean`){let r=document.createElement(`input`);return r.type=`checkbox`,r.checked=t===!0,r.addEventListener(`change`,()=>n(r.checked)),vj(e,r)}return r===`array`&&Array.isArray(t)&&t.every(e=>typeof e==`number`)?Sj(e,t,e=>n(e)):r===`string`?bj(e,String(t),e=>n(e)):Cj(e,t,e=>n(e??null),JSON.stringify(t))}function fj(e,t,n,r,i){let a=t.props?.[n]??r,o=oe(r),s=o===`number`||o===`boolean`||o===`string`||o===`array`,c=i=>{let a=Ye(i);e.mutate(()=>{t.props||={};let e=t.props;JSON.stringify(i)===JSON.stringify(r)?(delete e[n],Object.keys(e).length===0&&delete t.props):e[n]=i},e.selection&&s&&!a?{path:e.selection,key:n,value:i}:void 0)},l=pj(e,o,r);if(Ye(a))return mj(n,a[`@const`],l,c,r);let u=e=>l.length>0?hj(e,l,c):e;if(o===`number`)return u(xj(n,a,e=>c(e),()=>c(r)));if(o===`boolean`){let e=document.createElement(`input`);return e.type=`checkbox`,e.checked=a===!0,e.addEventListener(`change`,()=>c(e.checked)),u(vj(n,e))}return o===`string`?i&&i.length>0?u(yj(n,String(a??``),i,e=>c(e))):u(bj(n,String(a??``),e=>c(e))):o===`array`&&Array.isArray(r)&&r.length>0&&r.every(e=>typeof e==`number`)?u(Sj(n,a??r,e=>c(e))):Cj(n,a===r?void 0:a,e=>{c(e===void 0?r:e)},JSON.stringify(r))}function pj(e,t,n){let r=e.working.constants??{};return Object.keys(r).filter(e=>{let i=r[e];return oe(i)===t?t===`array`&&Array.isArray(n)&&Array.isArray(i)?i.length===n.length:!0:!1})}function mj(e,t,n,r,i){let a=document.createElement(`select`),o=n.includes(t)?n:[t,...n];for(let e of o){let n=document.createElement(`option`);n.value=e,n.textContent=e,e===t&&(n.selected=!0),a.appendChild(n)}let s=document.createElement(`option`);s.value=``,s.textContent=`↺ custom value`,a.appendChild(s),a.addEventListener(`change`,()=>{r(a.value?{"@const":a.value}:i)});let c=vj(`🔗 ${e}`,a);return c.classList.add(`const-bound`),c}function hj(e,t,n){let r=document.createElement(`select`);r.className=`const-picker`,r.title=`Bind to a named constant`;let i=document.createElement(`option`);i.value=``,i.textContent=`🔗`,r.appendChild(i);for(let e of t){let t=document.createElement(`option`);t.value=e,t.textContent=e,r.appendChild(t)}return r.value=``,r.addEventListener(`change`,()=>{r.value&&n({"@const":r.value})}),e.appendChild(r),e}function gj(e,t){let n=document.createElement(`div`);n.className=`section-title`,n.textContent=e,t&&n.appendChild(PA(t));let r=document.createElement(`span`);return r.className=`rule`,n.appendChild(r),n}function _j(e,t){let n=document.createElement(`span`);n.className=`chip`,n.textContent=e;let r=document.createElement(`button`);return r.type=`button`,r.textContent=`✕`,r.addEventListener(`click`,e=>{e.stopPropagation(),t()}),n.appendChild(r),n}function vj(e,t){let n=document.createElement(`label`);n.className=`field`;let r=document.createElement(`span`);return r.textContent=e,n.append(r,t),n}function yj(e,t,n,r){let i=document.createElement(`select`);for(let e of n.includes(t)?n:[t,...n]){let n=document.createElement(`option`);n.value=e,n.textContent=e,e===t&&(n.selected=!0),i.appendChild(n)}return i.addEventListener(`change`,()=>r(i.value)),vj(e,i)}function bj(e,t,n){let r=document.createElement(`input`);return r.type=`text`,r.value=t,r.addEventListener(`change`,()=>n(r.value)),vj(e,r)}function xj(e,t,n,r){let i=document.createElement(`input`);return i.type=`number`,i.step=`any`,i.value=String(t),i.addEventListener(`change`,()=>{if(i.value.trim()===``){r?.();return}let e=Number(i.value);Number.isFinite(e)&&n(e)}),vj(e,i)}function Sj(e,t,n){let r=document.createElement(`div`);r.className=`vector-row`;let i=[...t];return i.forEach((e,t)=>{let a=document.createElement(`input`);a.type=`number`,a.step=`any`,a.value=String(e??0),a.addEventListener(`change`,()=>{let e=Number(a.value);Number.isFinite(e)&&(i[t]=e,n([...i]))}),r.appendChild(a)}),vj(e,r)}function Cj(e,t,n,r=``){let i=document.createElement(`textarea`);i.rows=3,i.placeholder=r,i.value=t===void 0?``:JSON.stringify(t,null,2),i.addEventListener(`change`,()=>{let e=i.value.trim();if(e===``){i.classList.remove(`invalid`),n(void 0);return}try{let t=JSON.parse(e);i.classList.remove(`invalid`),n(t)}catch{i.classList.add(`invalid`)}});let a=vj(e,i);return a.className=`field wide`,a}var wj=class{working;selection=[];extra=[];validator=null;selectedAsset=null;selectedGroup=null;pendingGroups=new Set;pendingLivePatch=null;addingAsset=!1;addingGroup=!1;newAssetGroup=``;newGroupParent=``;startAddingAsset(){this.addingAsset=!0,this.addingGroup=!1,this.newAssetGroup=this.selectedGroup??``,this.clearFocus()}startAddingGroup(){this.addingGroup=!0,this.addingAsset=!1,this.newGroupParent=this.selectedGroup??``,this.clearFocus()}cancelAddForms(){this.addingAsset=!1,this.addingGroup=!1}onError=null;original;undoStack=[];listeners=new Set;constructor(e){this.working=e,this.original=JSON.stringify(e),this.working.root&&Tj(this.working.root)}get dirty(){return JSON.stringify(this.working)!==this.original}get canUndo(){return this.undoStack.length>0}nodeAt(e){if(e===null)return null;let t=this.working.root;for(let n of e)t=t?.children?.[n];return t??null}parentOf(e){return e.length===0?null:this.nodeAt(e.slice(0,-1))}mutate(e,t){this.undoStack.push(JSON.stringify(this.working)),this.undoStack.length>100&&this.undoStack.shift(),e(),this.pendingLivePatch=t??null,this.emit()}undo(){let e=this.undoStack.pop();e!==void 0&&(this.working=JSON.parse(e),this.selection=[],this.emit())}transact(e){let t=JSON.stringify(this.working),n=e(),r=e=>(this.working=JSON.parse(t),e&&this.onError?.(e),null);if(n===null)return r();let i=this.validator?.(this.working)??null;return i?r(i):(this.undoStack.push(t),this.undoStack.length>100&&this.undoStack.shift(),this.emit(),n)}moveNode(e,t,n){return this.moveNodes([e],t,n)?.[0]??null}moveNodes(e,t,n){let r=Aj(e).filter(e=>e.length>0);if(r.length===0)return null;for(let e of r)if(e.length<=t.length&&e.every((e,n)=>e===t[n]))return null;let i=this.working.root,a=r.map(e=>this.nodeAt(e)).filter(e=>e!==null);if(a.length!==r.length)return null;let o=this.nodeAt(t);return!o||a.includes(o)?null:this.transact(()=>{for(let e of a)Mj(i,e);let e,t;if(n===`into`)e=o,e.children||=[],t=e.children.length;else{let r=jj(i,o);if(!r)return null;e=r.parent,t=r.index+ +(n===`after`)}let r=[];a.forEach((n,r)=>{n.name=Pj(e,n.name??`Node`),e.children?.splice(t+r,0,n)});for(let e of a){let t=Nj(i,e);if(!t)return null;r.push(t)}return r})}duplicateNode(e){if(e.length===0)return null;let t=this.nodeAt(e),n=this.parentOf(e);return!t||!n?.children?null:this.transact(()=>{let r=JSON.parse(JSON.stringify(t));r.name=Pj(n,t.name??`Node`),Ej(r);let i=e[e.length-1]+1;return n.children?.splice(i,0,r),[...e.slice(0,-1),i]})}removeNodes(e){let t=Aj(e).filter(e=>e.length>0);if(t.length===0)return!1;let n=this.working.root,r=t.map(e=>this.nodeAt(e)).filter(e=>e!==null);return this.transact(()=>{for(let e of r)Mj(n,e);return!0})===!0}deleteRoot(){this.undoStack.push(JSON.stringify(this.working)),delete this.working.root,this.selection=null,this.extra=[],this.emit()}insertNode(e,t){if(!this.working.root){let t=JSON.parse(JSON.stringify(e));return Ej(t),this.transact(()=>(this.working.root=t,[]))}let n=this.nodeAt(t);return n?this.transact(()=>{let r=JSON.parse(JSON.stringify(e));return r.name=Pj(n,r.name??`Node`),Ej(r),n.children||=[],n.children.push(r),[...t,n.children.length-1]}):null}findRemovalReferences(e){let t=Aj(e).filter(e=>e.length>0),n=this.working.root,r=t.map(e=>this.nodeAt(e)).filter(e=>e!==null),i=new Set,a=[];for(let e of t){let t=[],r=n;for(let n of e){let e=r.children?.[n];if(!e)break;t.push(String(e.name)),r=e}a.push(t.join(`/`))}let o=e=>{typeof e.uid==`string`&&i.add(e.uid);for(let t of e.children??[])o(t)};for(let e of r)o(e);let s=[];if((this.working.connections??[]).forEach((e,t)=>{for(let n of[`from`,`to`]){let r=String(e[n]??``);if(a.some(e=>r===e||r.startsWith(`${e}/`))){s.push({kind:`connection`,where:`connections[${t}] ${String(e.signal)}: ${String(e.from)} → ${String(e.to)}`,connectionIndex:t});return}}}),i.size>0){let e=(t,n,a)=>{if(typeof t==`string`){!a&&i.has(t)&&s.push({kind:`uid`,where:n,value:t});return}if(Array.isArray(t)){t.forEach((t,r)=>{e(t,`${n}[${r}]`,a)});return}if(typeof t==`object`&&t){let i=t,o=a||r.includes(i),s=typeof i.name==`string`&&(i.type||i.children||i.instance)?n?`${n} › ${i.name}`:String(i.name):n;for(let[n,r]of Object.entries(t))n!==`uid`&&e(r,n===`children`||n===`root`?s:`${s?`${s}.`:``}${n}`,o)}};e(this.working,``,!1)}return s}removeNodesUnlinking(e){let t=this.findRemovalReferences(e),n=Aj(e).filter(e=>e.length>0),r=this.working.root,i=n.map(e=>this.nodeAt(e)).filter(e=>e!==null),a=new Set(t.filter(e=>e.kind===`uid`).map(e=>e.value)),o=new Set(t.filter(e=>e.kind===`connection`).map(e=>e.connectionIndex));return this.transact(()=>{o.size>0&&(this.working.connections=(this.working.connections??[]).filter((e,t)=>!o.has(t))),a.size>0&&Dj(this.working,a,i);for(let e of i)Mj(r,e);return!0})===!0}reset(e){this.working=e,this.original=JSON.stringify(e),this.working.root&&Tj(this.working.root),this.undoStack=[],this.selection=[],this.emit()}markSaved(){this.original=JSON.stringify(this.working),this.emit()}emitChange(){this.emit()}clearFocus(){this.selection=null,this.extra=[],this.selectedAsset=null,this.selectedGroup=null,this.emit()}selectAsset(e){this.selectedAsset=e,e!==null&&(this.cancelAddForms(),this.selectedGroup=null,this.selection=null,this.extra=[]),this.emit()}selectGroup(e){this.selectedGroup=e,e!==null&&(this.cancelAddForms(),this.selectedAsset=null,this.selection=null,this.extra=[]),this.emit()}assetMap(){return this.working.assets??{}}groupCount(e){return Object.keys(this.assetMap()).filter(t=>t.startsWith(`${e}/`)).length}addGroup(e){this.pendingGroups.add(e),this.selectGroup(e)}renameGroup(e,t){if(!t||t===e)return!1;let n=this.assetMap(),r=Object.keys(n).filter(t=>t.startsWith(`${e}/`));return this.pendingGroups.delete(e)&&this.pendingGroups.add(t),this.selectedGroup=t,this.mutate(()=>{for(let i of r){let r=`${t}/${i.slice(e.length+1)}`;n[r]=n[i],delete n[i],Oj(this.working,`$${i}`,`$${r}`)}}),r.length===0&&this.emit(),!0}moveGroup(e,t){if(t===e||t.startsWith(`${e}/`))return!1;let n=e.includes(`/`)?e.slice(e.lastIndexOf(`/`)+1):e,r=t?`${t}/${n}`:n;return r===e?!1:this.renameGroup(e,r)}renameAssetKey(e,t){let n=this.assetMap();return!(e in n)||!t||t===e||t in n?!1:(this.selectedAsset=t,this.mutate(()=>{n[t]=n[e],delete n[e],Oj(this.working,`$${e}`,`$${t}`)}),!0)}deleteGroup(e){let t=this.assetMap(),n=Object.keys(t).filter(t=>t.startsWith(`${e}/`));n.length>0&&this.mutate(()=>{for(let e of n)delete t[e];Object.keys(t).length===0&&delete this.working.assets}),this.pendingGroups.delete(e),this.selectedGroup===e&&(this.selectedGroup=null),this.emit()}moveAsset(e,t){let n=this.assetMap();if(!(e in n))return!1;let r=e.includes(`/`)?e.slice(e.lastIndexOf(`/`)+1):e,i=t?`${t}/${r}`:r;if(i===e)return!1;for(;i in n;)i=`${i}2`;return this.renameAssetKey(e,i)}select(e,t){if(this.cancelAddForms(),this.selectedAsset=null,this.selectedGroup=null,t?.toggle&&e!==null){let t=e.join(`.`);this.selection!==null&&this.selection.join(`.`)===t?this.selection=this.extra.shift()??this.selection:this.extra.some(e=>e.join(`.`)===t)?this.extra=this.extra.filter(e=>e.join(`.`)!==t):this.selection===null?this.selection=e:this.extra.push(e)}else this.selection=e,this.extra=[];this.emit()}allSelections(){return[...this.selection===null?[]:[this.selection,...this.extra]].sort(kj)}isSelected(e){let t=e.join(`.`);return this.selection!==null&&this.selection.join(`.`)===t||this.extra.some(e=>e.join(`.`)===t)}onChange(e){this.listeners.add(e)}emit(){for(let e of this.listeners)e()}};function Tj(e){(typeof e.uid!=`string`||e.uid===``)&&(e.uid=vt());for(let t of e.children??[])Tj(t)}function Ej(e){e.uid=vt();for(let t of e.children??[])Ej(t)}function Dj(e,t,n){if(Array.isArray(e)){for(let r=e.length-1;r>=0;r--)typeof e[r]==`string`&&t.has(e[r])?e.splice(r,1):Dj(e[r],t,n);return}if(typeof e==`object`&&e){if(n.includes(e))return;for(let[r,i]of Object.entries(e))r!==`uid`&&(typeof i==`string`&&t.has(i)?delete e[r]:Dj(i,t,n))}}function Oj(e,t,n){if(Array.isArray(e)){e.forEach((r,i)=>{r===t?e[i]=n:Oj(r,t,n)});return}if(typeof e==`object`&&e)for(let[r,i]of Object.entries(e))i===t?e[r]=n:Oj(i,t,n)}function kj(e,t){for(let n=0;n<Math.min(e.length,t.length);n++)if(e[n]!==t[n])return e[n]-t[n];return e.length-t.length}function Aj(e){let t=[...e].sort(kj),n=[];for(let e of t)n.some(t=>t.length<=e.length&&t.every((t,n)=>t===e[n]))||n.push(e);return n}function jj(e,t){let n=e.children?.indexOf(t)??-1;if(n>=0)return{parent:e,index:n};for(let n of e.children??[]){let e=jj(n,t);if(e)return e}return null}function Mj(e,t){let n=jj(e,t);n?.parent.children?.splice(n.index,1)}function Nj(e,t){if(e===t)return[];for(let[n,r]of(e.children??[]).entries()){let e=Nj(r,t);if(e)return[n,...e]}return null}function Pj(e,t){let n=new Set((e.children??[]).map(e=>e.name));if(!n.has(t))return t;let r=2;for(;n.has(`${t}${r}`);)r+=1;return`${t}${r}`}var Fj=new Map(LO.flatMap(e=>e.nodes.map(e=>[e.type,e.summary]))),Ij=new Set,Lj=null;function Rj(e){Lj=e}var zj=null,Bj=null,Vj=null;function Hj(e){return e?e.includes(`Body`)||e.includes(`Area`)||e.includes(`Controller`)?`cat-body`:e===`NetworkSpawner`?`cat-net`:e.endsWith(`3D`)?`cat-3d`:e.endsWith(`2D`)||e===`Label`||e===`UILayer`?`cat-2d`:`cat-core`:`cat-core`}function Uj(e,t){e.textContent=``;let n=document.createElement(`div`);n.className=`tree-row scene-row${t.selection===null&&t.selectedAsset===null&&t.selectedGroup===null&&!t.addingAsset&&!t.addingGroup?` selected`:``}`,n.textContent=`⚙ ${String(t.working.name??`scene`)}`,n.addEventListener(`click`,()=>t.select(null)),e.appendChild(n);let r=t.working.root;if(!r){let t=document.createElement(`div`);t.className=`muted-note explorer-empty`,t.textContent=`no root node — pick a type below and + to start the scene`,e.appendChild(t);return}Wj(r,t.selection),e.appendChild(Gj(r,[],t,``))}function Wj(e,t){if(!t)return;let n=e,r=``;for(let e of t){r=r?`${r}/${n.name}`:String(n.name),Ij.delete(r);let t=n.children?.[e];if(!t)return;n=t}}function Gj(e,t,n,r){let i=r?`${r}/${e.name}`:String(e.name),a=(e.children?.length??0)>0,o=Ij.has(i),s=document.createElement(`div`);s.className=`tree-branch`;let c=document.createElement(`div`);c.className=`tree-row${n.isSelected(t)?` selected`:``}`,c.draggable=t.length>0&&Vj!==i;let l=document.createElement(`span`);l.className=`chevron${a?``:` leaf`}${o?` collapsed`:``}`,a?l.appendChild(rO()):l.textContent=`·`,a&&l.addEventListener(`click`,e=>{e.stopPropagation(),o?Ij.delete(i):Ij.add(i),n.select(n.selection)});let u=document.createElement(`span`);if(u.className=`tree-icon ${Hj(e.type)}`,u.appendChild(nO(e.type)),u.title=e.type??`instance`,u.addEventListener(`click`,t=>{t.stopPropagation(),Kj(u,e.type??`instance`)}),c.append(l,u),Vj===i){let t=document.createElement(`input`);t.className=`rename-input`,t.value=e.name??``;let r=()=>{Vj=null;let r=t.value.trim();r&&r!==e.name?n.mutate(()=>{e.name=r}):n.select(n.selection)};t.addEventListener(`keydown`,e=>{e.stopPropagation(),e.key===`Enter`&&r(),e.key===`Escape`&&(Vj=null,n.select(n.selection))});for(let e of[`click`,`pointerdown`,`dblclick`,`mousedown`])t.addEventListener(e,e=>e.stopPropagation());t.addEventListener(`blur`,r),c.appendChild(t),queueMicrotask(()=>{t.focus(),t.select()})}else{let r=document.createElement(`span`);r.className=`tree-name`,r.textContent=e.name??`(unnamed)`,r.addEventListener(`dblclick`,e=>{e.stopPropagation(),Vj=i,n.select(t)}),c.appendChild(r)}if(c.addEventListener(`click`,e=>{a&&!e.metaKey&&!e.ctrlKey&&(o?Ij.delete(i):Ij.add(i)),n.select(t,{toggle:e.metaKey||e.ctrlKey})}),c.addEventListener(`contextmenu`,e=>{e.preventDefault(),n.isSelected(t)||n.select(t),qj(e.clientX,e.clientY,n,t,i)}),nM(c,t,n),s.appendChild(c),a&&!o){let r=document.createElement(`div`);r.className=`tree-children`,(e.children??[]).forEach((e,a)=>{r.appendChild(Gj(e,[...t,a],n,i))}),s.appendChild(r)}return s}function Kj(e,t){Qj();let n=document.createElement(`div`);n.className=`balloon floating`;let r=document.createElement(`span`);r.className=`balloon-title`,r.textContent=t,n.appendChild(r);let i=Fj.get(t);if(i){let e=document.createElement(`span`);e.textContent=$D(i),n.appendChild(e)}document.body.appendChild(n);let a=e.getBoundingClientRect();n.style.left=`${Math.min(innerWidth-240,a.right+8)}px`,n.style.top=`${Math.max(8,a.top-6)}px`,$j(n)}function qj(e,t,n,r,i){Qj();let a=n.allSelections(),o=a.length===1&&r.length>0,s=document.createElement(`div`);s.className=`context-menu floating`;let c=(e,t,n)=>{let r=document.createElement(`button`);r.type=`button`,r.className=`menu-item${n?.danger?` danger`:``}`,r.disabled=t===null;let i=document.createElement(`span`);if(i.textContent=e,r.appendChild(i),n?.kbd){let e=document.createElement(`kbd`);e.textContent=n.kbd,r.appendChild(e)}t&&r.addEventListener(`click`,()=>{Qj(),t()}),s.appendChild(r)};c(`duplicate${a.length>1?` ×${a.length}`:``}`,()=>{for(let e of[...a].reverse())n.duplicateNode(e)}),c(`rename`,o?()=>{Vj=i,n.select(r)}:null,{kbd:`dbl-click`}),s.appendChild(Jj()),c(`cut`,r.length>0?()=>Yj(n,!0):null),c(`copy`,r.length>0?()=>Yj(n,!1):null),c(`paste as child${Bj?` (${Bj.nodes.length})`:``}`,Bj?()=>Xj(n,r):null),s.appendChild(Jj()),c(`delete`,r.length>0?()=>(Lj??(e=>n.removeNodes(e)))(a):null,{danger:!0}),document.body.appendChild(s),s.style.left=`${Math.min(innerWidth-200-8,e)}px`,s.style.top=`${Math.min(innerHeight-s.offsetHeight-8,t)}px`,$j(s)}function Jj(){let e=document.createElement(`div`);return e.className=`menu-divider`,e}function Yj(e,t){let n=e.allSelections().filter(e=>e.length>0).map(t=>e.nodeAt(t)).filter(e=>e!==null);Bj={nodes:n.map(e=>JSON.parse(JSON.stringify(e))),cut:t?n:null}}function Xj(e,t){if(Bj){for(let n of Bj.nodes)e.insertNode(n,t);if(Bj.cut){let t=e.working.root,n=Bj.cut.map(e=>Zj(t,e)).filter(e=>e!==null);n.length>0&&e.removeNodes(n),Bj={nodes:Bj.nodes,cut:null}}}}function Zj(e,t){if(e===t)return[];for(let[n,r]of(e.children??[]).entries()){let e=Zj(r,t);if(e)return[n,...e]}return null}function Qj(){for(let e of document.querySelectorAll(`.floating`))e.remove()}function $j(e){let t=r=>{e.contains(r.target)||(e.remove(),document.removeEventListener(`pointerdown`,t,!0),document.removeEventListener(`keydown`,n,!0))},n=r=>{r.key===`Escape`&&(e.remove(),document.removeEventListener(`pointerdown`,t,!0),document.removeEventListener(`keydown`,n,!0))};setTimeout(()=>{document.addEventListener(`pointerdown`,t,!0),document.addEventListener(`keydown`,n,!0)},0)}function eM(e,t){let n=t.getBoundingClientRect(),r=(e.clientY-n.top)/n.height;return r<.25?`before`:r>.75?`after`:`into`}function tM(e){e.classList.remove(`drop-into`,`drop-before`,`drop-after`)}function nM(e,t,n){e.addEventListener(`dragstart`,r=>{zj=n.isSelected(t)?n.allSelections():[t],r.dataTransfer?.setData(`text/plain`,``),r.dataTransfer&&(r.dataTransfer.effectAllowed=`move`),e.classList.add(`dragging`)}),e.addEventListener(`dragend`,()=>{zj=null,e.classList.remove(`dragging`)}),e.addEventListener(`dragover`,n=>{if(!zj||zj.some(e=>e.length<=t.length&&e.every((e,n)=>e===t[n])))return;n.preventDefault(),n.dataTransfer&&(n.dataTransfer.dropEffect=`move`),tM(e);let r=t.length===0?`into`:eM(n,e);e.classList.add(`drop-${r}`)}),e.addEventListener(`dragleave`,()=>tM(e)),e.addEventListener(`drop`,r=>{if(tM(e),!zj)return;r.preventDefault();let i=t.length===0?`into`:eM(r,e),a=n.moveNodes(zj,t,i);zj=null,a?.[0]&&n.select(a[0])})}var rM={x:`#fb7185`,y:`#86efac`,z:`#7aa2ff`};function iM(e,t,n){let r=t-e.origin.x,i=n-e.origin.y;if(Math.abs(r)<=e.centerSize&&Math.abs(i)<=e.centerSize)return{kind:`center`};for(let t of e.axes){let e=t.sx*t.sx+t.sy*t.sy;if(e<1)continue;let n=Math.max(0,Math.min(1,(r*t.sx+i*t.sy)/e)),a=r-t.sx*n,o=i-t.sy*n;if(n>.25&&Math.hypot(a,o)<=8)return{kind:`axis`,axis:t.axis}}return null}function aM(e,t,n){let r=e.sx*e.sx+e.sy*e.sy;return r<1?0:(t*e.sx+n*e.sy)/r*e.worldPerUnit}function oM(e,t,n,r=!1){let{origin:i,axes:a,centerSize:o}=t;for(let t of a){let a=n?.kind===`axis`&&n.axis===t.axis;e.strokeStyle=t.color,e.fillStyle=t.color,e.globalAlpha=a?1:.9,e.lineWidth=a?3:2,e.beginPath(),e.moveTo(i.x,i.y),e.lineTo(i.x+t.sx,i.y+t.sy),e.stroke();let o=Math.hypot(t.sx,t.sy)||1,s=t.sx/o,c=t.sy/o,l=i.x+t.sx,u=i.y+t.sy;e.beginPath(),r?e.rect(l-5,u-5,10,10):(e.moveTo(l+s*9,u+c*9),e.lineTo(l-c*4.5,u+s*4.5),e.lineTo(l+c*4.5,u-s*4.5),e.closePath()),e.fill(),e.font=`700 9px Inter, system-ui, sans-serif`,e.textAlign=`center`,e.textBaseline=`middle`,e.fillText(t.axis.toUpperCase(),l+s*18,u+c*18)}let s=n?.kind===`center`;e.globalAlpha=s?1:.95,e.fillStyle=s?`#ffffff`:`#e2e6f0`,e.strokeStyle=`#0b0d14`,e.lineWidth=1.5,e.beginPath(),e.rect(i.x-o,i.y-o,o*2,o*2),e.fill(),e.stroke(),e.globalAlpha=1}function sM(e,t,n){return Math.atan2(n-e.y,t-e.x)*180/Math.PI}function cM(e,t){let n=t-e;for(;n>180;)n-=360;for(;n<=-180;)n+=360;return n}function lM(e,t,n,r){let i=null,a=r;for(let r of e)for(let e of r.points){let o=Math.hypot(t-e.x,n-e.y);o<a&&(a=o,i=r.axis)}return i}function uM(e,t,n){for(let r of t){if(r.points.length<2)continue;let t=n===r.axis;e.strokeStyle=rM[r.axis],e.globalAlpha=t?1:.75,e.lineWidth=t?3:2,e.beginPath();let i=r.points[0];e.moveTo(i.x,i.y);for(let t of r.points.slice(1))e.lineTo(t.x,t.y);e.closePath(),e.stroke()}e.globalAlpha=1}function dM(e,t,n,r){e.font=`600 11px ui-monospace, Menlo, monospace`;let i=e.measureText(r).width+14;e.fillStyle=`rgba(16, 19, 29, 0.92)`,e.strokeStyle=`rgba(110, 231, 220, 0.5)`,e.lineWidth=1,e.beginPath(),e.roundRect(t+14,n-26,i,20,5),e.fill(),e.stroke(),e.fillStyle=`#6ee7dc`,e.textAlign=`left`,e.textBaseline=`middle`,e.fillText(r,t+21,n-16)}var fM=class{engine;editCanvas;overlay;playCanvas;cb;renderer2d=null;renderer3d=null;rendererDim=null;orbit={yaw:.6,pitch:.35,dist:10,target:[0,1,0]};pathOf=new Map;nodeAtPath=new Map;selectedPath=null;extraPaths=[];hoveredNode=null;hoveredGizmo=null;lastGood=null;lastAppliedKey=null;playEngine=null;playRenderer=null;get playing(){return this.playEngine!==null}constructor(e,t,n,r){this.editCanvas=e,this.overlay=t,this.playCanvas=n,this.cb=r;let i=this;this.engine=new Ee({scheduler:e=>{let t=0,n=performance.now(),r=requestAnimationFrame(function a(o){t+=.001;let s=Math.min(.1,(o-n)/1e3);n=o;try{e(t),i.tickAmbientPreviews(s)}catch(e){console.error(`incanto-editor viewport:`,e)}r=requestAnimationFrame(a)});return()=>cancelAnimationFrame(r)}}),this.engine.start(),this.engine.updated.connect(()=>this.drawOverlay()),window.addEventListener(`keydown`,e=>{e.key===`Shift`&&(this.shiftHeld=!0)}),window.addEventListener(`keyup`,e=>{e.key===`Shift`&&(this.shiftHeld=!1)}),this.bindPointer()}apply(e){e.root||(e={...e,root:{name:`__empty`,type:(e.dimension??`2d`)===`3d`?`Node3D`:`Node2D`}});let t=JSON.stringify(e);if(t===this.lastAppliedKey&&this.rendererDim===(e.dimension??`2d`))return null;let n;try{let t=structuredClone(e);delete t.connections,DM(t.root),AM(t),n=at(t)}catch(e){return e instanceof Error?e.message:String(e)}this.lastGood=e,this.lastAppliedKey=t;let r=e.dimension??`2d`;if(this.rendererDim!==r){if(this.renderer2d?.dispose(),this.renderer3d?.dispose(),this.renderer2d=null,this.renderer3d=null,r===`2d`)this.renderer2d=new jp({canvas:this.editCanvas,engine:this.engine}),this.renderer2d.viewOverride=this.defaultView(e);else{this.renderer3d=new _D({canvas:this.editCanvas,engine:this.engine});let t=TM(e.root);if(t){let[e,n,r]=t;this.orbit.dist=Math.max(2,Math.hypot(e,n,r)),this.orbit.yaw=Math.atan2(e,r),this.orbit.pitch=Math.asin(Math.max(-.99,Math.min(.99,n/this.orbit.dist))),this.orbit.target=[0,Math.min(2,Math.abs(n)/2),0]}this.syncOrbit()}this.rendererDim=r}return this.engine.setScene(n),this.indexTree(n.root),this.hoveredNode=null,null}setSelection(e,t=[]){this.selectedPath=e,this.extraPaths=t}validate(e){if(!e.root)return null;try{let t=structuredClone(e);return delete t.connections,DM(t.root),AM(t),at(t),null}catch(e){return e instanceof Error?e.message:String(e)}}defaultView(e){let t=EM(e.root);if(t)return{cx:t[0],cy:t[1],zoom:1};let n=this.editCanvas.clientWidth||960,r=this.editCanvas.clientHeight||540;return{cx:n/2,cy:r/2,zoom:1}}indexTree(e){this.pathOf=new Map,this.nodeAtPath=new Map;let t=(e,n)=>{this.pathOf.set(e,n),this.nodeAtPath.set(n.join(`.`),e),e.children.forEach((e,r)=>{t(e,[...n,r])})};t(e,[])}liveSelected(){return this.selectedPath===null?null:this.nodeAtPath.get(this.selectedPath.join(`.`))??null}patchProp(e,t,n,r){if(this.playing)return!1;let i=this.nodeAtPath.get(t.join(`.`));if(!i)return!1;try{i[n]=r===void 0?void 0:structuredClone(r)}catch{return!1}return this.lastAppliedKey=JSON.stringify(e),!0}gizmoActive=null;gizmoMode=`move`;activeRing=null;readout=null;cancelActiveDrag=()=>{};shiftHeld=!1;showColliders=!0;get mode(){return this.gizmoMode}setMode(e){this.gizmoMode=e,this.cb.onModeChanged(e)}gizmoLayout(){let e=this.liveSelected();if(!e)return null;if(this.rendererDim===`2d`&&this.renderer2d){let t=e.position;if(!Array.isArray(t))return null;let n=wM(e),r=this.renderer2d.screenFromWorld(n.x,n.y),i=this.renderer2d.view().zoom;return{origin:r,centerSize:7,axes:[{axis:`x`,sx:64,sy:0,worldPerUnit:64/i,color:rM.x},{axis:`y`,sx:0,sy:64,worldPerUnit:64/i,color:rM.y}]}}if(this.rendererDim===`3d`&&this.renderer3d){let t=e._object3D,n=e.position;if(!t||!Array.isArray(n))return null;t.getWorldPosition(yM);let r=this.renderer3d.screenFromWorld(yM.x,yM.y,yM.z);if(r.behind)return null;let i=[],a=this.orbit.dist,o=Math.max(.2,a*.18);for(let[e,t]of[[`x`,[1,0,0]],[`y`,[0,1,0]],[`z`,[0,0,1]]]){let n=this.renderer3d.screenFromWorld(yM.x+t[0]*o,yM.y+t[1]*o,yM.z+t[2]*o);n.behind||i.push({axis:e,sx:n.x-r.x,sy:n.y-r.y,worldPerUnit:o,color:rM[e]})}return{origin:{x:r.x,y:r.y},centerSize:7,axes:i}}return null}ringLayout(){let e=this.liveSelected();if(!e)return null;if(this.rendererDim===`2d`&&this.renderer2d){let t=wM(e),n=this.renderer2d.screenFromWorld(t.x,t.y),r=[];for(let e=0;e<48;e++){let t=e/48*Math.PI*2;r.push({x:n.x+56*Math.cos(t),y:n.y+56*Math.sin(t)})}return{origin:n,rings:[{axis:`z`,points:r}]}}if(this.rendererDim===`3d`&&this.renderer3d){let t=e._object3D;if(!t)return null;t.getWorldPosition(yM);let n={x:yM.x,y:yM.y,z:yM.z},r=this.renderer3d.screenFromWorld(n.x,n.y,n.z);if(r.behind)return null;let i=Math.max(.2,this.orbit.dist*.16),a=[];for(let e of[`x`,`y`,`z`]){let t=[];for(let r=0;r<48;r++){let a=r/48*Math.PI*2,o=Math.cos(a)*i,s=Math.sin(a)*i,c=e===`x`?this.renderer3d.screenFromWorld(n.x,n.y+o,n.z+s):e===`y`?this.renderer3d.screenFromWorld(n.x+o,n.y,n.z+s):this.renderer3d.screenFromWorld(n.x+o,n.y+s,n.z);c.behind||t.push({x:c.x,y:c.y})}a.push({axis:e,points:t})}return{origin:{x:r.x,y:r.y},rings:a}}return null}orbitVectors(){let{yaw:e,pitch:t,dist:n,target:r}=this.orbit,i=Math.cos(t),a=new H(r[0]+n*i*Math.sin(e),r[1]+n*Math.sin(t),r[2]+n*i*Math.cos(e)),o=new H(r[0],r[1],r[2]).sub(a).normalize(),s=new H().crossVectors(o,hM).normalize();return{pos:a,right:s,up:new H().crossVectors(s,o).normalize(),forward:o}}syncOrbit(){if(!this.renderer3d)return;let{pos:e}=this.orbitVectors();this.renderer3d.viewOverride={position:[e.x,e.y,e.z],target:[...this.orbit.target]}}gameView(){if(this.rendererDim===`3d`&&this.renderer3d){let e=null;for(let[t]of this.pathOf)if(t instanceof ab&&(t.current||!e)&&(e=t,t.current))break;let t=e?e._ensureObject3D():null;if(t){t.getWorldPosition(gM),_M.set(0,0,-1).applyQuaternion(t.getWorldQuaternion(vM));let e=Math.min(40,Math.max(3,this.orbit.dist));this.orbit.target=[gM.x+_M.x*e,gM.y+_M.y*e,gM.z+_M.z*e],this.orbit.dist=e,this.orbit.yaw=Math.atan2(-_M.x,-_M.z),this.orbit.pitch=Math.asin(Math.max(-.99,Math.min(.99,-_M.y)))}this.renderer3d.viewOverride=null;return}this.renderer2d&&(this.renderer2d.viewOverride=null)}snapView(e,t){e===`y`?(this.orbit.pitch=t*1.45,this.orbit.yaw=0):(this.orbit.pitch=0,this.orbit.yaw=e===`z`?t===1?0:Math.PI:Math.PI/2*t),this.syncOrbit()}gizmoCenter(){return{x:this.overlay.clientWidth-58,y:58,r:36}}gizmoHandles(){let{right:e,up:t,forward:n}=this.orbitVectors(),r=this.gizmoCenter(),i=[];for(let[a,o]of[[`x`,new H(1,0,0)],[`y`,new H(0,1,0)],[`z`,new H(0,0,1)]])for(let s of[1,-1]){let c=o.clone().multiplyScalar(s);i.push({axis:a,sign:s,x:r.x+c.dot(e)*26,y:r.y-c.dot(t)*26,z:-c.dot(n)})}return i.sort((e,t)=>e.z-t.z)}gizmoHit(e,t){let n=this.gizmoCenter();if(Math.hypot(e-n.x,t-n.y)>n.r+8)return null;let r=null,i=14;for(let n of this.gizmoHandles()){let a=Math.hypot(e-n.x,t-n.y);a<i&&(i=a,r={axis:n.axis,sign:n.sign})}return r??{axis:`z`,sign:1}}gizmoHandleAt(e,t){let n=this.gizmoCenter();if(Math.hypot(e-n.x,t-n.y)>n.r+8)return null;let r=null,i=12;for(let n of this.gizmoHandles()){let a=Math.hypot(e-n.x,t-n.y);a<i&&(i=a,r={axis:n.axis,sign:n.sign})}return r}tickAmbientPreviews(e){if(!this.playing)for(let[t]of this.pathOf)(t instanceof rC||t instanceof vp||t instanceof oC||t instanceof $T||t instanceof PS)&&t.update(e)}modelAnimationsAt(e){let t=this.nodeAtPath.get(e.join(`.`));return t instanceof rC?t.availableAnimations():[]}drawOverlay(){let e=this.overlay.getContext(`2d`);if(!e)return;let t=this.overlay.clientWidth,n=this.overlay.clientHeight,r=Math.min(devicePixelRatio||1,2);if((this.overlay.width!==t*r||this.overlay.height!==n*r)&&(this.overlay.width=t*r,this.overlay.height=n*r),e.setTransform(r,0,0,r,0,0),e.clearRect(0,0,t,n),this.playing)return;if(this.rendererDim===`3d`){this.showColliders&&this.drawColliders3D(e),this.drawGizmo(e),this.drawModeGizmo(e),this.readout&&dM(e,this.readout.x,this.readout.y,this.readout.text);return}if(!this.renderer2d)return;if(this.drawAxes2D(e),this.showColliders)for(let[t]of this.pathOf){let n=t.collider;n&&typeof n==`object`&&`shape`in n&&this.drawCollider(e,t,n)}if(this.hoveredNode&&this.hoveredNode!==this.liveSelected()){let t=this.renderer2d.boundsOf(this.hoveredNode);t&&(e.strokeStyle=`rgba(110, 231, 220, 0.45)`,e.lineWidth=1.5,e.setLineDash([]),e.strokeRect(t.x,t.y,t.w,t.h))}for(let t of this.extraPaths){let n=this.nodeAtPath.get(t.join(`.`)),r=n?this.renderer2d.boundsOf(n):null;r&&(e.strokeStyle=`rgba(110, 231, 220, 0.7)`,e.lineWidth=1.5,e.setLineDash([4,3]),e.strokeRect(r.x-2,r.y-2,r.w+4,r.h+4),e.setLineDash([]))}let i=this.liveSelected();if(i){let t=this.renderer2d.boundsOf(i)??this.pointBounds(i);if(t){e.strokeStyle=`#6ee7dc`,e.lineWidth=2,e.setLineDash([6,4]),e.lineDashOffset=-(performance.now()/50%10),e.strokeRect(t.x-2,t.y-2,t.w+4,t.h+4),e.setLineDash([]),e.fillStyle=`#6ee7dc`;for(let[n,r]of[[t.x-2,t.y-2],[t.x+t.w+2,t.y-2],[t.x-2,t.y+t.h+2],[t.x+t.w+2,t.y+t.h+2]])e.fillRect(n-3,r-3,6,6)}}this.drawModeGizmo(e),this.readout&&dM(e,this.readout.x,this.readout.y,this.readout.text)}drawModeGizmo(e){if(this.gizmoMode===`rotate`){let t=this.ringLayout();t&&uM(e,t.rings,this.activeRing);return}let t=this.gizmoLayout();t&&oM(e,t,this.gizmoActive,this.gizmoMode===`scale`)}drawGizmo(e){let t=this.gizmoCenter();e.beginPath(),e.arc(t.x,t.y,t.r,0,Math.PI*2),e.fillStyle=`rgba(16, 19, 29, 0.72)`,e.fill(),e.strokeStyle=`rgba(52, 60, 84, 0.9)`,e.lineWidth=1,e.stroke();let n={x:`#fb7185`,y:`#86efac`,z:`#7aa2ff`},r={x1:`right`,"x-1":`left`,y1:`top`,"y-1":`bottom`,z1:`front`,"z-1":`back`};for(let r of this.gizmoHandles()){let i=this.hoveredGizmo?.axis===r.axis&&this.hoveredGizmo?.sign===r.sign,a=n[r.axis],o=(r.z+1)/2;e.globalAlpha=i?1:.45+o*.55,e.strokeStyle=a,e.lineWidth=1.5,e.beginPath(),e.moveTo(t.x,t.y),e.lineTo(r.x,r.y),e.stroke(),e.beginPath(),e.arc(r.x,r.y,(r.sign===1?7:5)+(i?2:0),0,Math.PI*2),r.sign===1?(e.fillStyle=a,e.fill(),e.fillStyle=`#0b0d14`,e.font=`700 8px ui-monospace, monospace`,e.textAlign=`center`,e.textBaseline=`middle`,e.fillText(r.axis.toUpperCase(),r.x,r.y+.5)):(e.fillStyle=`rgba(16, 19, 29, 0.9)`,e.fill(),e.stroke())}if(e.globalAlpha=1,this.hoveredGizmo){let n=r[`${this.hoveredGizmo.axis}${this.hoveredGizmo.sign}`];e.fillStyle=`#9aa3bd`,e.font=`600 10px Inter, system-ui, sans-serif`,e.textAlign=`center`,e.textBaseline=`bottom`,e.fillText(`${n} view`,t.x,t.y-t.r-6)}}drawColliders3D(e){let t=this.renderer3d;if(!t)return;e.save(),e.strokeStyle=pM,e.lineWidth=1.5;let n=(e,n,r)=>t.screenFromWorld(e,n,r),r=(t,r=!1)=>{e.beginPath();let i=!1;for(let[r,a,o]of t){let t=n(r,a,o);if(t.behind){i=!1;continue}i?e.lineTo(t.x,t.y):e.moveTo(t.x,t.y),i=!0}r&&e.closePath(),e.stroke()},i=(e,t,n,i,a)=>{let o=[];for(let r=0;r<=32;r++){let s=r/32*Math.PI*2,c=Math.cos(s)*i,l=Math.sin(s)*i;a===`xz`?o.push([e+c,t,n+l]):a===`xy`?o.push([e+c,t+l,n]):o.push([e,t+c,n+l])}r(o)};for(let[e]of this.pathOf){let t=e.collider,n=e._object3D;if(!t||typeof t!=`object`||!(`shape`in t)||!n)continue;n.getWorldPosition(yM);let a=t.offset??[0,0,0],o=yM.x+(a[0]??0),s=yM.y+(a[1]??0),c=yM.z+(a[2]??0);if(t.shape===`box`){let e=t.size??[1,1,1],n=(e[0]??1)/2,i=(e[1]??1)/2,a=(e[2]??1)/2;r([[o-n,s-i,c-a],[o+n,s-i,c-a],[o+n,s-i,c+a],[o-n,s-i,c+a]],!0),r([[o-n,s+i,c-a],[o+n,s+i,c-a],[o+n,s+i,c+a],[o-n,s+i,c+a]],!0);for(let[e,t]of[[-1,-1],[1,-1],[1,1],[-1,1]])r([[o+e*n,s-i,c+t*a],[o+e*n,s+i,c+t*a]])}else if(t.shape===`sphere`){let e=t.radius??.5;i(o,s,c,e,`xz`),i(o,s,c,e,`xy`),i(o,s,c,e,`yz`)}else if(t.shape===`capsule`){let e=t.radius??.4,n=(t.height??1)/2;i(o,s+n,c,e,`xz`),i(o,s-n,c,e,`xz`);for(let[t,i]of[[e,0],[-e,0],[0,e],[0,-e]])r([[o+t,s-n,c+i],[o+t,s+n,c+i]])}}e.restore()}drawAxes2D(e){let t=this.gizmoCenter();e.save(),e.shadowColor=`rgba(0, 0, 0, 0.25)`,e.shadowBlur=10,e.shadowOffsetY=2,e.beginPath(),e.arc(t.x,t.y,31,0,Math.PI*2),e.fillStyle=`rgba(13, 16, 24, 0.42)`,e.fill(),e.shadowColor=`transparent`,e.strokeStyle=`rgba(255, 255, 255, 0.16)`,e.lineWidth=1,e.stroke();let n=t.x-9,r=t.y-9;e.lineCap=`round`;let i=(t,i,a,o)=>{let s=n+t*21,c=r+i*21,l=e.createLinearGradient(n,r,s,c);l.addColorStop(0,`${a}55`),l.addColorStop(1,a),e.strokeStyle=l,e.lineWidth=2,e.beginPath(),e.moveTo(n,r),e.lineTo(s,c),e.stroke(),e.beginPath(),e.arc(s,c,6.5,0,Math.PI*2),e.fillStyle=a,e.fill(),e.font=`800 8px Inter, system-ui, sans-serif`,e.textAlign=`center`,e.textBaseline=`middle`,e.fillStyle=`#0b0d14`,e.fillText(o,s,c+.5)};i(1,0,`#fb7185`,`X`),i(0,1,`#86efac`,`Y`),e.beginPath(),e.arc(n,r,2.5,0,Math.PI*2),e.fillStyle=`#e2e6f0`,e.fill(),e.restore()}pointBounds(e){if(!this.renderer2d)return null;let t=e.position;if(!Array.isArray(t))return null;let n=wM(e),r=this.renderer2d.screenFromWorld(n.x,n.y);return{x:r.x-12,y:r.y-12,w:24,h:24}}drawCollider(e,t,n){if(!this.renderer2d)return;let r=wM(t),i=n.offset??[0,0],a=this.renderer2d.screenFromWorld(r.x+(i[0]??0),r.y+(i[1]??0)),o=this.renderer2d.view().zoom;e.strokeStyle=pM,e.lineWidth=1.5,e.setLineDash([]);let s=n.shape;if(s===`rect`){let t=n.size??[32,32],r=(t[0]??32)/2*o,i=(t[1]??32)/2*o;e.strokeRect(a.x-r,a.y-i,r*2,i*2)}else if(s===`circle`){let t=(n.radius??16)*o;e.beginPath(),e.arc(a.x,a.y,t,0,Math.PI*2),e.stroke()}else if(s===`capsule`){let t=(n.radius??16)*o,r=(n.height??32)/2*o;e.beginPath(),e.arc(a.x,a.y-r,t,Math.PI,0),e.arc(a.x,a.y+r,t,0,Math.PI),e.closePath(),e.stroke()}e.setLineDash([])}bindPointer(){let e=this.overlay,t=null,n=null,r=null,i=null,a=null,o=null,s=null,c=t=>{let n=this.ringLayout();if(!n)return!1;let r=lM(n.rings,t.offsetX,t.offsetY,9);if(!r)return!1;let i=this.liveSelected();if(!i)return!1;e.setPointerCapture(t.pointerId);let a=i.rotation,o=1;if(this.rendererDim===`3d`&&this.renderer3d){let{forward:e}=this.renderer3d.cameraBasis();o=(r===`x`?e.x:r===`y`?e.y:e.z)>0?1:-1}return s={axis:r,node:i,origin:n.origin,startAngle:sM(n.origin,t.offsetX,t.offsetY),startRotation:Array.isArray(a)?[...a]:a??0,sign:o},this.activeRing=r,!0},l=e=>{if(!s)return;let{axis:t,node:n,origin:r,startAngle:i,startRotation:a,sign:o}=s,c=cM(i,sM(r,e.offsetX,e.offsetY))*o,l=e.shiftKey||this.shiftHeld?15:null,u=e=>{let t=e+c;return l?Math.round(t/l)*l:Math.round(t*10)/10},d;if(Array.isArray(a)){let e={x:0,y:1,z:2}[t],r=[...a];r[e]=u(a[e]??0),d=r[e],n.rotation=r}else{let e=u(a);d=e,n.rotation=e}this.readout={x:e.offsetX,y:e.offsetY,text:`${d}°${l?` ⌁15°`:``}`}},u=e=>{if(!s)return;let{node:t,startRotation:n}=s;if(!e)t.rotation=n;else{let e=this.pathOf.get(t),n=t.rotation;e!==void 0&&n!==void 0&&(Array.isArray(n)?this.cb.onNodeRotated3D(e,[n[0]??0,n[1]??0,n[2]??0]):this.cb.onNodeRotated2D(e,n))}s=null,this.activeRing=null,this.readout=null};this.cancelActiveDrag=()=>{u(!1),o&&(o.node.position=o.startPos,o.node.scale=o.startScale,o=null,this.gizmoActive=null,this.readout=null)};let d=t=>{if(this.gizmoMode===`rotate`)return c(t);let n=this.gizmoLayout();if(!n)return!1;let r=iM(n,t.offsetX,t.offsetY);if(!r)return!1;let i=this.liveSelected();if(!i)return!1;e.setPointerCapture(t.pointerId);let a=[...i.position??[]],s=[...i.scale??[]];return o={hit:r,mode:this.gizmoMode===`scale`||t.button===2?`scale`:`move`,startX:t.offsetX,startY:t.offsetY,layout:n,node:i,startPos:a,startScale:s},this.gizmoActive=r,!0},f=e=>{if(!o)return;let{hit:t,mode:n,layout:r,node:i,startPos:a,startScale:s}=o,c=e.offsetX-o.startX,l=e.offsetY-o.startY,u=this.rendererDim===`3d`,d={x:0,y:1,z:2},f=e.shiftKey||this.shiftHeld,p=f?u?.5:10:null,m=f?.25:null,h=e=>p?Math.round(e/p)*p:e,g=e=>m?Math.max(m,Math.round(e/m)*m):e;if(n===`scale`){let n=Math.max(.05,1+(c-l)*.005),r=[...s];if(t.kind===`axis`)r[d[t.axis]]=mM(g((s[d[t.axis]]??1)*n));else for(let e=0;e<r.length;e++)r[e]=mM(g((s[e]??1)*n));i.scale=r,this.readout={x:e.offsetX,y:e.offsetY,text:`×${r.map(e=>mM(e)).join(`, `)}`};return}let _=[...a];if(t.kind===`axis`){let e=r.axes.find(e=>e.axis===t.axis);if(!e)return;let n=aM(e,c,l);if(u)_[d[t.axis]]=mM(h((a[d[t.axis]]??0)+n));else{let e=CM(i,t.axis===`x`?n:0,t.axis===`y`?n:0),r=t.axis===`x`?e.x:e.y;_[d[t.axis]]=Math.round(h((a[d[t.axis]]??0)+r))}}else if(u&&this.renderer3d){let{right:e,up:t}=this.renderer3d.cameraBasis(),n=this.orbit.dist*.0016;_[0]=mM(h((a[0]??0)+(e.x*c-t.x*l)*n)),_[1]=mM(h((a[1]??0)+(e.y*c-t.y*l)*n)),_[2]=mM(h((a[2]??0)+(e.z*c-t.z*l)*n))}else if(this.renderer2d){let e=this.renderer2d.view(),t=CM(i,c/e.zoom,l/e.zoom);_[0]=Math.round(h((a[0]??0)+t.x)),_[1]=Math.round(h((a[1]??0)+t.y))}i.position=_,this.readout={x:e.offsetX,y:e.offsetY,text:`[${_.map(e=>mM(e)).join(`, `)}]`}},p=()=>{if(!o)return;let{node:e,mode:t}=o,n=this.pathOf.get(e);if(n)if(t===`scale`){let t=e.scale??[];this.rendererDim===`3d`?this.cb.onNodeScaled3D(n,[t[0]??1,t[1]??1,t[2]??1]):this.cb.onNodeScaled(n,[t[0]??1,t[1]??1])}else{let t=e.position??[];this.rendererDim===`3d`?this.cb.onNodeMoved3D(n,[t[0]??0,t[1]??0,t[2]??0]):this.cb.onNodeMoved(n,[t[0]??0,t[1]??0])}o=null,this.gizmoActive=null};e.addEventListener(`pointerdown`,o=>{if(this.playing||d(o))return;if(this.rendererDim===`3d`){let t=this.gizmoHit(o.offsetX,o.offsetY);if(t){this.snapView(t.axis,t.sign);return}e.setPointerCapture(o.pointerId),o.button===1||o.button===2||o.shiftKey?i={x:o.offsetX,y:o.offsetY,target:[...this.orbit.target]}:(r={x:o.offsetX,y:o.offsetY,yaw:this.orbit.yaw,pitch:this.orbit.pitch},a={x:o.offsetX,y:o.offsetY,toggle:o.ctrlKey||o.metaKey});return}if(!this.renderer2d)return;if(e.setPointerCapture(o.pointerId),o.button===1||o.button===2||o.shiftKey){let e=this.renderer2d.view();n={startX:o.offsetX,startY:o.offsetY,cx:e.cx,cy:e.cy};return}let s=this.renderer2d.pick(o.offsetX,o.offsetY),c=s?this.pathOf.get(s)??null:null;if(this.cb.onPick(c,{toggle:o.ctrlKey||o.metaKey}),this.selectedPath=c,s&&c&&Array.isArray(s.position)){let e=s.position;t={node:s,startWorld:this.renderer2d.worldFromScreen(o.offsetX,o.offsetY),startPos:[e[0]??0,e[1]??0]}}}),e.addEventListener(`pointermove`,a=>{if(!this.playing){if(s){l(a);return}if(o){f(a);return}if(this.rendererDim===`3d`){if(!r&&!i?(this.hoveredGizmo=this.gizmoHandleAt(a.offsetX,a.offsetY),e.style.cursor=this.hoveredGizmo?`pointer`:`grab`):e.style.cursor=`grabbing`,r)this.orbit.yaw=r.yaw-(a.offsetX-r.x)*.006,this.orbit.pitch=Math.max(-1.5,Math.min(1.5,r.pitch+(a.offsetY-r.y)*.006)),this.syncOrbit();else if(i){let{right:e,up:t}=this.orbitVectors(),n=this.orbit.dist*.0016,r=(a.offsetX-i.x)*n,o=(a.offsetY-i.y)*n;this.orbit.target=[i.target[0]-e.x*r+t.x*o,i.target[1]-e.y*r+t.y*o,i.target[2]-e.z*r+t.z*o],this.syncOrbit()}return}if(this.renderer2d){if(e.style.cursor=`crosshair`,n){let e=this.renderer2d.view();this.renderer2d.viewOverride={cx:n.cx-(a.offsetX-n.startX)/e.zoom,cy:n.cy-(a.offsetY-n.startY)/e.zoom,zoom:e.zoom};return}if(t){let e=this.renderer2d.worldFromScreen(a.offsetX,a.offsetY),n=CM(t.node,e.x-t.startWorld.x,e.y-t.startWorld.y),r=Math.round(t.startPos[0]+n.x),i=Math.round(t.startPos[1]+n.y);t.node.position=[r,i];return}this.hoveredNode=this.renderer2d.pick(a.offsetX,a.offsetY)}}});let m=e=>{if(u(!0),p(),this.readout=null,a&&e&&this.renderer3d&&Math.hypot(e.offsetX-a.x,e.offsetY-a.y)<4){let e=this.renderer3d.pick(a.x,a.y),t=e?this.pathOf.get(e)??null:null;t&&(this.cb.onPick(t,{toggle:a.toggle}),this.selectedPath=t)}if(a=null,r=null,i=null,t){let e=this.pathOf.get(t.node),n=t.node.position;e&&(n[0]!==t.startPos[0]||n[1]!==t.startPos[1])&&this.cb.onNodeMoved(e,[n[0]??0,n[1]??0]),t=null}n=null};e.addEventListener(`pointerup`,m),e.addEventListener(`pointercancel`,m),e.addEventListener(`contextmenu`,e=>e.preventDefault()),e.addEventListener(`dblclick`,e=>{if(this.playing)return;let t=this.rendererDim===`3d`?this.renderer3d?.pick(e.offsetX,e.offsetY)??null:this.renderer2d?.pick(e.offsetX,e.offsetY)??null,n=t?this.pathOf.get(t)??null:null;n&&(this.cb.onPick(n,{toggle:e.ctrlKey||e.metaKey}),this.selectedPath=n)}),e.addEventListener(`wheel`,e=>{if(this.playing)return;e.preventDefault();let t=e.deltaY<0?1.1:1/1.1;if(this.rendererDim===`3d`){this.orbit.dist=Math.max(.3,Math.min(800,this.orbit.dist/t)),this.syncOrbit();return}if(!this.renderer2d)return;let n=this.liveSelected();if(e.altKey&&n&&Array.isArray(n.scale)){let e=n.scale,r=[Math.round((e[0]??1)*t*100)/100,Math.round((e[1]??1)*t*100)/100];n.scale=r;let i=this.pathOf.get(n);i&&this.cb.onNodeScaled(i,r);return}let r=this.renderer2d.view(),i=this.renderer2d.worldFromScreen(e.offsetX,e.offsetY),a=Math.min(8,Math.max(.1,r.zoom*t));this.renderer2d.viewOverride={cx:i.x-(e.offsetX-r.w/2)/a,cy:i.y-(e.offsetY-r.h/2)/a,zoom:a}},{passive:!1})}zoomToFit(){if(this.rendererDim===`3d`){let e=new aa;for(let[t]of this.pathOf){let n=t._object3D;n&&(e.expandByObject(n),e.expandByPoint(n.getWorldPosition(yM)))}if(e.isEmpty())this.orbit.target=[0,1,0],this.orbit.dist=10;else{let t=e.getCenter(new H),n=e.getSize(new H);this.orbit.target=[t.x,t.y,t.z],this.orbit.dist=Math.max(2.5,n.length()*.85)}this.syncOrbit();return}if(!this.renderer2d)return;let e=this.editCanvas.clientWidth||960,t=this.editCanvas.clientHeight||540,n=1/0,r=1/0,i=-1/0,a=-1/0;for(let[e]of this.pathOf){let t=e.position;if(!Array.isArray(t))continue;let o=wM(e);n=Math.min(n,o.x),r=Math.min(r,o.y),i=Math.max(i,o.x),a=Math.max(a,o.y)}if(!Number.isFinite(n))return;let o=(n+i)/2,s=(r+a)/2,c=Math.max(i-n+200,200),l=Math.max(a-r+200,200),u=Math.min(2,Math.min(e/c,t/l));this.renderer2d.viewOverride={cx:o,cy:s,zoom:u}}async play(e){if(this.playing)return null;let t=structuredClone(e);DM(t.root),AM(t);let n;try{n=at(structuredClone(t),{declareConnectionSignals:!0})}catch{delete t.connections;try{n=at(structuredClone(t))}catch(e){return e instanceof Error?e.message:String(e)}}let r=new Ee;r.setScene(n),r.input.attachKeyboard(window),this.playCanvas.hidden=!1;let i=e.dimension??`2d`;try{if(i===`2d`){this.playRenderer=new jp({canvas:this.playCanvas,engine:r});let{enablePhysics2D:e}=await Xf(async()=>{let{enablePhysics2D:e}=await Promise.resolve().then(()=>Rp);return{enablePhysics2D:e}},void 0,import.meta.url),t=await e(r);t.debugDraw=this.showColliders}else{this.playRenderer=new _D({canvas:this.playCanvas,engine:r});let{enablePhysics3D:e}=await Xf(async()=>{let{enablePhysics3D:e}=await Promise.resolve().then(()=>TD);return{enablePhysics3D:e}},void 0,import.meta.url),t=await e(r);t.debugDraw=this.showColliders}}catch(e){return this.stop(),e instanceof Error?e.message:String(e)}return r.start(),this.playEngine=r,this.cb.onPlayStateChanged(!0),null}stop(){this.playEngine?.input.dispose(),this.playEngine?.stop(),this.playEngine=null,this.playRenderer?.dispose(),this.playRenderer=null,this.playCanvas.hidden=!0,this.cb.onPlayStateChanged(!1),this.lastAppliedKey=null,this.lastGood&&this.apply(this.lastGood)}},pM=`rgba(0, 255, 110, 0.95)`;function mM(e){return Math.round(e*100)/100}var hM=new H(0,1,0),gM=new H,_M=new H,vM=new V,yM=new H,bM=new G,xM=new H,SM=new H;function CM(e,t,n){let r=e._object2D?.parent;return r?(bM.copy(r.matrixWorld).invert(),xM.set(t,-n,0).applyMatrix4(bM),SM.set(0,0,0).applyMatrix4(bM),{x:xM.x-SM.x,y:-(xM.y-SM.y)}):{x:t,y:n}}function wM(e){let t=e._object2D;if(t)return t.getWorldPosition(yM),{x:yM.x,y:-yM.y};let n=0,r=0,i=e;for(;i;)Array.isArray(i.position)&&(n+=i.position[0]??0,r+=i.position[1]??0),i=i.parent;return{x:n,y:r}}function TM(e){if(!e)return null;if(e.type===`Camera3D`){let t=e.props?.position??[0,2,8];return[t[0]??0,t[1]??2,t[2]??8]}for(let t of e.children??[]){let e=TM(t);if(e)return e}return null}function EM(e){if(!e)return null;if(e.type===`Camera2D`){let t=e.props?.position??[0,0];return[t[0]??0,t[1]??0]}for(let t of e.children??[]){let e=EM(t);if(e)return e}return null}function DM(e){if(typeof e!=`object`||!e)return;let t=e;delete t.script;for(let e of t.children??[])DM(e)}var OM=null;function kM(){if(OM)return OM;let e=document.createElement(`canvas`);e.width=16,e.height=16;let t=e.getContext(`2d`);for(let e=0;e<2;e++)for(let n=0;n<2;n++)t.fillStyle=(n+e)%2==0?`#c252c2`:`#2b2b3b`,t.fillRect(n*8,e*8,8,8);return OM=e.toDataURL(),OM}function AM(e){let t=e.assets;if(t)for(let e of Object.values(t))typeof e?.url==`string`&&!e.url.includes(`/`)&&!e.url.includes(`.`)&&(e.url=kM())}Ke(),Tp(),_E(),FD();var $=e=>{let t=document.querySelector(e);if(!t)throw Error(`missing ${e}`);return t};async function jM(){let e=await HD();$(`#engine-version`).textContent=`incanto@${e.version}`,NA($(`#popover`)),HO(),$(`#docs-btn`).addEventListener(`click`,()=>BO());let t=$(`#lang-btn`),n=[{code:`en`,label:`English`},{code:`ko`,label:`한국어`}],r=()=>{$(`#lang-current`).textContent=XD()===`ko`?`한국어`:`EN`};r(),t.addEventListener(`click`,()=>{document.querySelector(`.lang-menu`)?.remove();let e=document.createElement(`div`);e.className=`context-menu floating lang-menu`;for(let{code:t,label:r}of n){let n=document.createElement(`button`);n.type=`button`,n.className=`menu-item`;let i=document.createElement(`span`);i.textContent=r;let a=document.createElement(`span`);a.textContent=XD()===t?`✓`:``,a.className=`lang-check`,n.append(i,a),n.addEventListener(`click`,()=>{e.remove(),ZD(t)}),e.appendChild(n)}document.body.appendChild(e);let r=t.getBoundingClientRect();e.style.left=`${r.left}px`,e.style.top=`${r.bottom+6}px`;let i=n=>{e.contains(n.target)||n.target===t||(e.remove(),document.removeEventListener(`pointerdown`,i,!0))};setTimeout(()=>document.addEventListener(`pointerdown`,i,!0),0)}),QD(()=>{r(),UO(),i.select(i.selection)});let i=new wj({format:1,type:`scene`,name:``,root:null}),a=$(`#tree`),o=$(`#inspector`),s=$(`#save-btn`),c=$(`#undo-btn`),l=$(`#play-btn`),u=$(`#error-banner`),d=$(`#play-notice`),f=$(`#add-type`),p=$(`#picker`),m=$(`#picker-list`),h=$(`#scenes-btn`),g=e.input,_=e.output,v,y=!1,b=e=>{u.hidden=!1,u.textContent=e,NO.error(e)},x=new fM($(`#viewport`),$(`#overlay`),$(`#play-canvas`),{onPick:(e,t)=>i.select(e??null,t),onNodeMoved:(e,t)=>k(e,`position`,t,[0,0]),onNodeScaled:(e,t)=>k(e,`scale`,t,[1,1]),onNodeMoved3D:(e,t)=>k(e,`position`,t,[0,0,0]),onNodeScaled3D:(e,t)=>k(e,`scale`,t,[1,1,1]),onNodeRotated2D:(e,t)=>O(e,`rotation`,t,0),onNodeRotated3D:(e,t)=>k(e,`rotation`,t,[0,0,0]),onModeChanged:e=>{for(let t of[`move`,`rotate`,`scale`])$(`#tool-${t}`).classList.toggle(`active`,t===e)},onPlayStateChanged:e=>{document.body.classList.toggle(`playing`,e),l.classList.toggle(`stop`,e),l.innerHTML=e?`<svg width="11" height="11" viewBox="0 0 24 24" fill="currentColor"><rect x="6" y="6" width="12" height="12"/></svg> stop`:`<svg width="11" height="11" viewBox="0 0 24 24" fill="currentColor"><path d="M8 5v14l11-7z"/></svg> play`,d.hidden=!e,e&&(d.textContent=`▶ simulating — physics + input live; game scripts run only in your real game. Esc or stop to return.`)},onError:b}),S=de().sort(),C=``,w=e=>{if(e===C)return;C=e,f.textContent=``;let t=e===`3d`?[`3D`,`3D Physics`,`Core`,`Network`,`2D`,`2D Physics`]:[`2D`,`2D Physics`,`Core`,`Network`,`3D`,`3D Physics`];for(let[e]of FO)t.includes(e)||t.push(e);for(let e of t){let t=FO.find(([t])=>t===e)?.[1];if(!t)continue;let n=document.createElement(`optgroup`);n.label=e;for(let e of S.filter(t)){let t=document.createElement(`option`);t.value=e,t.textContent=e,n.appendChild(t)}n.children.length>0&&f.appendChild(n)}f.value=e===`3d`?`MeshInstance3D`:`Sprite2D`};w(`2d`);let T=()=>{let e=i.selection??[];for(let t of f.querySelectorAll(`option`)){let n=structuredClone(i.working),r=n.root;if(!n.root)n.root={name:`__probe`,type:t.value};else{for(let t of e)r=r?.children?.[t];if(!r)continue;r.children||=[],r.children.push({name:`__probe`,type:t.value})}t.disabled=x.validate(n)!==null}};f.addEventListener(`mousedown`,T),f.addEventListener(`focus`,T);let E=!1,D=()=>{if(!y)return;Uj(a,i),dO($(`#asset-tree`),i),o.textContent=``,fO(o,i,se)||LA(o,i,{modelRefs:()=>NM(i,`model`),animationsForSelection:()=>i.selection?x.modelAnimationsAt(i.selection):[],assetRefs:e=>PM(i,e),addAsset:(e,t)=>FM(i,e,t),confirm:se}),w(i.working.dimension??`2d`),$(`#hints`).hidden=(i.working.dimension??`2d`)===`3d`,$(`#hints-3d`).hidden=(i.working.dimension??`2d`)!==`3d`,x.setSelection(i.selection,i.extra);let e=i.pendingLivePatch;if(i.pendingLivePatch=null,!x.playing)if(e&&x.patchProp(i.working,e.path,e.key,e.value))u.hidden=!0;else{let e=x.apply(i.working);u.hidden=e===null,e!==null&&b(e)}s.disabled=!i.dirty||!i.working.root,s.title=i.working.root?``:`Add a root node before saving`,c.disabled=!i.canUndo,i.dirty!==E&&(E=i.dirty,NO.change(i.dirty))};i.onChange(D),i.validator=e=>x.validate(e),i.onError=b;let O=(e,t,n,r)=>{let a=i.nodeAt(e);a&&i.mutate(()=>{a.props||={},n===r?(delete a.props[t],Object.keys(a.props).length===0&&delete a.props):a.props[t]=n},{path:e,key:t,value:n})},k=(e,t,n,r)=>{let a=i.nodeAt(e);a&&i.mutate(()=>{a.props||={},JSON.stringify(n)===JSON.stringify(r)?(delete a.props[t],Object.keys(a.props).length===0&&delete a.props):a.props[t]=n},{path:e,key:t,value:n})},A=async(t,n,r)=>{x.playing&&x.stop();let a=await KD(t);v=t,g=n,_=r,y=!0,$(`#file-path`).textContent=t??n,$(`#file-chip`).title=`input: ${n}\noutput: ${r}`,p.hidden=!0,i.reset(a),e.mode===`project`&&NO.open(n,r)},ee=async()=>{let e=await UD();if(m.textContent=``,e.length===0){let e=document.createElement(`div`);e.className=`tree-row`,e.textContent=`no *.scene.json found — create one below`,m.appendChild(e)}for(let t of e){let e=document.createElement(`div`);e.className=`tree-row`,e.textContent=t.rel,e.addEventListener(`click`,()=>{A(t.rel,t.abs,t.abs).catch(e=>b(e instanceof Error?e.message:String(e)))}),m.appendChild(e)}p.hidden=!1},te=()=>{p.hidden=!0};if($(`#picker-close`).addEventListener(`click`,te),p.addEventListener(`pointerdown`,e=>{e.target===p&&te()}),$(`#picker-create`).addEventListener(`click`,()=>{(async()=>{let e=$(`#picker-path`).value.trim();if(e)try{let t=await WD(e.endsWith(`.scene.json`)?e:`${e}.scene.json`);await A(t.rel,t.abs,t.abs)}catch(e){b(e instanceof Error?e.message:String(e))}})()}),e.mode===`project`){h.hidden=!1,h.addEventListener(`click`,()=>void ee());let e=await UD(),t=e.length===1?e[0]:void 0;t?await A(t.rel,t.abs,t.abs):await ee()}else await A(void 0,e.input,e.output);$(`#add-btn`).addEventListener(`click`,()=>{let e=i.selection??[],t=f.value,n=i.insertNode({name:t,type:t},e);n&&i.select(n)});let ne=$(`#generate`);DA(i),$(`#generate-btn`).addEventListener(`click`,()=>TA(i));let re=$(`#confirm`),ie=e=>{if(i.selection===null&&e.length===0){b(`The scene itself cannot be deleted — it IS the file.`);return}if(e.length===0){let e=i.working.root;if(!e)return;let t=MM(e);se(`This deletes the ROOT '${String(e.name??``)}'${t>0?` AND its ${t} descendant node${t===1?``:`s`}`:``} — the scene goes empty, and the next node you add becomes the new root.`,`delete root`,()=>i.deleteRoot());return}let t=e[0]?.slice(0,-1)??[],n=i.findRemovalReferences(e);if(n.length===0){let n=e.reduce((e,t)=>{let n=i.nodeAt(t);return e+(n?MM(n):0)},0);if(n>0){se(`This deletes ${e.length>1?`${e.length} nodes`:`'${String(i.nodeAt(e[0]??[])?.name??``)}'`} AND ${n} descendant node${n===1?``:`s`}.`,`delete ${e.length+n} nodes`,()=>{i.removeNodes(e)&&i.select(t)});return}i.removeNodes(e)&&i.select(t);return}$(`#confirm-text`).textContent=`${n.length} reference${n.length>1?`s`:``} still point at the node(s) you are deleting. Unlink them and delete, or cancel.`;let r=$(`#confirm-list`);r.textContent=``;for(let e of n){let t=document.createElement(`div`);t.textContent=`${e.kind===`uid`?`◆ uid`:`⇄ connection`} ${e.where}`,r.appendChild(t)}$(`#confirm-title`).textContent=`still referenced`,$(`#confirm-delete`).textContent=`unlink & delete`,re.hidden=!1,ae={selections:e,parentOfFirst:t}},ae=null,oe=null;function se(e,t,n){$(`#confirm-title`).textContent=`are you sure?`,$(`#confirm-text`).textContent=e,$(`#confirm-list`).textContent=``,$(`#confirm-delete`).textContent=t,oe=n,re.hidden=!1}function j(){re.hidden=!0,ae=null,oe=null}$(`#confirm-close`).addEventListener(`click`,j),$(`#confirm-cancel`).addEventListener(`click`,j),re.addEventListener(`pointerdown`,e=>{e.target===re&&j()}),$(`#confirm-delete`).addEventListener(`click`,()=>{if(oe){oe(),j();return}ae&&i.removeNodesUnlinking(ae.selections)&&i.select(ae.parentOfFirst),j()}),Rj(ie),$(`#asset-add-btn`).addEventListener(`click`,e=>{e.stopPropagation(),i.startAddingAsset()}),$(`#group-add-btn`).addEventListener(`click`,e=>{e.stopPropagation(),i.startAddingGroup()});for(let e of document.querySelectorAll(`.section-toggle`)){let t=e.dataset.section??``,n=document.querySelector(`[data-body="${t}"]`),r=`incanto-editor-section-${t}`,i=t=>{e.classList.toggle(`collapsed`,t),n&&(n.hidden=t),localStorage.setItem(r,t?`1`:``)};i(localStorage.getItem(r)===`1`),e.addEventListener(`click`,()=>i(!e.classList.contains(`collapsed`)))}$(`#delete-btn`).addEventListener(`click`,()=>{ie(i.allSelections().filter(e=>e.length>0))}),l.addEventListener(`click`,()=>{if(x.playing){x.stop(),D();return}x.play(i.working).then(e=>{e&&b(e)})});for(let e of[`move`,`rotate`,`scale`])$(`#tool-${e}`).addEventListener(`click`,()=>x.setMode(e));x.setMode(`move`);let ce=$(`#tool-colliders`),le=`incanto-editor-show-colliders`,M=e=>{x.showColliders=e,ce.classList.toggle(`active`,e),localStorage.setItem(le,e?``:`0`)};M(localStorage.getItem(le)!==`0`),ce.addEventListener(`click`,()=>M(!x.showColliders));let ue=$(`#hints-dock`),fe=`incanto-editor-hints-open`;ue.classList.toggle(`open`,localStorage.getItem(fe)===`1`),$(`#hints-toggle`).addEventListener(`click`,()=>{let e=ue.classList.toggle(`open`);localStorage.setItem(fe,e?`1`:``)}),$(`#fit-btn`).addEventListener(`click`,()=>x.zoomToFit()),$(`#gameview-btn`).addEventListener(`click`,()=>x.gameView()),s.addEventListener(`click`,()=>{(async()=>{try{await qD(i.working,v),i.markSaved(),NO.save(g,_,i.working)}catch(e){b(e instanceof Error?e.message:String(e))}})()}),c.addEventListener(`click`,()=>i.undo()),window.addEventListener(`keydown`,e=>{if(e.key===`Escape`&&x.cancelActiveDrag(),e.key===`Escape`&&!$(`#docs`).hidden){VO();return}if(e.key===`Escape`&&!re.hidden){j();return}if(e.key===`Escape`&&!ne.hidden){EA();return}if(e.key===`Escape`&&!p.hidden){te();return}if(e.key===`Escape`&&x.playing){x.stop(),D();return}if(x.playing)return;let t=e.target.matches(`input, textarea, select`);(e.metaKey||e.ctrlKey)&&e.key===`z`?(e.preventDefault(),i.undo()):(e.metaKey||e.ctrlKey)&&e.key===`s`?(e.preventDefault(),s.disabled||s.click()):!t&&e.code===`KeyF`?x.zoomToFit():!t&&(e.code===`Digit0`||e.code===`Numpad0`)?x.gameView():!t&&e.code===`KeyW`?x.setMode(`move`):!t&&e.code===`KeyE`?x.setMode(`rotate`):!t&&e.code===`KeyR`&&x.setMode(`scale`)}),D(),NO.ready(g,_,e.version)}function MM(e){let t=0;for(let n of e.children??[])t+=1+MM(n);return t}function NM(e,t){let n=e.working.assets;return n?Object.entries(n).filter(([,e])=>e?.type===t).map(([e])=>`$${e}`):[]}function PM(e,t){let n=e.working.assets;return n?Object.entries(n).filter(([,e])=>!t||e?.type!==void 0&&t.includes(e.type)).map(([e])=>`$${e}`):[]}function FM(e,t,n){let r=e.working.assets??{};for(let[e,t]of Object.entries(r))if(t?.url===n.url)return`$${e}`;let i=t;for(let e=2;i in r;e++)i=`${t}-${e}`;return e.mutate(()=>{e.working.assets||(e.working.assets={}),e.working.assets[i]=n}),`$${i}`}jM().catch(e=>{let t=document.querySelector(`#error-banner`);t&&(t.hidden=!1,t.textContent=e instanceof Error?e.message:String(e))});export{Xf as t};
|
|
7365
|
+
`}},network:{title:{en:`network — replicate to other players`,ko:`network — 다른 플레이어에게 복제`},body:{en:`mode "owner" means THIS player owns the node and broadcasts the listed sync keys to everyone in the room (one owner node per player — usually your player character). Other players see it via a NetworkSpawner. Keys are relative to this node: "position", or "Skin.animation" for a child prop.`,ko:`mode "owner"는 이 플레이어가 노드를 소유하고 sync 키 목록을 방의 모두에게 송출한다는 뜻입니다(플레이어당 owner 노드 하나 — 보통 내 캐릭터). 다른 플레이어는 NetworkSpawner로 봅니다. 키는 이 노드 기준 상대 표기입니다: "position", 자식 prop은 "Skin.animation".`},example:`{ "mode": "owner", "sync": ["position"], "throttleMs": 50 }`},collider:{title:{en:`collider — the physics shape`,ko:`collider — 물리 모양`},body:{en:`Shapes: rect (size [w,h]), circle (radius), capsule (radius + height — good for characters). offset shifts the shape from the node position. The green dashed outline in the viewport shows exactly where it is.`,ko:`모양: rect(size [w,h]), circle(radius), capsule(radius+height — 캐릭터에 적합). offset이 노드 위치에서 모양을 이동시킵니다. 뷰포트의 초록 점선이 정확한 위치를 보여줍니다.`},example:`{ "shape": "capsule", "radius": 12, "height": 16 }`},physics:{title:{en:`physics — scene gravity`,ko:`physics — 씬 중력`},body:{en:`World gravity in px/s² (y-down: positive y pulls DOWN). [0, 1400] feels platformer-y; [0, 0] for top-down. Takes effect when the game calls enablePhysics2D — and in the editor’s play mode.`,ko:`월드 중력, px/s² 단위(y-아래: 양수 y가 아래로 당김). [0, 1400]이면 플랫포머 느낌, 탑다운은 [0, 0]. 게임이 enablePhysics2D를 부를 때 — 그리고 에디터 플레이 모드에서 — 적용됩니다.`},example:`"physics": { "gravity": [0, 1400] }`}},RA=null;function zA(e){document.addEventListener(`click`,t=>{let n=t.target,r=n.closest(`[data-help]`);if(!r){e.contains(n)||(e.hidden=!0);return}let i=LA[r.dataset.help??``];if(!i)return;if(!e.hidden&&RA===r){e.hidden=!0;return}RA=r,e.textContent=``;let a=document.createElement(`h4`);a.textContent=aO(i.title);let o=document.createElement(`div`);if(o.textContent=aO(i.body),e.append(a,o),i.example){let t=document.createElement(`pre`);t.textContent=i.example,e.appendChild(t)}if(i.copy){let t=document.createElement(`div`);t.className=`pop-actions`;let n=document.createElement(`button`);n.type=`button`,n.className=`linklike`,n.textContent=`⧉ ${i.copy.label}`,n.addEventListener(`click`,()=>{navigator.clipboard.writeText(i.copy?.text??``),n.textContent=`✓ copied`}),t.appendChild(n),e.appendChild(t)}e.hidden=!1;let s=r.getBoundingClientRect();e.style.left=`${Math.max(8,Math.min(innerWidth-300-8,s.left-300+20))}px`,e.style.top=`${Math.min(innerHeight-60,s.bottom+8)}px`})}function BA(e){let t=document.createElement(`button`);return t.type=`button`,t.className=`help-btn`,t.dataset.help=e,t.textContent=`?`,t.title=`What is this?`,t}var VA=JSON.parse('[{"name":"2dbasic","file":"characters/2dbasic.png","url":"incanto/assets/characters/2dbasic.png","kind":"character","bytes":30499,"description":"2dbasic sprite sheet image.anything,base character. (frame size 192x192)","animation":"characters/2dbasic.json","frameWidth":111,"frameHeight":83},{"name":"attacked","file":"audio/attacked.mp3","url":"incanto/assets/audio/attacked.mp3","kind":"audio","bytes":8757,"description":"Short hurt / took-damage SFX for the player or an enemy."},{"name":"bark_birch_color","file":"vegetation/bark/birch_color_1k.jpg","url":"https://agent8-games.verse8.io/assets/3D/default/textures/vegetation/bark/birch_color_1k.jpg","kind":"foliage","bytes":194186,"description":"Birch bark base color texture (1k), ambientcg Bark (CC0) packaged by ez-tree (MIT). Tree3D trunks/branches sample it by default from the agent8 CDN (the only sanctioned external host); the bundled `incanto/assets` copy (see this entry’s `file`) serves it offline."},{"name":"bark_birch_normal","file":"vegetation/bark/birch_normal_1k.jpg","url":"https://agent8-games.verse8.io/assets/3D/default/textures/vegetation/bark/birch_normal_1k.jpg","kind":"foliage","bytes":378046,"description":"Birch bark tangent-space normal texture (1k), ambientcg Bark (CC0) packaged by ez-tree (MIT). Tree3D trunks/branches sample it by default from the agent8 CDN (the only sanctioned external host); the bundled `incanto/assets` copy (see this entry’s `file`) serves it offline."},{"name":"bark_birch_roughness","file":"vegetation/bark/birch_roughness_1k.jpg","url":"https://agent8-games.verse8.io/assets/3D/default/textures/vegetation/bark/birch_roughness_1k.jpg","kind":"foliage","bytes":127387,"description":"Birch bark roughness texture (1k), ambientcg Bark (CC0) packaged by ez-tree (MIT). Tree3D trunks/branches sample it by default from the agent8 CDN (the only sanctioned external host); the bundled `incanto/assets` copy (see this entry’s `file`) serves it offline."},{"name":"bark_oak_color","file":"vegetation/bark/oak_color_1k.jpg","url":"https://agent8-games.verse8.io/assets/3D/default/textures/vegetation/bark/oak_color_1k.jpg","kind":"foliage","bytes":297877,"description":"Oak bark base color texture (1k), ambientcg Bark (CC0) packaged by ez-tree (MIT). Tree3D trunks/branches sample it by default from the agent8 CDN (the only sanctioned external host); the bundled `incanto/assets` copy (see this entry’s `file`) serves it offline."},{"name":"bark_oak_normal","file":"vegetation/bark/oak_normal_1k.jpg","url":"https://agent8-games.verse8.io/assets/3D/default/textures/vegetation/bark/oak_normal_1k.jpg","kind":"foliage","bytes":67610,"description":"Oak bark tangent-space normal texture (1k), ambientcg Bark (CC0) packaged by ez-tree (MIT). Tree3D trunks/branches sample it by default from the agent8 CDN (the only sanctioned external host); the bundled `incanto/assets` copy (see this entry’s `file`) serves it offline."},{"name":"bark_oak_roughness","file":"vegetation/bark/oak_roughness_1k.jpg","url":"https://agent8-games.verse8.io/assets/3D/default/textures/vegetation/bark/oak_roughness_1k.jpg","kind":"foliage","bytes":16648,"description":"Oak bark roughness texture (1k), ambientcg Bark (CC0) packaged by ez-tree (MIT). Tree3D trunks/branches sample it by default from the agent8 CDN (the only sanctioned external host); the bundled `incanto/assets` copy (see this entry’s `file`) serves it offline."},{"name":"bark_pine_color","file":"vegetation/bark/pine_color_1k.jpg","url":"https://agent8-games.verse8.io/assets/3D/default/textures/vegetation/bark/pine_color_1k.jpg","kind":"foliage","bytes":196361,"description":"Pine bark base color texture (1k), ambientcg Bark (CC0) packaged by ez-tree (MIT). Tree3D trunks/branches sample it by default from the agent8 CDN (the only sanctioned external host); the bundled `incanto/assets` copy (see this entry’s `file`) serves it offline."},{"name":"bark_pine_normal","file":"vegetation/bark/pine_normal_1k.jpg","url":"https://agent8-games.verse8.io/assets/3D/default/textures/vegetation/bark/pine_normal_1k.jpg","kind":"foliage","bytes":58237,"description":"Pine bark tangent-space normal texture (1k), ambientcg Bark (CC0) packaged by ez-tree (MIT). Tree3D trunks/branches sample it by default from the agent8 CDN (the only sanctioned external host); the bundled `incanto/assets` copy (see this entry’s `file`) serves it offline."},{"name":"bark_pine_roughness","file":"vegetation/bark/pine_roughness_1k.jpg","url":"https://agent8-games.verse8.io/assets/3D/default/textures/vegetation/bark/pine_roughness_1k.jpg","kind":"foliage","bytes":36136,"description":"Pine bark roughness texture (1k), ambientcg Bark (CC0) packaged by ez-tree (MIT). Tree3D trunks/branches sample it by default from the agent8 CDN (the only sanctioned external host); the bundled `incanto/assets` copy (see this entry’s `file`) serves it offline."},{"name":"box","file":"items/box.png","url":"incanto/assets/items/box.png","kind":"item","bytes":10861,"description":"Item box sprite for Dungeons and Dungeoners. Container sprite that holds random items or rewards when opened by player."},{"name":"buff_potion","file":"items/buff_potion.png","url":"incanto/assets/items/buff_potion.png","kind":"item","bytes":3809,"description":"Buff potion item sprite for Dungeons and Dungeoners. Consumable item that grants temporary stat boosts or positive effects to player character."},{"name":"coin","file":"items/coin.png","url":"incanto/assets/items/coin.png","kind":"item","bytes":1689,"description":"Gold coin collectible sprite for Dungeons and Dungeoners. Currency item with metallic sheen, used as in-game money or collectible reward."},{"name":"explosion","file":"audio/explosion.mp3","url":"incanto/assets/audio/explosion.mp3","kind":"audio","bytes":40124,"description":"Impactful explosion / large destructive hit SFX. Pair with the Particles2D \\"explosion\\" preset."},{"name":"floor00","file":"tiles/floor00.jpg","url":"incanto/assets/tiles/floor00.jpg","kind":"tile","bytes":28736,"description":"Basic floor tile texture for Dungeons and Dungeoners project, suitable for dungeon ground surfaces."},{"name":"gem","file":"items/gem.png","url":"incanto/assets/items/gem.png","kind":"item","bytes":8762,"description":"Gem collectible sprite for Dungeons and Dungeoners. Valuable gemstone item used as currency, crafting material, or quest objective."},{"name":"ghost","file":"characters/ghost.png","url":"incanto/assets/characters/ghost.png","kind":"character","bytes":22933,"description":"Ghost character with translucent appearance sprite sheet image (frame size 112x128)","animation":"characters/ghost.json","frameWidth":112,"frameHeight":128},{"name":"goblin","file":"characters/goblin.png","url":"incanto/assets/characters/goblin.png","kind":"character","bytes":57994,"description":"Medieval goblin with torch sprite sheet image (frame size 192x192)","animation":"characters/goblin.json","frameWidth":192,"frameHeight":192},{"name":"gold","file":"items/gold.png","url":"incanto/assets/items/gold.png","kind":"item","bytes":22313,"description":"Gold item sprite for Dungeons and Dungeoners. Gold pile or gold bar sprite representing valuable currency or treasure reward."},{"name":"gold-loot","file":"audio/gold_loot.mp3","url":"incanto/assets/audio/gold_loot.mp3","kind":"audio","bytes":7506,"description":"Metallic coin / gold pickup jingle — collecting currency."},{"name":"ground_dirt","file":"vegetation/ground/dirt_color.jpg","url":"https://agent8-games.verse8.io/assets/3D/default/textures/vegetation/ground/dirt_color.jpg","kind":"foliage","bytes":231383,"description":"Dense dirt/gravel ground texture (1024px) from the ez-tree demo app (MIT) — the reference meadow ground. Terrain3D grassland themes tile it for slope + noise-patch dirt by default; also bundled in `incanto/assets` (see `file`) for offline use."},{"name":"ground_dirt_normal","file":"vegetation/ground/dirt_normal.jpg","url":"https://agent8-games.verse8.io/assets/3D/default/textures/vegetation/ground/dirt_normal.jpg","kind":"foliage","bytes":123478,"description":"Dirt ground normal map (1024px) from the ez-tree demo app (MIT). Terrain3D grassland themes apply it across the whole ground band (demo parity); also bundled in `incanto/assets` (see `file`) for offline use."},{"name":"ground_grass","file":"vegetation/ground/grass.jpg","url":"https://agent8-games.verse8.io/assets/3D/default/textures/vegetation/ground/grass.jpg","kind":"foliage","bytes":322486,"description":"Mossy meadow grass ground texture (1024px) from the ez-tree demo app (MIT). Terrain3D grassland themes (meadow/forest generators) tile it as the grass splat layer by default; also bundled in `incanto/assets` (see `file`) for offline use."},{"name":"heal","file":"audio/heal.mp3","url":"incanto/assets/audio/heal.mp3","kind":"audio","bytes":29764,"description":"Soothing health-restore / heal spell SFX."},{"name":"hit-metal-bang","file":"audio/hit_metal_bang.mp3","url":"incanto/assets/audio/hit_metal_bang.mp3","kind":"audio","bytes":17972,"description":"Metallic impact — hitting armor, metal, or a blocked attack."},{"name":"hp_potion","file":"items/hp_potion.png","url":"incanto/assets/items/hp_potion.png","kind":"item","bytes":3513,"description":"Health potion item sprite for Dungeons and Dungeoners. Consumable healing item that restores player health points when used."},{"name":"ice-spear","file":"audio/ice_spear.mp3","url":"incanto/assets/audio/ice_spear.mp3","kind":"audio","bytes":21315,"description":"Sharp piercing ice projectile fire SFX."},{"name":"leaves_ash","file":"vegetation/ash_color.png","url":"https://agent8-games.verse8.io/assets/3D/default/textures/vegetation/ash_color.png","kind":"foliage","bytes":181423,"description":"Ash leaf-cluster alpha-cutout texture (1024px) from ez-tree by Dan Greenheck (MIT). Tree3D samples it for broadleaf/dead ash variants by default; copy it next to your game and set leafTexture to serve offline."},{"name":"leaves_aspen","file":"vegetation/aspen_color.png","url":"https://agent8-games.verse8.io/assets/3D/default/textures/vegetation/aspen_color.png","kind":"foliage","bytes":142116,"description":"Aspen leaf-cluster alpha-cutout texture (1024px) from ez-tree by Dan Greenheck (MIT). Tree3D samples it for high-tier broadleaf aspen variants; copy + leafTexture for offline serving."},{"name":"leaves_oak","file":"vegetation/oak_color.png","url":"https://agent8-games.verse8.io/assets/3D/default/textures/vegetation/oak_color.png","kind":"foliage","bytes":238115,"description":"Oak leaf-cluster alpha-cutout texture (1024px) from ez-tree by Dan Greenheck (MIT). Tree3D samples it for broadleaf oak variants by default; copy + leafTexture for offline serving."},{"name":"leaves_pine","file":"vegetation/pine_color.png","url":"https://agent8-games.verse8.io/assets/3D/default/textures/vegetation/pine_color.png","kind":"foliage","bytes":303684,"description":"Pine needle-cluster alpha-cutout texture (1024px) from ez-tree by Dan Greenheck (MIT). Tree3D samples it for conifer variants by default; copy + leafTexture for offline serving."},{"name":"locked_item_box","file":"items/locked_item_box.png","url":"incanto/assets/items/locked_item_box.png","kind":"item","bytes":11074,"description":"Locked item box sprite for Dungeons and Dungeoners. Container sprite requiring key or lockpick to open, containing valuable rewards."},{"name":"map","file":"items/map.png","url":"incanto/assets/items/map.png","kind":"item","bytes":3754,"description":"Map item sprite for Dungeons and Dungeoners. Navigation item revealing dungeon layout or providing exploration assistance."},{"name":"medieval-knight","file":"characters/medieval-knight.png","url":"incanto/assets/characters/medieval-knight.png","kind":"character","bytes":84367,"description":"(frame size 192x192) Using a medieval-themed SD(Super Deformed) knight sprite sheet image, you can apply idle, move, and attack animations, among others.","animation":"characters/medieval-knight.json","frameWidth":192,"frameHeight":192},{"name":"minecraft-tiles","file":"tiles/minecraft-tiles.png","url":"incanto/assets/tiles/minecraft-tiles.png","kind":"tile","bytes":10511,"description":"Minecraft-themed tiles sprite sheet image (frame size 16x16)"},{"name":"monster-died","file":"audio/monster_died.mp3","url":"incanto/assets/audio/monster_died.mp3","kind":"audio","bytes":16836,"description":"Enemy defeat / death SFX."},{"name":"resurrection_potion","file":"items/resurrection_potion.png","url":"incanto/assets/items/resurrection_potion.png","kind":"item","bytes":3471,"description":"Resurrection potion item sprite for Dungeons and Dungeoners. Rare consumable that revives fallen party members or prevents death."},{"name":"slash","file":"audio/slash.mp3","url":"incanto/assets/audio/slash.mp3","kind":"audio","bytes":10425,"description":"Quick sword swing / melee slash SFX."},{"name":"smite","file":"audio/smite.mp3","url":"incanto/assets/audio/smite.mp3","kind":"audio","bytes":12956,"description":"Powerful holy downward-strike SFX."},{"name":"spells-cast","file":"audio/spells_cast.mp3","url":"incanto/assets/audio/spells_cast.mp3","kind":"audio","bytes":22151,"description":"Generic magical spell-cast / chant SFX."},{"name":"super_box","file":"items/super_box.png","url":"incanto/assets/items/super_box.png","kind":"item","bytes":12184,"description":"Super item box sprite for Dungeons and Dungeoners. Premium container sprite containing rare or powerful items and equipment."},{"name":"swoosh","file":"effects/swoosh.png","url":"incanto/assets/effects/swoosh.png","kind":"effect","bytes":2599,"description":"Swoosh effect sprite for Dungeons and Dungeoners. Motion blur or attack trail effect used for melee attacks, sword slashes, or fast movement animations."},{"name":"trap","file":"items/trap.png","url":"incanto/assets/items/trap.png","kind":"item","bytes":3969,"description":"Trap object sprite for Dungeons and Dungeoners. Hazard sprite that damages players when triggered in dungeon exploration."},{"name":"ui-click","file":"audio/ui_click.wav","url":"incanto/assets/audio/ui_click.wav","kind":"audio","bytes":17332,"description":"Short tactile UI click for menus and buttons."},{"name":"walk","file":"audio/walk.mp3","url":"incanto/assets/audio/walk.mp3","kind":"audio","bytes":5642,"description":"Single footstep SFX for character movement (loop or one-shot)."},{"name":"wall00","file":"tiles/wall00.jpg","url":"incanto/assets/tiles/wall00.jpg","kind":"tile","bytes":44344,"description":"Basic wall tile texture for Dungeons and Dungeoners project, suitable for dungeon vertical boundaries."}]');function HA(e){return(e.kind===`foliage`||e.kind===`tile`)&&!e.animation}function UA(e,t,n){if(e.textContent=``,t.selection===null){GA(e,t,n);return}let r=t.nodeAt(t.selection);r&&ZA(e,t,r,n)}var WA=[`input`,`multiplayer`];function GA(e,t,n){e.appendChild(Sj(`scene`)),e.appendChild(Ej(`name`,String(t.working.name??``),e=>{t.mutate(()=>{t.working.name=e})}));let r=document.createElement(`select`);for(let e of[`2d`,`3d`]){let n=document.createElement(`option`);n.value=e,n.textContent=e,(t.working.dimension??`2d`)===e&&(n.selected=!0),r.appendChild(n)}r.addEventListener(`change`,()=>{t.mutate(()=>{t.working.dimension=r.value})}),e.appendChild(wj(`dimension`,r)),e.appendChild(Sj(`physics`,`physics`));let i=(t.working.physics??{}).gravity??(t.working.dimension===`3d`?[0,-9.81,0]:[0,980]),a=t.working.dimension===`3d`?[0,-9.81,0]:[0,980];e.appendChild(Oj(`gravity`,i,e=>{t.mutate(()=>{if(JSON.stringify(e)===JSON.stringify(a)){let e={...t.working.physics};delete e.gravity,Object.keys(e).length===0?delete t.working.physics:t.working.physics=e}else t.working.physics={...t.working.physics,gravity:e}})})),e.appendChild(Sj(`environment`)),KA(e,t),e.appendChild(Sj(`constants`)),gj(e,t,n),e.appendChild(Sj(`advanced`));for(let n of WA)e.appendChild(kj(n,t.working[n],e=>{t.mutate(()=>{e===void 0?delete t.working[n]:t.working[n]=e})}))}function KA(e,t){let n=t.working.environment??{},r=e=>{t.mutate(()=>{let n={...t.working.environment??{},...e};for(let e of Object.keys(n))n[e]===void 0&&delete n[e];Object.keys(n).length===0?delete t.working.environment:t.working.environment=n})};e.appendChild(qA(`background`,n.background??``,e=>{r({background:e===``?void 0:e})})),e.appendChild(qA(`ambient color`,n.ambient?.color??``,e=>{let t={...n.ambient,color:e===``?void 0:e};t.color===void 0&&delete t.color,r({ambient:Object.keys(t).length?t:void 0})})),e.appendChild(Dj(`ambient power`,n.ambient?.intensity??0,e=>{r({ambient:{...n.ambient,intensity:e}})}));let i=n.rendering??{},a=e=>{let t={...i,...e};for(let e of Object.keys(t))t[e]===void 0&&delete t[e];r({rendering:Object.keys(t).length?t:void 0})},o=document.createElement(`input`);o.type=`checkbox`,o.checked=i.antialias!==!1,o.addEventListener(`change`,()=>{a({antialias:o.checked?void 0:!1})}),e.appendChild(wj(`antialias`,o));let s=document.createElement(`select`);for(let[e,t]of[[``,`1 — soft hiDPI upscale (default)`],[`device`,`device — crisp retina`],[`2`,`2`]]){let n=document.createElement(`option`);n.value=e,n.textContent=t,String(i.pixelRatio??``)===e&&(n.selected=!0),s.appendChild(n)}s.addEventListener(`change`,()=>{a({pixelRatio:s.value===``?void 0:s.value===`device`?`device`:Number(s.value)})}),e.appendChild(wj(`pixel ratio`,s))}function qA(e,t,n){let r=document.createElement(`div`);r.className=`color-row`;let i=document.createElement(`input`);i.type=`color`,i.value=/^#[0-9a-fA-F]{6}$/.test(t)?t:`#000000`;let a=document.createElement(`input`);return a.type=`text`,a.className=`mono`,a.placeholder=`unset`,a.value=t,i.addEventListener(`input`,()=>{a.value=i.value}),i.addEventListener(`change`,()=>n(i.value)),a.addEventListener(`change`,()=>n(a.value.trim())),r.append(i,a),wj(e,r)}function JA(e,t){let n=document.createElement(`div`);n.className=`subcard`;let r=document.createElement(`div`);r.className=`subcard-head`,r.textContent=`underwater`,n.appendChild(r);let i=t.props?.underwater,a=i!==!1,o=i&&typeof i==`object`&&!Array.isArray(i)?i:{},s=n=>{e.mutate(()=>{t.props||={},n===!0?delete t.props.underwater:t.props.underwater=n,Object.keys(t.props).length===0&&delete t.props})},c=(e,t)=>{let n={...o};t===void 0||t===``?delete n[e]:n[e]=t,s(Object.keys(n).length===0?!0:n)},l=document.createElement(`input`);if(l.type=`checkbox`,l.checked=a,l.addEventListener(`change`,()=>s(l.checked)),n.appendChild(wj(`enabled`,l)),a){n.appendChild(qA(`murk color`,String(o.color??``),e=>c(`color`,e))),n.appendChild(Dj(`visibility (m)`,Number(o.visibility??22),e=>c(`visibility`,e===22?void 0:e),()=>c(`visibility`,void 0)));let e=document.createElement(`input`);e.type=`checkbox`,e.checked=(o.caustics??!0)!==!1,e.addEventListener(`change`,()=>c(`caustics`,e.checked?void 0:!1)),n.appendChild(wj(`caustics`,e))}return n}function YA(e,t){let n=document.createElement(`div`);n.className=`subcard`;let r=document.createElement(`div`);r.className=`subcard-head`,r.textContent=`material`,n.appendChild(r);let i=t.props?.material??{},a=(n,r)=>{let i={...t.props?.material??{}};r===void 0||r===``?delete i[n]:i[n]=r;let a=Object.keys(i).length===0?void 0:i;e.mutate(()=>{t.props||={},a===void 0?(delete t.props.material,Object.keys(t.props).length===0&&delete t.props):t.props.material=a},e.selection?{path:e.selection,key:`material`,value:a}:void 0)};n.appendChild(qA(`color`,String(i.color??``),e=>a(`color`,e))),n.appendChild(XA(`roughness`,Number(i.roughness??1),e=>a(`roughness`,e))),n.appendChild(XA(`metalness`,Number(i.metalness??0),e=>a(`metalness`,e))),n.appendChild(qA(`emissive`,String(i.emissive??``),e=>a(`emissive`,e))),n.appendChild(XA(`emissive power`,Number(i.emissiveIntensity??1),e=>a(`emissiveIntensity`,e===1?void 0:e),2)),n.appendChild(XA(`opacity`,Number(i.opacity??1),e=>a(`opacity`,e===1?void 0:e)));let o=document.createElement(`input`);o.type=`checkbox`,o.checked=i.wireframe===!0,o.addEventListener(`change`,()=>a(`wireframe`,o.checked||void 0)),n.appendChild(wj(`wireframe`,o));let s=document.createElement(`input`);s.type=`checkbox`,s.checked=i.flatShading===!0,s.addEventListener(`change`,()=>a(`flatShading`,s.checked||void 0)),n.appendChild(wj(`flatShading`,s)),n.appendChild(sj(`map (texture)`,String(i.map??``),cj(e=>HA(e)&&!e.name.includes(`normal`)&&!e.name.includes(`roughness`)),e=>a(`map`,e.trim()),{placeholder:`built-in or custom URL`})),n.appendChild(sj(`normal map`,String(i.normalMap??``),cj(e=>HA(e)&&e.name.includes(`normal`)),e=>a(`normalMap`,e.trim()),{placeholder:`built-in or custom URL`}));let c=!!(i.map||i.normalMap),l=Array.isArray(i.repeat)?i.repeat:[1,1],u=Oj(`repeat [u,v]`,[l[0]??1,l[1]??1],e=>{let[t,n]=e;!Number.isFinite(t)||!Number.isFinite(n)||t===1&&n===1||!t&&!n?a(`repeat`,void 0):a(`repeat`,[t,n])});return u.title=c?`Texture tiling across the mesh UVs.`:`Only takes effect with a map/normal map set (engine validates).`,n.appendChild(u),n}function XA(e,t,n,r=1){let i=document.createElement(`div`);i.className=`slider-row`;let a=document.createElement(`input`);a.type=`range`,a.min=`0`,a.max=String(r),a.step=`0.05`,a.value=String(Number.isFinite(t)?t:0);let o=document.createElement(`input`);return o.type=`number`,o.step=`0.05`,o.min=`0`,o.max=String(r),o.value=a.value,a.addEventListener(`input`,()=>{o.value=a.value}),a.addEventListener(`change`,()=>n(Number(a.value))),o.addEventListener(`change`,()=>{a.value=o.value,n(Number(o.value))}),i.append(a,o),wj(e,i)}function ZA(e,t,n,r){if(e.appendChild(Sj(n.type??`instance`)),e.appendChild(QA(n)),e.appendChild(Ej(`name`,n.name??``,e=>{e.trim()!==``&&t.mutate(()=>{n.name=e.trim()})})),n.type){let i;try{i=fe(n.type)}catch{i={}}for(let[a,o]of Object.entries(i))if(a===`collider`)e.appendChild(nj(t,n,o.default));else if(a===`material`)e.appendChild(YA(t,n));else if(n.type===`ModelInstance3D`&&a===`model`&&r)e.appendChild(aj(`model`,String(n.props?.model??``),r.modelRefs(),e=>rj(t,n,`model`,e,``)));else if(n.type===`ModelInstance3D`&&a===`animation`&&r)e.appendChild(aj(`animation`,String(n.props?.animation??``),r.animationsForSelection(),e=>rj(t,n,`animation`,e,``)));else if(n.type===`Tree3D`&&a===`leafTexture`)e.appendChild(sj(`leaf texture`,String(n.props?.leafTexture??``),cj(e=>e.name.startsWith(`leaves_`)),e=>rj(t,n,`leafTexture`,e.trim(),``),{placeholder:`default (per-type) or URL`}));else if(n.type===`Terrain3D`&&a===`textureBase`)e.appendChild(sj(`texture base`,String(n.props?.textureBase??o.default??``),uj,e=>rj(t,n,`textureBase`,e.trim(),o.default),{placeholder:`splat texture base URL`}));else if(n.type===`AnimatedSprite2D`&&a===`sheet`||n.type===`Sprite2D`&&a===`texture`){let i=a===`sheet`;e.appendChild(dj(t,n,a,r,i))}else n.type===`AudioPlayer`&&a===`src`?e.appendChild(sj(`src (audio file)`,String(n.props?.src??``),lj(),e=>rj(t,n,`src`,e.trim(),``),{placeholder:`built-in or custom URL — or use preset`,note:"For zero-asset SFX, set the `preset` prop instead of src."})):n.type===`Flowers3D`&&a===`varieties`?e.appendChild(fj(t,n)):n.type===`Water3D`&&a===`underwater`?e.appendChild(JA(t,n)):e.appendChild(vj(t,n,a,o.default,o.options))}e.appendChild(Sj(`structure`)),e.appendChild($A(t,n)),e.appendChild(ej(t,n)),e.appendChild(tj(t,n))}function QA(e){let t=document.createElement(`div`);t.className=`field uid-line`;let n=document.createElement(`span`);n.textContent=`uid`;let r=document.createElement(`div`);r.className=`uid-value`;let i=document.createElement(`code`);i.textContent=String(e.uid??``);let a=document.createElement(`button`);return a.type=`button`,a.className=`uid-copy`,a.title=`Copy uid`,a.innerHTML=`<svg aria-hidden="true" width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>`,a.addEventListener(`click`,()=>{navigator.clipboard?.writeText(String(e.uid??``)),a.classList.add(`copied`),a.innerHTML=`✓`,setTimeout(()=>{a.classList.remove(`copied`),a.innerHTML=`<svg aria-hidden="true" width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>`},800)}),r.append(i,a),t.append(n,r),t}function $A(e,t){let n=document.createElement(`label`);n.className=`field wide`;let r=document.createElement(`span`);r.append(`groups `,BA(`groups`));let i=document.createElement(`div`);i.className=`chips`;for(let n of t.groups??[])i.appendChild(Cj(n,()=>{e.mutate(()=>{t.groups=(t.groups??[]).filter(e=>e!==n),t.groups.length===0&&delete t.groups})}));let a=document.createElement(`input`);return a.placeholder=(t.groups?.length??0)===0?`add a tag… (e.g. coins)`:``,a.addEventListener(`keydown`,n=>{if(n.key!==`Enter`)return;let r=a.value.trim();r&&e.mutate(()=>{t.groups=[...t.groups??[],r]})}),i.appendChild(a),i.addEventListener(`click`,()=>a.focus()),n.append(r,i),n}function ej(e,t){let n=document.createElement(`div`);n.className=`subcard`;let r=document.createElement(`div`);r.className=`subcard-head`,r.append(`script `,BA(`script`));let i=document.createElement(`span`);i.className=`spacer`,r.appendChild(i);let a=t.script;if(a){let n=document.createElement(`button`);n.type=`button`,n.className=`linklike`,n.textContent=`detach`,n.addEventListener(`click`,()=>{e.mutate(()=>{delete t.script})}),r.appendChild(n)}if(n.appendChild(r),!a){let r=document.createElement(`div`);r.className=`muted-note`,r.textContent=`No behavior attached. Behaviors are TypeScript classes in your game.`;let i=document.createElement(`button`);return i.type=`button`,i.className=`ghost`,i.textContent=`+ attach behavior`,i.addEventListener(`click`,()=>{e.mutate(()=>{t.script={name:`MyBehavior`}})}),n.append(r,i),n}n.appendChild(Ej(`name`,a.name??``,n=>{e.mutate(()=>{t.script.name=n.trim()})})),n.appendChild(kj(`props`,a.props,n=>{e.mutate(()=>{n===void 0?delete t.script.props:t.script.props=n})}));let o=document.createElement(`div`);return o.className=`muted-note`,o.textContent=`? has copy-paste boilerplate for the game side.`,n.appendChild(o),n}function tj(e,t){let n=document.createElement(`div`);n.className=`subcard`;let r=document.createElement(`div`);r.className=`subcard-head`,r.append(`network `,BA(`network`)),n.appendChild(r);let i=t.network,a=document.createElement(`select`);for(let[e,t]of[[``,`not replicated`],[`owner`,`owner — this player broadcasts it`]]){let n=document.createElement(`option`);n.value=e,n.textContent=t,(i?.mode??``)===e&&(n.selected=!0),a.appendChild(n)}if(a.addEventListener(`change`,()=>{e.mutate(()=>{a.value===``?delete t.network:t.network={mode:a.value,sync:i?.sync??[`position`]}})}),n.appendChild(wj(`mode`,a)),i?.mode===`owner`){let r=document.createElement(`div`);r.className=`chips`;for(let n of i.sync??[])r.appendChild(Cj(n,()=>{e.mutate(()=>{t.network.sync=(i.sync??[]).filter(e=>e!==n)})}));let a=document.createElement(`input`);a.placeholder=`position · Skin.animation…`,a.addEventListener(`keydown`,n=>{if(n.key!==`Enter`)return;let r=a.value.trim();r&&e.mutate(()=>{t.network.sync=[...i.sync??[],r]})}),r.appendChild(a);let o=document.createElement(`label`);o.className=`field wide`;let s=document.createElement(`span`);s.textContent=`sync keys`,o.append(s,r),n.appendChild(o),n.appendChild(Dj(`throttle ms`,i.throttleMs??50,n=>{e.mutate(()=>{t.network.throttleMs=n})}))}return n}function nj(e,t,n){let r=document.createElement(`div`);r.className=`subcard`;let i=document.createElement(`div`);i.className=`subcard-head`,i.append(`collider `,BA(`collider`)),r.appendChild(i);let a=t.props?.collider??{},o=n=>{e.mutate(()=>{t.props||={};let e=t.props;Object.keys(n).length===0?(delete e.collider,Object.keys(e).length===0&&delete t.props):e.collider=n})},s=(t.type??``).endsWith(`3D`),c=s?[[``,`none`],[`box`,`box — crates, floors`],[`sphere`,`sphere — balls, pickups`],[`capsule`,`capsule — characters`]]:[[``,`none`],[`rect`,`rect — boxes, platforms`],[`circle`,`circle — coins, balls`],[`capsule`,`capsule — characters`]],l=document.createElement(`select`);for(let[e,t]of c){let n=document.createElement(`option`);n.value=e,n.textContent=t,(a.shape??``)===e&&(n.selected=!0),l.appendChild(n)}return l.addEventListener(`change`,()=>{l.value===``?o({}):l.value===`rect`?o({shape:`rect`,size:a.size??[32,32]}):l.value===`box`?o({shape:`box`,size:a.size??[1,1,1]}):l.value===`circle`?o({shape:`circle`,radius:a.radius??16}):l.value===`sphere`?o({shape:`sphere`,radius:a.radius??.5}):o(s?{shape:`capsule`,radius:a.radius??.4,height:a.height??1}:{shape:`capsule`,radius:a.radius??12,height:a.height??16})}),r.appendChild(wj(`shape`,l)),a.shape===`rect`||a.shape===`box`?r.appendChild(Oj(`size`,a.size??(s?[1,1,1]:[32,32]),e=>o({...a,size:e}))):a.shape===`circle`||a.shape===`sphere`?r.appendChild(Dj(`radius`,a.radius??(s?.5:16),e=>o({...a,radius:e}))):a.shape===`capsule`&&(r.appendChild(Dj(`radius`,a.radius??12,e=>o({...a,radius:e}))),r.appendChild(Dj(`height`,a.height??16,e=>o({...a,height:e})))),a.shape&&r.appendChild(Oj(`offset`,a.offset??(s?[0,0,0]:[0,0]),e=>o({...a,offset:e}))),r}function rj(e,t,n,r,i){e.mutate(()=>{t.props||={},JSON.stringify(r)===JSON.stringify(i)?(delete t.props[n],Object.keys(t.props).length===0&&delete t.props):t.props[n]=r})}var ij=0;function aj(e,t,n,r){let i=document.createElement(`input`);i.type=`text`,i.value=t;let a=`suggest-${ij++}`;i.setAttribute(`list`,a);let o=document.createElement(`datalist`);o.id=a;for(let e of n){let t=document.createElement(`option`);t.value=e,o.appendChild(t)}i.addEventListener(`change`,()=>r(i.value.trim()));let s=wj(e,i);return s.appendChild(o),s}var oj=0;function sj(e,t,n,r,i={}){let a=document.createElement(`label`);a.className=`field wide asset-field`;let o=document.createElement(`span`);o.textContent=e,a.appendChild(o);let s=document.createElement(`input`);s.type=`text`,s.value=t,i.placeholder&&(s.placeholder=i.placeholder),s.addEventListener(`change`,()=>r(s.value.trim()));let c=document.createElement(`button`);c.type=`button`,c.className=`asset-toggle`,c.title=`Browse built-in assets`,c.textContent=`▾`;let l=document.createElement(`div`);l.className=`asset-control`,l.append(s,c),a.appendChild(l);let u=document.createElement(`div`);u.className=`asset-panel`,u.hidden=!0;let d=document.createElement(`input`);d.type=`text`,d.className=`asset-search`,d.placeholder=`search ${n.length} built-in${n.length===1?``:`s`}…`;let f=document.createElement(`div`);f.className=`asset-options`,u.append(d,f),a.appendChild(u);let p=e=>{f.textContent=``;let t=e.trim().toLowerCase(),i=n.filter(e=>t===``||e.label.toLowerCase().includes(t)||(e.hint?.toLowerCase().includes(t)??!1)||e.value.toLowerCase().includes(t));if(i.length===0){let e=document.createElement(`div`);e.className=`asset-empty`,e.textContent=`no match — type a custom URL above`,f.appendChild(e);return}for(let e of i){let t=document.createElement(`button`);t.type=`button`,t.className=`asset-option`;let n=document.createElement(`strong`);if(n.textContent=e.label,t.appendChild(n),e.hint){let n=document.createElement(`small`);n.textContent=e.hint,t.appendChild(n)}t.addEventListener(`click`,()=>{s.value=e.value,u.hidden=!0,r(e.value)}),f.appendChild(t)}};if(c.addEventListener(`click`,()=>{u.hidden=!u.hidden,u.hidden||(p(d.value),d.focus())}),d.addEventListener(`input`,()=>p(d.value)),d.addEventListener(`keydown`,e=>{e.key===`Escape`&&(u.hidden=!0)}),i.note){let e=document.createElement(`div`);e.className=`muted-note asset-note`,e.textContent=i.note,a.appendChild(e)}return s.id=`asset-input-${oj++}`,a.htmlFor=s.id,a}function cj(e){return VA.filter(e).map(e=>({label:e.name,value:e.url,hint:`${e.kind} · ${e.description.slice(0,70)}`}))}function lj(){return VA.filter(e=>e.kind===`audio`).map(e=>({label:e.name,value:e.url,hint:`audio · ${e.description.slice(0,70)}`}))}var uj=[{label:`agent8 default terrain`,value:`https://agent8-games.verse8.io/assets/3D/default/textures/terrain`,hint:`sand/grass/stone/snow splat set (Terrain3D default)`}];function dj(e,t,n,r,i){let a=String(t.props?.[n]??``),o=i?[`spritesheet`]:[`texture`,`spritesheet`],s=(r?.assetRefs?.(o)??[]).map(e=>({label:e,value:e,hint:`scene asset`})),c=VA.filter(e=>i?!!e.animation:e.kind===`character`||e.kind===`item`||e.kind===`tile`);for(let e of c)s.push({label:`built-in: ${e.name}`,value:`$${e.name}`,hint:`${e.kind} · creates a scene asset (${e.animation?`spritesheet`:`texture`})`});return sj(i?`sheet`:`texture`,a,s,i=>{let a=i.startsWith(`$`)&&c.find(e=>`$${e.name}`===i);if(a&&r?.addAsset){let i={type:a.animation?`spritesheet`:`texture`,url:a.url};if(a.animation){let e=a.frameWidth,t=a.frameHeight;e&&(i.frameWidth=e),t&&(i.frameHeight=t)}rj(e,t,n,`$${r.addAsset(a.name,i).replace(/^\$/,``)}`,``);return}rj(e,t,n,i.trim(),``)},{placeholder:`$assetKey`,note:"Picks a $asset ref. Built-ins create the scene asset; packaged sprites need `incanto-assets copy` (or a bundler import) to serve at runtime."})}function fj(e,t){let n=document.createElement(`label`);n.className=`field wide`;let r=document.createElement(`span`);r.textContent=`varieties`,n.appendChild(r);let i=new Set(Array.isArray(t.props?.varieties)?t.props.varieties:[]),a=document.createElement(`div`);a.className=`chips varieties`;let o=()=>{let n=Zb.filter(e=>i.has(e));rj(e,t,`varieties`,n.length===0||n.length===Zb.length?[]:n,[])};for(let e of Zb){let t=document.createElement(`label`);t.className=`variety-opt`;let n=document.createElement(`input`);n.type=`checkbox`,n.checked=i.size===0||i.has(e),n.addEventListener(`change`,()=>{if(i.size===0)for(let e of Zb)i.add(e);n.checked?i.add(e):i.delete(e),o()});let r=document.createElement(`span`);r.textContent=e,t.append(n,r),a.appendChild(t)}n.appendChild(a);let s=document.createElement(`div`);return s.className=`muted-note`,s.textContent=`All (or none) = the default mix of all three.`,n.appendChild(s),n}var pj=[[`number`,()=>0],[`text`,()=>``],[`boolean`,()=>!1],[`color`,()=>`#ffffff`],[`vec2`,()=>[0,0]],[`vec3`,()=>[0,0,0]]];function mj(e,t){let n=0,r=e=>{if(Ye(e)){e[`@const`]===t&&(n+=1);return}if(Array.isArray(e))for(let t of e)r(t);else if(e&&typeof e==`object`)for(let t of Object.values(e))r(t)};for(let[t,n]of Object.entries(e))t!==`constants`&&r(n);return n}function hj(e,t,n){let r=e=>{if(Ye(e))return e[`@const`]===t?JSON.parse(JSON.stringify(n??null)):e;if(Array.isArray(e))return e.map(r);if(e&&typeof e==`object`){let t=e;for(let e of Object.keys(t))t[e]=r(t[e]);return t}return e};for(let t of Object.keys(e))t!==`constants`&&(e[t]=r(e[t]))}function gj(e,t,n){let r=t.working.constants??{},i=Object.keys(r),a=(e,n)=>{t.mutate(()=>{let r={...t.working.constants??{}};r[e]=n,t.working.constants=r})},o=(e,n)=>{t.mutate(()=>{n&&hj(t.working,e,r[e]);let i={...t.working.constants??{}};delete i[e],Object.keys(i).length===0?delete t.working.constants:t.working.constants=i})},s=e=>{let r=mj(t.working,e);if(r>0&&n?.confirm){n.confirm(`"${e}" is used by ${r} prop${r===1?``:`s`}. Unlink them (inline its current value) and delete?`,`unlink & delete`,()=>o(e,!0));return}o(e,r>0)};if(i.length===0){let t=document.createElement(`div`);t.className=`hint`,t.textContent=`No constants yet. Add one, then bind props to it with the 🔗 picker.`,e.appendChild(t)}for(let t of i){let n=document.createElement(`div`);n.className=`const-row`,n.appendChild(_j(t,r[t],e=>a(t,e)));let i=document.createElement(`button`);i.type=`button`,i.className=`const-del`,i.textContent=`✕`,i.title=`delete constant "${t}"`,i.addEventListener(`click`,()=>s(t)),n.appendChild(i),e.appendChild(n)}let c=document.createElement(`div`);c.className=`const-add`;let l=document.createElement(`input`);l.type=`text`,l.placeholder=`new constant name`;let u=document.createElement(`select`);for(let[e]of pj){let t=document.createElement(`option`);t.value=e,t.textContent=e,u.appendChild(t)}let d=document.createElement(`button`);d.type=`button`,d.textContent=`+ add`,d.addEventListener(`click`,()=>{let e=l.value.trim();e&&(t.working.constants??{})[e]===void 0&&(a(e,(pj.find(([e])=>e===u.value)?.[1]??(()=>0))()),l.value=``)}),c.append(l,u,d),e.appendChild(c)}function _j(e,t,n){let r=oe(t);if(r===`number`)return Dj(e,t,e=>n(e),()=>n(0));if(r===`boolean`){let r=document.createElement(`input`);return r.type=`checkbox`,r.checked=t===!0,r.addEventListener(`change`,()=>n(r.checked)),wj(e,r)}return r===`array`&&Array.isArray(t)&&t.every(e=>typeof e==`number`)?Oj(e,t,e=>n(e)):r===`string`?Ej(e,String(t),e=>n(e)):kj(e,t,e=>n(e??null),JSON.stringify(t))}function vj(e,t,n,r,i){let a=t.props?.[n]??r,o=oe(r),s=o===`number`||o===`boolean`||o===`string`||o===`array`,c=i=>{let a=Ye(i);e.mutate(()=>{t.props||={};let e=t.props;JSON.stringify(i)===JSON.stringify(r)?(delete e[n],Object.keys(e).length===0&&delete t.props):e[n]=i},e.selection&&s&&!a?{path:e.selection,key:n,value:i}:void 0)},l=yj(e,o,r);if(Ye(a))return bj(n,a[`@const`],l,c,r);let u=e=>l.length>0?xj(e,l,c):e;if(o===`number`)return u(Dj(n,a,e=>c(e),()=>c(r)));if(o===`boolean`){let e=document.createElement(`input`);return e.type=`checkbox`,e.checked=a===!0,e.addEventListener(`change`,()=>c(e.checked)),u(wj(n,e))}return o===`string`?i&&i.length>0?u(Tj(n,String(a??``),i,e=>c(e))):u(Ej(n,String(a??``),e=>c(e))):o===`array`&&Array.isArray(r)&&r.length>0&&r.every(e=>typeof e==`number`)?u(Oj(n,a??r,e=>c(e))):kj(n,a===r?void 0:a,e=>{c(e===void 0?r:e)},JSON.stringify(r))}function yj(e,t,n){let r=e.working.constants??{};return Object.keys(r).filter(e=>{let i=r[e];return oe(i)===t?t===`array`&&Array.isArray(n)&&Array.isArray(i)?i.length===n.length:!0:!1})}function bj(e,t,n,r,i){let a=document.createElement(`select`),o=n.includes(t)?n:[t,...n];for(let e of o){let n=document.createElement(`option`);n.value=e,n.textContent=e,e===t&&(n.selected=!0),a.appendChild(n)}let s=document.createElement(`option`);s.value=``,s.textContent=`↺ custom value`,a.appendChild(s),a.addEventListener(`change`,()=>{r(a.value?{"@const":a.value}:i)});let c=wj(`🔗 ${e}`,a);return c.classList.add(`const-bound`),c}function xj(e,t,n){let r=document.createElement(`select`);r.className=`const-picker`,r.title=`Bind to a named constant`;let i=document.createElement(`option`);i.value=``,i.textContent=`🔗`,r.appendChild(i);for(let e of t){let t=document.createElement(`option`);t.value=e,t.textContent=e,r.appendChild(t)}return r.value=``,r.addEventListener(`change`,()=>{r.value&&n({"@const":r.value})}),e.appendChild(r),e}function Sj(e,t){let n=document.createElement(`div`);n.className=`section-title`,n.textContent=e,t&&n.appendChild(BA(t));let r=document.createElement(`span`);return r.className=`rule`,n.appendChild(r),n}function Cj(e,t){let n=document.createElement(`span`);n.className=`chip`,n.textContent=e;let r=document.createElement(`button`);return r.type=`button`,r.textContent=`✕`,r.addEventListener(`click`,e=>{e.stopPropagation(),t()}),n.appendChild(r),n}function wj(e,t){let n=document.createElement(`label`);n.className=`field`;let r=document.createElement(`span`);return r.textContent=e,n.append(r,t),n}function Tj(e,t,n,r){let i=document.createElement(`select`);for(let e of n.includes(t)?n:[t,...n]){let n=document.createElement(`option`);n.value=e,n.textContent=e,e===t&&(n.selected=!0),i.appendChild(n)}return i.addEventListener(`change`,()=>r(i.value)),wj(e,i)}function Ej(e,t,n){let r=document.createElement(`input`);return r.type=`text`,r.value=t,r.addEventListener(`change`,()=>n(r.value)),wj(e,r)}function Dj(e,t,n,r){let i=document.createElement(`input`);return i.type=`number`,i.step=`any`,i.value=String(t),i.addEventListener(`change`,()=>{if(i.value.trim()===``){r?.();return}let e=Number(i.value);Number.isFinite(e)&&n(e)}),wj(e,i)}function Oj(e,t,n){let r=document.createElement(`div`);r.className=`vector-row`;let i=[...t];return i.forEach((e,t)=>{let a=document.createElement(`input`);a.type=`number`,a.step=`any`,a.value=String(e??0),a.addEventListener(`change`,()=>{let e=Number(a.value);Number.isFinite(e)&&(i[t]=e,n([...i]))}),r.appendChild(a)}),wj(e,r)}function kj(e,t,n,r=``){let i=document.createElement(`textarea`);i.rows=3,i.placeholder=r,i.value=t===void 0?``:JSON.stringify(t,null,2),i.addEventListener(`change`,()=>{let e=i.value.trim();if(e===``){i.classList.remove(`invalid`),n(void 0);return}try{let t=JSON.parse(e);i.classList.remove(`invalid`),n(t)}catch{i.classList.add(`invalid`)}});let a=wj(e,i);return a.className=`field wide`,a}var Aj=class{working;selection=[];extra=[];validator=null;selectedAsset=null;selectedGroup=null;pendingGroups=new Set;pendingLivePatch=null;addingAsset=!1;addingGroup=!1;newAssetGroup=``;newGroupParent=``;startAddingAsset(){this.addingAsset=!0,this.addingGroup=!1,this.newAssetGroup=this.selectedGroup??``,this.clearFocus()}startAddingGroup(){this.addingGroup=!0,this.addingAsset=!1,this.newGroupParent=this.selectedGroup??``,this.clearFocus()}cancelAddForms(){this.addingAsset=!1,this.addingGroup=!1}onError=null;original;undoStack=[];listeners=new Set;constructor(e){this.working=e,this.original=JSON.stringify(e),this.working.root&&jj(this.working.root)}get dirty(){return JSON.stringify(this.working)!==this.original}get canUndo(){return this.undoStack.length>0}nodeAt(e){if(e===null)return null;let t=this.working.root;for(let n of e)t=t?.children?.[n];return t??null}parentOf(e){return e.length===0?null:this.nodeAt(e.slice(0,-1))}mutate(e,t){this.undoStack.push(JSON.stringify(this.working)),this.undoStack.length>100&&this.undoStack.shift(),e(),this.pendingLivePatch=t??null,this.emit()}undo(){let e=this.undoStack.pop();e!==void 0&&(this.working=JSON.parse(e),this.selection=[],this.emit())}transact(e){let t=JSON.stringify(this.working),n=e(),r=e=>(this.working=JSON.parse(t),e&&this.onError?.(e),null);if(n===null)return r();let i=this.validator?.(this.working)??null;return i?r(i):(this.undoStack.push(t),this.undoStack.length>100&&this.undoStack.shift(),this.emit(),n)}moveNode(e,t,n){return this.moveNodes([e],t,n)?.[0]??null}moveNodes(e,t,n){let r=Ij(e).filter(e=>e.length>0);if(r.length===0)return null;for(let e of r)if(e.length<=t.length&&e.every((e,n)=>e===t[n]))return null;let i=this.working.root,a=r.map(e=>this.nodeAt(e)).filter(e=>e!==null);if(a.length!==r.length)return null;let o=this.nodeAt(t);return!o||a.includes(o)?null:this.transact(()=>{for(let e of a)Rj(i,e);let e,t;if(n===`into`)e=o,e.children||=[],t=e.children.length;else{let r=Lj(i,o);if(!r)return null;e=r.parent,t=r.index+ +(n===`after`)}let r=[];a.forEach((n,r)=>{n.name=Bj(e,n.name??`Node`),e.children?.splice(t+r,0,n)});for(let e of a){let t=zj(i,e);if(!t)return null;r.push(t)}return r})}duplicateNode(e){if(e.length===0)return null;let t=this.nodeAt(e),n=this.parentOf(e);return!t||!n?.children?null:this.transact(()=>{let r=JSON.parse(JSON.stringify(t));r.name=Bj(n,t.name??`Node`),Mj(r);let i=e[e.length-1]+1;return n.children?.splice(i,0,r),[...e.slice(0,-1),i]})}removeNodes(e){let t=Ij(e).filter(e=>e.length>0);if(t.length===0)return!1;let n=this.working.root,r=t.map(e=>this.nodeAt(e)).filter(e=>e!==null);return this.transact(()=>{for(let e of r)Rj(n,e);return!0})===!0}deleteRoot(){this.undoStack.push(JSON.stringify(this.working)),delete this.working.root,this.selection=null,this.extra=[],this.emit()}insertNode(e,t){if(!this.working.root){let t=JSON.parse(JSON.stringify(e));return Mj(t),this.transact(()=>(this.working.root=t,[]))}let n=this.nodeAt(t);return n?this.transact(()=>{let r=JSON.parse(JSON.stringify(e));return r.name=Bj(n,r.name??`Node`),Mj(r),n.children||=[],n.children.push(r),[...t,n.children.length-1]}):null}findRemovalReferences(e){let t=Ij(e).filter(e=>e.length>0),n=this.working.root,r=t.map(e=>this.nodeAt(e)).filter(e=>e!==null),i=new Set,a=[];for(let e of t){let t=[],r=n;for(let n of e){let e=r.children?.[n];if(!e)break;t.push(String(e.name)),r=e}a.push(t.join(`/`))}let o=e=>{typeof e.uid==`string`&&i.add(e.uid);for(let t of e.children??[])o(t)};for(let e of r)o(e);let s=[];if((this.working.connections??[]).forEach((e,t)=>{for(let n of[`from`,`to`]){let r=String(e[n]??``);if(a.some(e=>r===e||r.startsWith(`${e}/`))){s.push({kind:`connection`,where:`connections[${t}] ${String(e.signal)}: ${String(e.from)} → ${String(e.to)}`,connectionIndex:t});return}}}),i.size>0){let e=(t,n,a)=>{if(typeof t==`string`){!a&&i.has(t)&&s.push({kind:`uid`,where:n,value:t});return}if(Array.isArray(t)){t.forEach((t,r)=>{e(t,`${n}[${r}]`,a)});return}if(typeof t==`object`&&t){let i=t,o=a||r.includes(i),s=typeof i.name==`string`&&(i.type||i.children||i.instance)?n?`${n} › ${i.name}`:String(i.name):n;for(let[n,r]of Object.entries(t))n!==`uid`&&e(r,n===`children`||n===`root`?s:`${s?`${s}.`:``}${n}`,o)}};e(this.working,``,!1)}return s}removeNodesUnlinking(e){let t=this.findRemovalReferences(e),n=Ij(e).filter(e=>e.length>0),r=this.working.root,i=n.map(e=>this.nodeAt(e)).filter(e=>e!==null),a=new Set(t.filter(e=>e.kind===`uid`).map(e=>e.value)),o=new Set(t.filter(e=>e.kind===`connection`).map(e=>e.connectionIndex));return this.transact(()=>{o.size>0&&(this.working.connections=(this.working.connections??[]).filter((e,t)=>!o.has(t))),a.size>0&&Nj(this.working,a,i);for(let e of i)Rj(r,e);return!0})===!0}reset(e){this.working=e,this.original=JSON.stringify(e),this.working.root&&jj(this.working.root),this.undoStack=[],this.selection=[],this.emit()}markSaved(){this.original=JSON.stringify(this.working),this.emit()}emitChange(){this.emit()}clearFocus(){this.selection=null,this.extra=[],this.selectedAsset=null,this.selectedGroup=null,this.emit()}selectAsset(e){this.selectedAsset=e,e!==null&&(this.cancelAddForms(),this.selectedGroup=null,this.selection=null,this.extra=[]),this.emit()}selectGroup(e){this.selectedGroup=e,e!==null&&(this.cancelAddForms(),this.selectedAsset=null,this.selection=null,this.extra=[]),this.emit()}assetMap(){return this.working.assets??{}}groupCount(e){return Object.keys(this.assetMap()).filter(t=>t.startsWith(`${e}/`)).length}addGroup(e){this.pendingGroups.add(e),this.selectGroup(e)}renameGroup(e,t){if(!t||t===e)return!1;let n=this.assetMap(),r=Object.keys(n).filter(t=>t.startsWith(`${e}/`));return this.pendingGroups.delete(e)&&this.pendingGroups.add(t),this.selectedGroup=t,this.mutate(()=>{for(let i of r){let r=`${t}/${i.slice(e.length+1)}`;n[r]=n[i],delete n[i],Pj(this.working,`$${i}`,`$${r}`)}}),r.length===0&&this.emit(),!0}moveGroup(e,t){if(t===e||t.startsWith(`${e}/`))return!1;let n=e.includes(`/`)?e.slice(e.lastIndexOf(`/`)+1):e,r=t?`${t}/${n}`:n;return r===e?!1:this.renameGroup(e,r)}renameAssetKey(e,t){let n=this.assetMap();return!(e in n)||!t||t===e||t in n?!1:(this.selectedAsset=t,this.mutate(()=>{n[t]=n[e],delete n[e],Pj(this.working,`$${e}`,`$${t}`)}),!0)}deleteGroup(e){let t=this.assetMap(),n=Object.keys(t).filter(t=>t.startsWith(`${e}/`));n.length>0&&this.mutate(()=>{for(let e of n)delete t[e];Object.keys(t).length===0&&delete this.working.assets}),this.pendingGroups.delete(e),this.selectedGroup===e&&(this.selectedGroup=null),this.emit()}moveAsset(e,t){let n=this.assetMap();if(!(e in n))return!1;let r=e.includes(`/`)?e.slice(e.lastIndexOf(`/`)+1):e,i=t?`${t}/${r}`:r;if(i===e)return!1;for(;i in n;)i=`${i}2`;return this.renameAssetKey(e,i)}select(e,t){if(this.cancelAddForms(),this.selectedAsset=null,this.selectedGroup=null,t?.toggle&&e!==null){let t=e.join(`.`);this.selection!==null&&this.selection.join(`.`)===t?this.selection=this.extra.shift()??this.selection:this.extra.some(e=>e.join(`.`)===t)?this.extra=this.extra.filter(e=>e.join(`.`)!==t):this.selection===null?this.selection=e:this.extra.push(e)}else this.selection=e,this.extra=[];this.emit()}allSelections(){return[...this.selection===null?[]:[this.selection,...this.extra]].sort(Fj)}isSelected(e){let t=e.join(`.`);return this.selection!==null&&this.selection.join(`.`)===t||this.extra.some(e=>e.join(`.`)===t)}onChange(e){this.listeners.add(e)}emit(){for(let e of this.listeners)e()}};function jj(e){(typeof e.uid!=`string`||e.uid===``)&&(e.uid=vt());for(let t of e.children??[])jj(t)}function Mj(e){e.uid=vt();for(let t of e.children??[])Mj(t)}function Nj(e,t,n){if(Array.isArray(e)){for(let r=e.length-1;r>=0;r--)typeof e[r]==`string`&&t.has(e[r])?e.splice(r,1):Nj(e[r],t,n);return}if(typeof e==`object`&&e){if(n.includes(e))return;for(let[r,i]of Object.entries(e))r!==`uid`&&(typeof i==`string`&&t.has(i)?delete e[r]:Nj(i,t,n))}}function Pj(e,t,n){if(Array.isArray(e)){e.forEach((r,i)=>{r===t?e[i]=n:Pj(r,t,n)});return}if(typeof e==`object`&&e)for(let[r,i]of Object.entries(e))i===t?e[r]=n:Pj(i,t,n)}function Fj(e,t){for(let n=0;n<Math.min(e.length,t.length);n++)if(e[n]!==t[n])return e[n]-t[n];return e.length-t.length}function Ij(e){let t=[...e].sort(Fj),n=[];for(let e of t)n.some(t=>t.length<=e.length&&t.every((t,n)=>t===e[n]))||n.push(e);return n}function Lj(e,t){let n=e.children?.indexOf(t)??-1;if(n>=0)return{parent:e,index:n};for(let n of e.children??[]){let e=Lj(n,t);if(e)return e}return null}function Rj(e,t){let n=Lj(e,t);n?.parent.children?.splice(n.index,1)}function zj(e,t){if(e===t)return[];for(let[n,r]of(e.children??[]).entries()){let e=zj(r,t);if(e)return[n,...e]}return null}function Bj(e,t){let n=new Set((e.children??[]).map(e=>e.name));if(!n.has(t))return t;let r=2;for(;n.has(`${t}${r}`);)r+=1;return`${t}${r}`}var Vj=new Map(UO.flatMap(e=>e.nodes.map(e=>[e.type,e.summary]))),Hj=new Set,Uj=null;function Wj(e){Uj=e}var Gj=null,Kj=null,qj=null;function Jj(e){return e?e.includes(`Body`)||e.includes(`Area`)||e.includes(`Controller`)?`cat-body`:e===`NetworkSpawner`?`cat-net`:e.endsWith(`3D`)?`cat-3d`:e.endsWith(`2D`)||e===`Label`||e===`UILayer`?`cat-2d`:`cat-core`:`cat-core`}function Yj(e,t){e.textContent=``;let n=document.createElement(`div`);n.className=`tree-row scene-row${t.selection===null&&t.selectedAsset===null&&t.selectedGroup===null&&!t.addingAsset&&!t.addingGroup?` selected`:``}`,n.textContent=`⚙ ${String(t.working.name??`scene`)}`,n.addEventListener(`click`,()=>t.select(null)),e.appendChild(n);let r=t.working.root;if(!r){let t=document.createElement(`div`);t.className=`muted-note explorer-empty`,t.textContent=`no root node — pick a type below and + to start the scene`,e.appendChild(t);return}Xj(r,t.selection),e.appendChild(Zj(r,[],t,``))}function Xj(e,t){if(!t)return;let n=e,r=``;for(let e of t){r=r?`${r}/${n.name}`:String(n.name),Hj.delete(r);let t=n.children?.[e];if(!t)return;n=t}}function Zj(e,t,n,r){let i=r?`${r}/${e.name}`:String(e.name),a=(e.children?.length??0)>0,o=Hj.has(i),s=document.createElement(`div`);s.className=`tree-branch`;let c=document.createElement(`div`);c.className=`tree-row${n.isSelected(t)?` selected`:``}`,c.draggable=t.length>0&&qj!==i;let l=document.createElement(`span`);l.className=`chevron${a?``:` leaf`}${o?` collapsed`:``}`,a?l.appendChild(lO()):l.textContent=`·`,a&&l.addEventListener(`click`,e=>{e.stopPropagation(),o?Hj.delete(i):Hj.add(i),n.select(n.selection)});let u=document.createElement(`span`);if(u.className=`tree-icon ${Jj(e.type)}`,u.appendChild(cO(e.type)),u.title=e.type??`instance`,u.addEventListener(`click`,t=>{t.stopPropagation(),Qj(u,e.type??`instance`)}),c.append(l,u),qj===i){let t=document.createElement(`input`);t.className=`rename-input`,t.value=e.name??``;let r=()=>{qj=null;let r=t.value.trim();r&&r!==e.name?n.mutate(()=>{e.name=r}):n.select(n.selection)};t.addEventListener(`keydown`,e=>{e.stopPropagation(),e.key===`Enter`&&r(),e.key===`Escape`&&(qj=null,n.select(n.selection))});for(let e of[`click`,`pointerdown`,`dblclick`,`mousedown`])t.addEventListener(e,e=>e.stopPropagation());t.addEventListener(`blur`,r),c.appendChild(t),queueMicrotask(()=>{t.focus(),t.select()})}else{let r=document.createElement(`span`);r.className=`tree-name`,r.textContent=e.name??`(unnamed)`,r.addEventListener(`dblclick`,e=>{e.stopPropagation(),qj=i,n.select(t)}),c.appendChild(r)}if(c.addEventListener(`click`,e=>{a&&!e.metaKey&&!e.ctrlKey&&(o?Hj.delete(i):Hj.add(i)),n.select(t,{toggle:e.metaKey||e.ctrlKey})}),c.addEventListener(`contextmenu`,e=>{e.preventDefault(),n.isSelected(t)||n.select(t),$j(e.clientX,e.clientY,n,t,i)}),cM(c,t,n),s.appendChild(c),a&&!o){let r=document.createElement(`div`);r.className=`tree-children`,(e.children??[]).forEach((e,a)=>{r.appendChild(Zj(e,[...t,a],n,i))}),s.appendChild(r)}return s}function Qj(e,t){iM();let n=document.createElement(`div`);n.className=`balloon floating`;let r=document.createElement(`span`);r.className=`balloon-title`,r.textContent=t,n.appendChild(r);let i=Vj.get(t);if(i){let e=document.createElement(`span`);e.textContent=aO(i),n.appendChild(e)}document.body.appendChild(n);let a=e.getBoundingClientRect();n.style.left=`${Math.min(innerWidth-240,a.right+8)}px`,n.style.top=`${Math.max(8,a.top-6)}px`,aM(n)}function $j(e,t,n,r,i){iM();let a=n.allSelections(),o=a.length===1&&r.length>0,s=document.createElement(`div`);s.className=`context-menu floating`;let c=(e,t,n)=>{let r=document.createElement(`button`);r.type=`button`,r.className=`menu-item${n?.danger?` danger`:``}`,r.disabled=t===null;let i=document.createElement(`span`);if(i.textContent=e,r.appendChild(i),n?.kbd){let e=document.createElement(`kbd`);e.textContent=n.kbd,r.appendChild(e)}t&&r.addEventListener(`click`,()=>{iM(),t()}),s.appendChild(r)};c(`duplicate${a.length>1?` ×${a.length}`:``}`,()=>{for(let e of[...a].reverse())n.duplicateNode(e)}),c(`rename`,o?()=>{qj=i,n.select(r)}:null,{kbd:`dbl-click`}),s.appendChild(eM()),c(`cut`,r.length>0?()=>tM(n,!0):null),c(`copy`,r.length>0?()=>tM(n,!1):null),c(`paste as child${Kj?` (${Kj.nodes.length})`:``}`,Kj?()=>nM(n,r):null),s.appendChild(eM()),c(`delete`,r.length>0?()=>(Uj??(e=>n.removeNodes(e)))(a):null,{danger:!0}),document.body.appendChild(s),s.style.left=`${Math.min(innerWidth-200-8,e)}px`,s.style.top=`${Math.min(innerHeight-s.offsetHeight-8,t)}px`,aM(s)}function eM(){let e=document.createElement(`div`);return e.className=`menu-divider`,e}function tM(e,t){let n=e.allSelections().filter(e=>e.length>0).map(t=>e.nodeAt(t)).filter(e=>e!==null);Kj={nodes:n.map(e=>JSON.parse(JSON.stringify(e))),cut:t?n:null}}function nM(e,t){if(Kj){for(let n of Kj.nodes)e.insertNode(n,t);if(Kj.cut){let t=e.working.root,n=Kj.cut.map(e=>rM(t,e)).filter(e=>e!==null);n.length>0&&e.removeNodes(n),Kj={nodes:Kj.nodes,cut:null}}}}function rM(e,t){if(e===t)return[];for(let[n,r]of(e.children??[]).entries()){let e=rM(r,t);if(e)return[n,...e]}return null}function iM(){for(let e of document.querySelectorAll(`.floating`))e.remove()}function aM(e){let t=r=>{e.contains(r.target)||(e.remove(),document.removeEventListener(`pointerdown`,t,!0),document.removeEventListener(`keydown`,n,!0))},n=r=>{r.key===`Escape`&&(e.remove(),document.removeEventListener(`pointerdown`,t,!0),document.removeEventListener(`keydown`,n,!0))};setTimeout(()=>{document.addEventListener(`pointerdown`,t,!0),document.addEventListener(`keydown`,n,!0)},0)}function oM(e,t){let n=t.getBoundingClientRect(),r=(e.clientY-n.top)/n.height;return r<.25?`before`:r>.75?`after`:`into`}function sM(e){e.classList.remove(`drop-into`,`drop-before`,`drop-after`)}function cM(e,t,n){e.addEventListener(`dragstart`,r=>{Gj=n.isSelected(t)?n.allSelections():[t],r.dataTransfer?.setData(`text/plain`,``),r.dataTransfer&&(r.dataTransfer.effectAllowed=`move`),e.classList.add(`dragging`)}),e.addEventListener(`dragend`,()=>{Gj=null,e.classList.remove(`dragging`)}),e.addEventListener(`dragover`,n=>{if(!Gj||Gj.some(e=>e.length<=t.length&&e.every((e,n)=>e===t[n])))return;n.preventDefault(),n.dataTransfer&&(n.dataTransfer.dropEffect=`move`),sM(e);let r=t.length===0?`into`:oM(n,e);e.classList.add(`drop-${r}`)}),e.addEventListener(`dragleave`,()=>sM(e)),e.addEventListener(`drop`,r=>{if(sM(e),!Gj)return;r.preventDefault();let i=t.length===0?`into`:oM(r,e),a=n.moveNodes(Gj,t,i);Gj=null,a?.[0]&&n.select(a[0])})}var lM={x:`#fb7185`,y:`#86efac`,z:`#7aa2ff`};function uM(e,t,n){let r=t-e.origin.x,i=n-e.origin.y;if(Math.abs(r)<=e.centerSize&&Math.abs(i)<=e.centerSize)return{kind:`center`};for(let t of e.axes){let e=t.sx*t.sx+t.sy*t.sy;if(e<1)continue;let n=Math.max(0,Math.min(1,(r*t.sx+i*t.sy)/e)),a=r-t.sx*n,o=i-t.sy*n;if(n>.25&&Math.hypot(a,o)<=8)return{kind:`axis`,axis:t.axis}}return null}function dM(e,t,n){let r=e.sx*e.sx+e.sy*e.sy;return r<1?0:(t*e.sx+n*e.sy)/r*e.worldPerUnit}function fM(e,t,n,r=!1){let{origin:i,axes:a,centerSize:o}=t;for(let t of a){let a=n?.kind===`axis`&&n.axis===t.axis;e.strokeStyle=t.color,e.fillStyle=t.color,e.globalAlpha=a?1:.9,e.lineWidth=a?3:2,e.beginPath(),e.moveTo(i.x,i.y),e.lineTo(i.x+t.sx,i.y+t.sy),e.stroke();let o=Math.hypot(t.sx,t.sy)||1,s=t.sx/o,c=t.sy/o,l=i.x+t.sx,u=i.y+t.sy;e.beginPath(),r?e.rect(l-5,u-5,10,10):(e.moveTo(l+s*9,u+c*9),e.lineTo(l-c*4.5,u+s*4.5),e.lineTo(l+c*4.5,u-s*4.5),e.closePath()),e.fill(),e.font=`700 9px Inter, system-ui, sans-serif`,e.textAlign=`center`,e.textBaseline=`middle`,e.fillText(t.axis.toUpperCase(),l+s*18,u+c*18)}let s=n?.kind===`center`;e.globalAlpha=s?1:.95,e.fillStyle=s?`#ffffff`:`#e2e6f0`,e.strokeStyle=`#0b0d14`,e.lineWidth=1.5,e.beginPath(),e.rect(i.x-o,i.y-o,o*2,o*2),e.fill(),e.stroke(),e.globalAlpha=1}function pM(e,t,n){return Math.atan2(n-e.y,t-e.x)*180/Math.PI}function mM(e,t){let n=t-e;for(;n>180;)n-=360;for(;n<=-180;)n+=360;return n}function hM(e,t,n,r){let i=null,a=r;for(let r of e)for(let e of r.points){let o=Math.hypot(t-e.x,n-e.y);o<a&&(a=o,i=r.axis)}return i}function gM(e,t,n){for(let r of t){if(r.points.length<2)continue;let t=n===r.axis;e.strokeStyle=lM[r.axis],e.globalAlpha=t?1:.75,e.lineWidth=t?3:2,e.beginPath();let i=r.points[0];e.moveTo(i.x,i.y);for(let t of r.points.slice(1))e.lineTo(t.x,t.y);e.closePath(),e.stroke()}e.globalAlpha=1}function _M(e,t,n,r){e.font=`600 11px ui-monospace, Menlo, monospace`;let i=e.measureText(r).width+14;e.fillStyle=`rgba(16, 19, 29, 0.92)`,e.strokeStyle=`rgba(110, 231, 220, 0.5)`,e.lineWidth=1,e.beginPath(),e.roundRect(t+14,n-26,i,20,5),e.fill(),e.stroke(),e.fillStyle=`#6ee7dc`,e.textAlign=`left`,e.textBaseline=`middle`,e.fillText(r,t+21,n-16)}var vM=class{engine;editCanvas;overlay;playCanvas;cb;renderer2d=null;renderer3d=null;rendererDim=null;orbit={yaw:.6,pitch:.35,dist:10,target:[0,1,0]};pathOf=new Map;nodeAtPath=new Map;selectedPath=null;extraPaths=[];hoveredNode=null;hoveredGizmo=null;lastGood=null;lastAppliedKey=null;playEngine=null;playRenderer=null;get playing(){return this.playEngine!==null}constructor(e,t,n,r){this.editCanvas=e,this.overlay=t,this.playCanvas=n,this.cb=r;let i=this;this.engine=new Ee({scheduler:e=>{let t=0,n=performance.now(),r=requestAnimationFrame(function a(o){t+=.001;let s=Math.min(.1,(o-n)/1e3);n=o;try{e(t),i.tickAmbientPreviews(s)}catch(e){console.error(`incanto-editor viewport:`,e)}r=requestAnimationFrame(a)});return()=>cancelAnimationFrame(r)}}),this.engine.start(),this.engine.updated.connect(()=>this.drawOverlay()),window.addEventListener(`keydown`,e=>{e.key===`Shift`&&(this.shiftHeld=!0)}),window.addEventListener(`keyup`,e=>{e.key===`Shift`&&(this.shiftHeld=!1)}),this.bindPointer()}apply(e){e.root||(e={...e,root:{name:`__empty`,type:(e.dimension??`2d`)===`3d`?`Node3D`:`Node2D`}});let t=JSON.stringify(e);if(t===this.lastAppliedKey&&this.rendererDim===(e.dimension??`2d`))return null;let n;try{let t=structuredClone(e);delete t.connections,NM(t.root),IM(t),n=at(t)}catch(e){return e instanceof Error?e.message:String(e)}this.lastGood=e,this.lastAppliedKey=t;let r=e.dimension??`2d`;if(this.rendererDim!==r){if(this.renderer2d?.dispose(),this.renderer3d?.dispose(),this.renderer2d=null,this.renderer3d=null,r===`2d`)this.renderer2d=new jp({canvas:this.editCanvas,engine:this.engine}),this.renderer2d.viewOverride=this.defaultView(e);else{this.renderer3d=new xD({canvas:this.editCanvas,engine:this.engine});let t=jM(e.root);if(t){let[e,n,r]=t;this.orbit.dist=Math.max(2,Math.hypot(e,n,r)),this.orbit.yaw=Math.atan2(e,r),this.orbit.pitch=Math.asin(Math.max(-.99,Math.min(.99,n/this.orbit.dist))),this.orbit.target=[0,Math.min(2,Math.abs(n)/2),0]}this.syncOrbit()}this.rendererDim=r}return this.engine.setScene(n),this.indexTree(n.root),this.hoveredNode=null,null}setSelection(e,t=[]){this.selectedPath=e,this.extraPaths=t}validate(e){if(!e.root)return null;try{let t=structuredClone(e);return delete t.connections,NM(t.root),IM(t),at(t),null}catch(e){return e instanceof Error?e.message:String(e)}}defaultView(e){let t=MM(e.root);if(t)return{cx:t[0],cy:t[1],zoom:1};let n=this.editCanvas.clientWidth||960,r=this.editCanvas.clientHeight||540;return{cx:n/2,cy:r/2,zoom:1}}indexTree(e){this.pathOf=new Map,this.nodeAtPath=new Map;let t=(e,n)=>{this.pathOf.set(e,n),this.nodeAtPath.set(n.join(`.`),e),e.children.forEach((e,r)=>{t(e,[...n,r])})};t(e,[])}liveSelected(){return this.selectedPath===null?null:this.nodeAtPath.get(this.selectedPath.join(`.`))??null}patchProp(e,t,n,r){if(this.playing)return!1;let i=this.nodeAtPath.get(t.join(`.`));if(!i)return!1;try{i[n]=r===void 0?void 0:structuredClone(r)}catch{return!1}return this.lastAppliedKey=JSON.stringify(e),!0}gizmoActive=null;gizmoMode=`move`;activeRing=null;readout=null;cancelActiveDrag=()=>{};shiftHeld=!1;showColliders=!0;get mode(){return this.gizmoMode}setMode(e){this.gizmoMode=e,this.cb.onModeChanged(e)}gizmoLayout(){let e=this.liveSelected();if(!e)return null;if(this.rendererDim===`2d`&&this.renderer2d){let t=e.position;if(!Array.isArray(t))return null;let n=AM(e),r=this.renderer2d.screenFromWorld(n.x,n.y),i=this.renderer2d.view().zoom;return{origin:r,centerSize:7,axes:[{axis:`x`,sx:64,sy:0,worldPerUnit:64/i,color:lM.x},{axis:`y`,sx:0,sy:64,worldPerUnit:64/i,color:lM.y}]}}if(this.rendererDim===`3d`&&this.renderer3d){let t=e._object3D,n=e.position;if(!t||!Array.isArray(n))return null;t.getWorldPosition(TM);let r=this.renderer3d.screenFromWorld(TM.x,TM.y,TM.z);if(r.behind)return null;let i=[],a=this.orbit.dist,o=Math.max(.2,a*.18);for(let[e,t]of[[`x`,[1,0,0]],[`y`,[0,1,0]],[`z`,[0,0,1]]]){let n=this.renderer3d.screenFromWorld(TM.x+t[0]*o,TM.y+t[1]*o,TM.z+t[2]*o);n.behind||i.push({axis:e,sx:n.x-r.x,sy:n.y-r.y,worldPerUnit:o,color:lM[e]})}return{origin:{x:r.x,y:r.y},centerSize:7,axes:i}}return null}ringLayout(){let e=this.liveSelected();if(!e)return null;if(this.rendererDim===`2d`&&this.renderer2d){let t=AM(e),n=this.renderer2d.screenFromWorld(t.x,t.y),r=[];for(let e=0;e<48;e++){let t=e/48*Math.PI*2;r.push({x:n.x+56*Math.cos(t),y:n.y+56*Math.sin(t)})}return{origin:n,rings:[{axis:`z`,points:r}]}}if(this.rendererDim===`3d`&&this.renderer3d){let t=e._object3D;if(!t)return null;t.getWorldPosition(TM);let n={x:TM.x,y:TM.y,z:TM.z},r=this.renderer3d.screenFromWorld(n.x,n.y,n.z);if(r.behind)return null;let i=Math.max(.2,this.orbit.dist*.16),a=[];for(let e of[`x`,`y`,`z`]){let t=[];for(let r=0;r<48;r++){let a=r/48*Math.PI*2,o=Math.cos(a)*i,s=Math.sin(a)*i,c=e===`x`?this.renderer3d.screenFromWorld(n.x,n.y+o,n.z+s):e===`y`?this.renderer3d.screenFromWorld(n.x+o,n.y,n.z+s):this.renderer3d.screenFromWorld(n.x+o,n.y+s,n.z);c.behind||t.push({x:c.x,y:c.y})}a.push({axis:e,points:t})}return{origin:{x:r.x,y:r.y},rings:a}}return null}orbitVectors(){let{yaw:e,pitch:t,dist:n,target:r}=this.orbit,i=Math.cos(t),a=new H(r[0]+n*i*Math.sin(e),r[1]+n*Math.sin(t),r[2]+n*i*Math.cos(e)),o=new H(r[0],r[1],r[2]).sub(a).normalize(),s=new H().crossVectors(o,xM).normalize();return{pos:a,right:s,up:new H().crossVectors(s,o).normalize(),forward:o}}syncOrbit(){if(!this.renderer3d)return;let{pos:e}=this.orbitVectors();this.renderer3d.viewOverride={position:[e.x,e.y,e.z],target:[...this.orbit.target]}}gameView(){if(this.rendererDim===`3d`&&this.renderer3d){let e=null;for(let[t]of this.pathOf)if(t instanceof lb&&(t.current||!e)&&(e=t,t.current))break;let t=e?e._ensureObject3D():null;if(t){t.getWorldPosition(SM),CM.set(0,0,-1).applyQuaternion(t.getWorldQuaternion(wM));let e=Math.min(40,Math.max(3,this.orbit.dist));this.orbit.target=[SM.x+CM.x*e,SM.y+CM.y*e,SM.z+CM.z*e],this.orbit.dist=e,this.orbit.yaw=Math.atan2(-CM.x,-CM.z),this.orbit.pitch=Math.asin(Math.max(-.99,Math.min(.99,-CM.y)))}this.renderer3d.viewOverride=null;return}this.renderer2d&&(this.renderer2d.viewOverride=null)}snapView(e,t){e===`y`?(this.orbit.pitch=t*1.45,this.orbit.yaw=0):(this.orbit.pitch=0,this.orbit.yaw=e===`z`?t===1?0:Math.PI:Math.PI/2*t),this.syncOrbit()}gizmoCenter(){return{x:this.overlay.clientWidth-58,y:58,r:36}}gizmoHandles(){let{right:e,up:t,forward:n}=this.orbitVectors(),r=this.gizmoCenter(),i=[];for(let[a,o]of[[`x`,new H(1,0,0)],[`y`,new H(0,1,0)],[`z`,new H(0,0,1)]])for(let s of[1,-1]){let c=o.clone().multiplyScalar(s);i.push({axis:a,sign:s,x:r.x+c.dot(e)*26,y:r.y-c.dot(t)*26,z:-c.dot(n)})}return i.sort((e,t)=>e.z-t.z)}gizmoHit(e,t){let n=this.gizmoCenter();if(Math.hypot(e-n.x,t-n.y)>n.r+8)return null;let r=null,i=14;for(let n of this.gizmoHandles()){let a=Math.hypot(e-n.x,t-n.y);a<i&&(i=a,r={axis:n.axis,sign:n.sign})}return r??{axis:`z`,sign:1}}gizmoHandleAt(e,t){let n=this.gizmoCenter();if(Math.hypot(e-n.x,t-n.y)>n.r+8)return null;let r=null,i=12;for(let n of this.gizmoHandles()){let a=Math.hypot(e-n.x,t-n.y);a<i&&(i=a,r={axis:n.axis,sign:n.sign})}return r}tickAmbientPreviews(e){if(!this.playing)for(let[t]of this.pathOf)(t instanceof sC||t instanceof vp||t instanceof uC||t instanceof rE||t instanceof RS)&&t.update(e)}modelAnimationsAt(e){let t=this.nodeAtPath.get(e.join(`.`));return t instanceof sC?t.availableAnimations():[]}drawOverlay(){let e=this.overlay.getContext(`2d`);if(!e)return;let t=this.overlay.clientWidth,n=this.overlay.clientHeight,r=Math.min(devicePixelRatio||1,2);if((this.overlay.width!==t*r||this.overlay.height!==n*r)&&(this.overlay.width=t*r,this.overlay.height=n*r),e.setTransform(r,0,0,r,0,0),e.clearRect(0,0,t,n),this.playing)return;if(this.rendererDim===`3d`){this.showColliders&&this.drawColliders3D(e),this.drawGizmo(e),this.drawModeGizmo(e),this.readout&&_M(e,this.readout.x,this.readout.y,this.readout.text);return}if(!this.renderer2d)return;if(this.drawAxes2D(e),this.showColliders)for(let[t]of this.pathOf){let n=t.collider;n&&typeof n==`object`&&`shape`in n&&this.drawCollider(e,t,n)}if(this.hoveredNode&&this.hoveredNode!==this.liveSelected()){let t=this.renderer2d.boundsOf(this.hoveredNode);t&&(e.strokeStyle=`rgba(110, 231, 220, 0.45)`,e.lineWidth=1.5,e.setLineDash([]),e.strokeRect(t.x,t.y,t.w,t.h))}for(let t of this.extraPaths){let n=this.nodeAtPath.get(t.join(`.`)),r=n?this.renderer2d.boundsOf(n):null;r&&(e.strokeStyle=`rgba(110, 231, 220, 0.7)`,e.lineWidth=1.5,e.setLineDash([4,3]),e.strokeRect(r.x-2,r.y-2,r.w+4,r.h+4),e.setLineDash([]))}let i=this.liveSelected();if(i){let t=this.renderer2d.boundsOf(i)??this.pointBounds(i);if(t){e.strokeStyle=`#6ee7dc`,e.lineWidth=2,e.setLineDash([6,4]),e.lineDashOffset=-(performance.now()/50%10),e.strokeRect(t.x-2,t.y-2,t.w+4,t.h+4),e.setLineDash([]),e.fillStyle=`#6ee7dc`;for(let[n,r]of[[t.x-2,t.y-2],[t.x+t.w+2,t.y-2],[t.x-2,t.y+t.h+2],[t.x+t.w+2,t.y+t.h+2]])e.fillRect(n-3,r-3,6,6)}}this.drawModeGizmo(e),this.readout&&_M(e,this.readout.x,this.readout.y,this.readout.text)}drawModeGizmo(e){if(this.gizmoMode===`rotate`){let t=this.ringLayout();t&&gM(e,t.rings,this.activeRing);return}let t=this.gizmoLayout();t&&fM(e,t,this.gizmoActive,this.gizmoMode===`scale`)}drawGizmo(e){let t=this.gizmoCenter();e.beginPath(),e.arc(t.x,t.y,t.r,0,Math.PI*2),e.fillStyle=`rgba(16, 19, 29, 0.72)`,e.fill(),e.strokeStyle=`rgba(52, 60, 84, 0.9)`,e.lineWidth=1,e.stroke();let n={x:`#fb7185`,y:`#86efac`,z:`#7aa2ff`},r={x1:`right`,"x-1":`left`,y1:`top`,"y-1":`bottom`,z1:`front`,"z-1":`back`};for(let r of this.gizmoHandles()){let i=this.hoveredGizmo?.axis===r.axis&&this.hoveredGizmo?.sign===r.sign,a=n[r.axis],o=(r.z+1)/2;e.globalAlpha=i?1:.45+o*.55,e.strokeStyle=a,e.lineWidth=1.5,e.beginPath(),e.moveTo(t.x,t.y),e.lineTo(r.x,r.y),e.stroke(),e.beginPath(),e.arc(r.x,r.y,(r.sign===1?7:5)+(i?2:0),0,Math.PI*2),r.sign===1?(e.fillStyle=a,e.fill(),e.fillStyle=`#0b0d14`,e.font=`700 8px ui-monospace, monospace`,e.textAlign=`center`,e.textBaseline=`middle`,e.fillText(r.axis.toUpperCase(),r.x,r.y+.5)):(e.fillStyle=`rgba(16, 19, 29, 0.9)`,e.fill(),e.stroke())}if(e.globalAlpha=1,this.hoveredGizmo){let n=r[`${this.hoveredGizmo.axis}${this.hoveredGizmo.sign}`];e.fillStyle=`#9aa3bd`,e.font=`600 10px Inter, system-ui, sans-serif`,e.textAlign=`center`,e.textBaseline=`bottom`,e.fillText(`${n} view`,t.x,t.y-t.r-6)}}drawColliders3D(e){let t=this.renderer3d;if(!t)return;e.save(),e.strokeStyle=yM,e.lineWidth=1.5;let n=(e,n,r)=>t.screenFromWorld(e,n,r),r=(t,r=!1)=>{e.beginPath();let i=!1;for(let[r,a,o]of t){let t=n(r,a,o);if(t.behind){i=!1;continue}i?e.lineTo(t.x,t.y):e.moveTo(t.x,t.y),i=!0}r&&e.closePath(),e.stroke()},i=(e,t,n,i,a)=>{let o=[];for(let r=0;r<=32;r++){let s=r/32*Math.PI*2,c=Math.cos(s)*i,l=Math.sin(s)*i;a===`xz`?o.push([e+c,t,n+l]):a===`xy`?o.push([e+c,t+l,n]):o.push([e,t+c,n+l])}r(o)};for(let[e]of this.pathOf){let t=e.collider,n=e._object3D;if(!t||typeof t!=`object`||!(`shape`in t)||!n)continue;n.getWorldPosition(TM);let a=t.offset??[0,0,0],o=TM.x+(a[0]??0),s=TM.y+(a[1]??0),c=TM.z+(a[2]??0);if(t.shape===`box`){let e=t.size??[1,1,1],n=(e[0]??1)/2,i=(e[1]??1)/2,a=(e[2]??1)/2;r([[o-n,s-i,c-a],[o+n,s-i,c-a],[o+n,s-i,c+a],[o-n,s-i,c+a]],!0),r([[o-n,s+i,c-a],[o+n,s+i,c-a],[o+n,s+i,c+a],[o-n,s+i,c+a]],!0);for(let[e,t]of[[-1,-1],[1,-1],[1,1],[-1,1]])r([[o+e*n,s-i,c+t*a],[o+e*n,s+i,c+t*a]])}else if(t.shape===`sphere`){let e=t.radius??.5;i(o,s,c,e,`xz`),i(o,s,c,e,`xy`),i(o,s,c,e,`yz`)}else if(t.shape===`capsule`){let e=t.radius??.4,n=(t.height??1)/2;i(o,s+n,c,e,`xz`),i(o,s-n,c,e,`xz`);for(let[t,i]of[[e,0],[-e,0],[0,e],[0,-e]])r([[o+t,s-n,c+i],[o+t,s+n,c+i]])}}e.restore()}drawAxes2D(e){let t=this.gizmoCenter();e.save(),e.shadowColor=`rgba(0, 0, 0, 0.25)`,e.shadowBlur=10,e.shadowOffsetY=2,e.beginPath(),e.arc(t.x,t.y,31,0,Math.PI*2),e.fillStyle=`rgba(13, 16, 24, 0.42)`,e.fill(),e.shadowColor=`transparent`,e.strokeStyle=`rgba(255, 255, 255, 0.16)`,e.lineWidth=1,e.stroke();let n=t.x-9,r=t.y-9;e.lineCap=`round`;let i=(t,i,a,o)=>{let s=n+t*21,c=r+i*21,l=e.createLinearGradient(n,r,s,c);l.addColorStop(0,`${a}55`),l.addColorStop(1,a),e.strokeStyle=l,e.lineWidth=2,e.beginPath(),e.moveTo(n,r),e.lineTo(s,c),e.stroke(),e.beginPath(),e.arc(s,c,6.5,0,Math.PI*2),e.fillStyle=a,e.fill(),e.font=`800 8px Inter, system-ui, sans-serif`,e.textAlign=`center`,e.textBaseline=`middle`,e.fillStyle=`#0b0d14`,e.fillText(o,s,c+.5)};i(1,0,`#fb7185`,`X`),i(0,1,`#86efac`,`Y`),e.beginPath(),e.arc(n,r,2.5,0,Math.PI*2),e.fillStyle=`#e2e6f0`,e.fill(),e.restore()}pointBounds(e){if(!this.renderer2d)return null;let t=e.position;if(!Array.isArray(t))return null;let n=AM(e),r=this.renderer2d.screenFromWorld(n.x,n.y);return{x:r.x-12,y:r.y-12,w:24,h:24}}drawCollider(e,t,n){if(!this.renderer2d)return;let r=AM(t),i=n.offset??[0,0],a=this.renderer2d.screenFromWorld(r.x+(i[0]??0),r.y+(i[1]??0)),o=this.renderer2d.view().zoom;e.strokeStyle=yM,e.lineWidth=1.5,e.setLineDash([]);let s=n.shape;if(s===`rect`){let t=n.size??[32,32],r=(t[0]??32)/2*o,i=(t[1]??32)/2*o;e.strokeRect(a.x-r,a.y-i,r*2,i*2)}else if(s===`circle`){let t=(n.radius??16)*o;e.beginPath(),e.arc(a.x,a.y,t,0,Math.PI*2),e.stroke()}else if(s===`capsule`){let t=(n.radius??16)*o,r=(n.height??32)/2*o;e.beginPath(),e.arc(a.x,a.y-r,t,Math.PI,0),e.arc(a.x,a.y+r,t,0,Math.PI),e.closePath(),e.stroke()}e.setLineDash([])}bindPointer(){let e=this.overlay,t=null,n=null,r=null,i=null,a=null,o=null,s=null,c=t=>{let n=this.ringLayout();if(!n)return!1;let r=hM(n.rings,t.offsetX,t.offsetY,9);if(!r)return!1;let i=this.liveSelected();if(!i)return!1;e.setPointerCapture(t.pointerId);let a=i.rotation,o=1;if(this.rendererDim===`3d`&&this.renderer3d){let{forward:e}=this.renderer3d.cameraBasis();o=(r===`x`?e.x:r===`y`?e.y:e.z)>0?1:-1}return s={axis:r,node:i,origin:n.origin,startAngle:pM(n.origin,t.offsetX,t.offsetY),startRotation:Array.isArray(a)?[...a]:a??0,sign:o},this.activeRing=r,!0},l=e=>{if(!s)return;let{axis:t,node:n,origin:r,startAngle:i,startRotation:a,sign:o}=s,c=mM(i,pM(r,e.offsetX,e.offsetY))*o,l=e.shiftKey||this.shiftHeld?15:null,u=e=>{let t=e+c;return l?Math.round(t/l)*l:Math.round(t*10)/10},d;if(Array.isArray(a)){let e={x:0,y:1,z:2}[t],r=[...a];r[e]=u(a[e]??0),d=r[e],n.rotation=r}else{let e=u(a);d=e,n.rotation=e}this.readout={x:e.offsetX,y:e.offsetY,text:`${d}°${l?` ⌁15°`:``}`}},u=e=>{if(!s)return;let{node:t,startRotation:n}=s;if(!e)t.rotation=n;else{let e=this.pathOf.get(t),n=t.rotation;e!==void 0&&n!==void 0&&(Array.isArray(n)?this.cb.onNodeRotated3D(e,[n[0]??0,n[1]??0,n[2]??0]):this.cb.onNodeRotated2D(e,n))}s=null,this.activeRing=null,this.readout=null};this.cancelActiveDrag=()=>{u(!1),o&&(o.node.position=o.startPos,o.node.scale=o.startScale,o=null,this.gizmoActive=null,this.readout=null)};let d=t=>{if(this.gizmoMode===`rotate`)return c(t);let n=this.gizmoLayout();if(!n)return!1;let r=uM(n,t.offsetX,t.offsetY);if(!r)return!1;let i=this.liveSelected();if(!i)return!1;e.setPointerCapture(t.pointerId);let a=[...i.position??[]],s=[...i.scale??[]];return o={hit:r,mode:this.gizmoMode===`scale`||t.button===2?`scale`:`move`,startX:t.offsetX,startY:t.offsetY,layout:n,node:i,startPos:a,startScale:s},this.gizmoActive=r,!0},f=e=>{if(!o)return;let{hit:t,mode:n,layout:r,node:i,startPos:a,startScale:s}=o,c=e.offsetX-o.startX,l=e.offsetY-o.startY,u=this.rendererDim===`3d`,d={x:0,y:1,z:2},f=e.shiftKey||this.shiftHeld,p=f?u?.5:10:null,m=f?.25:null,h=e=>p?Math.round(e/p)*p:e,g=e=>m?Math.max(m,Math.round(e/m)*m):e;if(n===`scale`){let n=Math.max(.05,1+(c-l)*.005),r=[...s];if(t.kind===`axis`)r[d[t.axis]]=bM(g((s[d[t.axis]]??1)*n));else for(let e=0;e<r.length;e++)r[e]=bM(g((s[e]??1)*n));i.scale=r,this.readout={x:e.offsetX,y:e.offsetY,text:`×${r.map(e=>bM(e)).join(`, `)}`};return}let _=[...a];if(t.kind===`axis`){let e=r.axes.find(e=>e.axis===t.axis);if(!e)return;let n=dM(e,c,l);if(u)_[d[t.axis]]=bM(h((a[d[t.axis]]??0)+n));else{let e=kM(i,t.axis===`x`?n:0,t.axis===`y`?n:0),r=t.axis===`x`?e.x:e.y;_[d[t.axis]]=Math.round(h((a[d[t.axis]]??0)+r))}}else if(u&&this.renderer3d){let{right:e,up:t}=this.renderer3d.cameraBasis(),n=this.orbit.dist*.0016;_[0]=bM(h((a[0]??0)+(e.x*c-t.x*l)*n)),_[1]=bM(h((a[1]??0)+(e.y*c-t.y*l)*n)),_[2]=bM(h((a[2]??0)+(e.z*c-t.z*l)*n))}else if(this.renderer2d){let e=this.renderer2d.view(),t=kM(i,c/e.zoom,l/e.zoom);_[0]=Math.round(h((a[0]??0)+t.x)),_[1]=Math.round(h((a[1]??0)+t.y))}i.position=_,this.readout={x:e.offsetX,y:e.offsetY,text:`[${_.map(e=>bM(e)).join(`, `)}]`}},p=()=>{if(!o)return;let{node:e,mode:t}=o,n=this.pathOf.get(e);if(n)if(t===`scale`){let t=e.scale??[];this.rendererDim===`3d`?this.cb.onNodeScaled3D(n,[t[0]??1,t[1]??1,t[2]??1]):this.cb.onNodeScaled(n,[t[0]??1,t[1]??1])}else{let t=e.position??[];this.rendererDim===`3d`?this.cb.onNodeMoved3D(n,[t[0]??0,t[1]??0,t[2]??0]):this.cb.onNodeMoved(n,[t[0]??0,t[1]??0])}o=null,this.gizmoActive=null};e.addEventListener(`pointerdown`,o=>{if(this.playing||d(o))return;if(this.rendererDim===`3d`){let t=this.gizmoHit(o.offsetX,o.offsetY);if(t){this.snapView(t.axis,t.sign);return}e.setPointerCapture(o.pointerId),o.button===1||o.button===2||o.shiftKey?i={x:o.offsetX,y:o.offsetY,target:[...this.orbit.target]}:(r={x:o.offsetX,y:o.offsetY,yaw:this.orbit.yaw,pitch:this.orbit.pitch},a={x:o.offsetX,y:o.offsetY,toggle:o.ctrlKey||o.metaKey});return}if(!this.renderer2d)return;if(e.setPointerCapture(o.pointerId),o.button===1||o.button===2||o.shiftKey){let e=this.renderer2d.view();n={startX:o.offsetX,startY:o.offsetY,cx:e.cx,cy:e.cy};return}let s=this.renderer2d.pick(o.offsetX,o.offsetY),c=s?this.pathOf.get(s)??null:null;if(this.cb.onPick(c,{toggle:o.ctrlKey||o.metaKey}),this.selectedPath=c,s&&c&&Array.isArray(s.position)){let e=s.position;t={node:s,startWorld:this.renderer2d.worldFromScreen(o.offsetX,o.offsetY),startPos:[e[0]??0,e[1]??0]}}}),e.addEventListener(`pointermove`,a=>{if(!this.playing){if(s){l(a);return}if(o){f(a);return}if(this.rendererDim===`3d`){if(!r&&!i?(this.hoveredGizmo=this.gizmoHandleAt(a.offsetX,a.offsetY),e.style.cursor=this.hoveredGizmo?`pointer`:`grab`):e.style.cursor=`grabbing`,r)this.orbit.yaw=r.yaw-(a.offsetX-r.x)*.006,this.orbit.pitch=Math.max(-1.5,Math.min(1.5,r.pitch+(a.offsetY-r.y)*.006)),this.syncOrbit();else if(i){let{right:e,up:t}=this.orbitVectors(),n=this.orbit.dist*.0016,r=(a.offsetX-i.x)*n,o=(a.offsetY-i.y)*n;this.orbit.target=[i.target[0]-e.x*r+t.x*o,i.target[1]-e.y*r+t.y*o,i.target[2]-e.z*r+t.z*o],this.syncOrbit()}return}if(this.renderer2d){if(e.style.cursor=`crosshair`,n){let e=this.renderer2d.view();this.renderer2d.viewOverride={cx:n.cx-(a.offsetX-n.startX)/e.zoom,cy:n.cy-(a.offsetY-n.startY)/e.zoom,zoom:e.zoom};return}if(t){let e=this.renderer2d.worldFromScreen(a.offsetX,a.offsetY),n=kM(t.node,e.x-t.startWorld.x,e.y-t.startWorld.y),r=Math.round(t.startPos[0]+n.x),i=Math.round(t.startPos[1]+n.y);t.node.position=[r,i];return}this.hoveredNode=this.renderer2d.pick(a.offsetX,a.offsetY)}}});let m=e=>{if(u(!0),p(),this.readout=null,a&&e&&this.renderer3d&&Math.hypot(e.offsetX-a.x,e.offsetY-a.y)<4){let e=this.renderer3d.pick(a.x,a.y),t=e?this.pathOf.get(e)??null:null;t&&(this.cb.onPick(t,{toggle:a.toggle}),this.selectedPath=t)}if(a=null,r=null,i=null,t){let e=this.pathOf.get(t.node),n=t.node.position;e&&(n[0]!==t.startPos[0]||n[1]!==t.startPos[1])&&this.cb.onNodeMoved(e,[n[0]??0,n[1]??0]),t=null}n=null};e.addEventListener(`pointerup`,m),e.addEventListener(`pointercancel`,m),e.addEventListener(`contextmenu`,e=>e.preventDefault()),e.addEventListener(`dblclick`,e=>{if(this.playing)return;let t=this.rendererDim===`3d`?this.renderer3d?.pick(e.offsetX,e.offsetY)??null:this.renderer2d?.pick(e.offsetX,e.offsetY)??null,n=t?this.pathOf.get(t)??null:null;n&&(this.cb.onPick(n,{toggle:e.ctrlKey||e.metaKey}),this.selectedPath=n)}),e.addEventListener(`wheel`,e=>{if(this.playing)return;e.preventDefault();let t=e.deltaY<0?1.1:1/1.1;if(this.rendererDim===`3d`){this.orbit.dist=Math.max(.3,Math.min(800,this.orbit.dist/t)),this.syncOrbit();return}if(!this.renderer2d)return;let n=this.liveSelected();if(e.altKey&&n&&Array.isArray(n.scale)){let e=n.scale,r=[Math.round((e[0]??1)*t*100)/100,Math.round((e[1]??1)*t*100)/100];n.scale=r;let i=this.pathOf.get(n);i&&this.cb.onNodeScaled(i,r);return}let r=this.renderer2d.view(),i=this.renderer2d.worldFromScreen(e.offsetX,e.offsetY),a=Math.min(8,Math.max(.1,r.zoom*t));this.renderer2d.viewOverride={cx:i.x-(e.offsetX-r.w/2)/a,cy:i.y-(e.offsetY-r.h/2)/a,zoom:a}},{passive:!1})}zoomToFit(){if(this.rendererDim===`3d`){let e=new aa;for(let[t]of this.pathOf){let n=t._object3D;n&&(e.expandByObject(n),e.expandByPoint(n.getWorldPosition(TM)))}if(e.isEmpty())this.orbit.target=[0,1,0],this.orbit.dist=10;else{let t=e.getCenter(new H),n=e.getSize(new H);this.orbit.target=[t.x,t.y,t.z],this.orbit.dist=Math.max(2.5,n.length()*.85)}this.syncOrbit();return}if(!this.renderer2d)return;let e=this.editCanvas.clientWidth||960,t=this.editCanvas.clientHeight||540,n=1/0,r=1/0,i=-1/0,a=-1/0;for(let[e]of this.pathOf){let t=e.position;if(!Array.isArray(t))continue;let o=AM(e);n=Math.min(n,o.x),r=Math.min(r,o.y),i=Math.max(i,o.x),a=Math.max(a,o.y)}if(!Number.isFinite(n))return;let o=(n+i)/2,s=(r+a)/2,c=Math.max(i-n+200,200),l=Math.max(a-r+200,200),u=Math.min(2,Math.min(e/c,t/l));this.renderer2d.viewOverride={cx:o,cy:s,zoom:u}}async play(e){if(this.playing)return null;let t=structuredClone(e);NM(t.root),IM(t);let n;try{n=at(structuredClone(t),{declareConnectionSignals:!0})}catch{delete t.connections;try{n=at(structuredClone(t))}catch(e){return e instanceof Error?e.message:String(e)}}let r=new Ee;r.setScene(n),r.input.attachKeyboard(window),this.playCanvas.hidden=!1;let i=e.dimension??`2d`;try{if(i===`2d`){this.playRenderer=new jp({canvas:this.playCanvas,engine:r});let{enablePhysics2D:e}=await Xf(async()=>{let{enablePhysics2D:e}=await Promise.resolve().then(()=>Rp);return{enablePhysics2D:e}},void 0,import.meta.url),t=await e(r);t.debugDraw=this.showColliders}else{this.playRenderer=new xD({canvas:this.playCanvas,engine:r});let{enablePhysics3D:e}=await Xf(async()=>{let{enablePhysics3D:e}=await Promise.resolve().then(()=>jD);return{enablePhysics3D:e}},void 0,import.meta.url),t=await e(r);t.debugDraw=this.showColliders}}catch(e){return this.stop(),e instanceof Error?e.message:String(e)}return r.start(),this.playEngine=r,this.cb.onPlayStateChanged(!0),null}stop(){this.playEngine?.input.dispose(),this.playEngine?.stop(),this.playEngine=null,this.playRenderer?.dispose(),this.playRenderer=null,this.playCanvas.hidden=!0,this.cb.onPlayStateChanged(!1),this.lastAppliedKey=null,this.lastGood&&this.apply(this.lastGood)}},yM=`rgba(0, 255, 110, 0.95)`;function bM(e){return Math.round(e*100)/100}var xM=new H(0,1,0),SM=new H,CM=new H,wM=new V,TM=new H,EM=new G,DM=new H,OM=new H;function kM(e,t,n){let r=e._object2D?.parent;return r?(EM.copy(r.matrixWorld).invert(),DM.set(t,-n,0).applyMatrix4(EM),OM.set(0,0,0).applyMatrix4(EM),{x:DM.x-OM.x,y:-(DM.y-OM.y)}):{x:t,y:n}}function AM(e){let t=e._object2D;if(t)return t.getWorldPosition(TM),{x:TM.x,y:-TM.y};let n=0,r=0,i=e;for(;i;)Array.isArray(i.position)&&(n+=i.position[0]??0,r+=i.position[1]??0),i=i.parent;return{x:n,y:r}}function jM(e){if(!e)return null;if(e.type===`Camera3D`){let t=e.props?.position??[0,2,8];return[t[0]??0,t[1]??2,t[2]??8]}for(let t of e.children??[]){let e=jM(t);if(e)return e}return null}function MM(e){if(!e)return null;if(e.type===`Camera2D`){let t=e.props?.position??[0,0];return[t[0]??0,t[1]??0]}for(let t of e.children??[]){let e=MM(t);if(e)return e}return null}function NM(e){if(typeof e!=`object`||!e)return;let t=e;delete t.script;for(let e of t.children??[])NM(e)}var PM=null;function FM(){if(PM)return PM;let e=document.createElement(`canvas`);e.width=16,e.height=16;let t=e.getContext(`2d`);for(let e=0;e<2;e++)for(let n=0;n<2;n++)t.fillStyle=(n+e)%2==0?`#c252c2`:`#2b2b3b`,t.fillRect(n*8,e*8,8,8);return PM=e.toDataURL(),PM}function IM(e){let t=e.assets;if(t)for(let e of Object.values(t))typeof e?.url==`string`&&!e.url.includes(`/`)&&!e.url.includes(`.`)&&(e.url=FM())}Ke(),Tp(),xE(),VD();var $=e=>{let t=document.querySelector(e);if(!t)throw Error(`missing ${e}`);return t};async function LM(){let e=await JD();$(`#engine-version`).textContent=`incanto@${e.version}`,zA($(`#popover`)),JO(),$(`#docs-btn`).addEventListener(`click`,()=>KO());let t=$(`#lang-btn`),n=[{code:`en`,label:`English`},{code:`ko`,label:`한국어`}],r=()=>{$(`#lang-current`).textContent=nO()===`ko`?`한국어`:`EN`};r(),t.addEventListener(`click`,()=>{document.querySelector(`.lang-menu`)?.remove();let e=document.createElement(`div`);e.className=`context-menu floating lang-menu`;for(let{code:t,label:r}of n){let n=document.createElement(`button`);n.type=`button`,n.className=`menu-item`;let i=document.createElement(`span`);i.textContent=r;let a=document.createElement(`span`);a.textContent=nO()===t?`✓`:``,a.className=`lang-check`,n.append(i,a),n.addEventListener(`click`,()=>{e.remove(),rO(t)}),e.appendChild(n)}document.body.appendChild(e);let r=t.getBoundingClientRect();e.style.left=`${r.left}px`,e.style.top=`${r.bottom+6}px`;let i=n=>{e.contains(n.target)||n.target===t||(e.remove(),document.removeEventListener(`pointerdown`,i,!0))};setTimeout(()=>document.addEventListener(`pointerdown`,i,!0),0)}),iO(()=>{r(),YO(),i.select(i.selection)});let i=new Aj({format:1,type:`scene`,name:``,root:null}),a=$(`#tree`),o=$(`#inspector`),s=$(`#save-btn`),c=$(`#undo-btn`),l=$(`#play-btn`),u=$(`#error-banner`),d=$(`#play-notice`),f=$(`#add-type`),p=$(`#picker`),m=$(`#picker-list`),h=$(`#scenes-btn`),g=e.input,_=e.output,v,y=!1,b=e=>{u.hidden=!1,u.textContent=e,zO.error(e)},x=new vM($(`#viewport`),$(`#overlay`),$(`#play-canvas`),{onPick:(e,t)=>i.select(e??null,t),onNodeMoved:(e,t)=>k(e,`position`,t,[0,0]),onNodeScaled:(e,t)=>k(e,`scale`,t,[1,1]),onNodeMoved3D:(e,t)=>k(e,`position`,t,[0,0,0]),onNodeScaled3D:(e,t)=>k(e,`scale`,t,[1,1,1]),onNodeRotated2D:(e,t)=>O(e,`rotation`,t,0),onNodeRotated3D:(e,t)=>k(e,`rotation`,t,[0,0,0]),onModeChanged:e=>{for(let t of[`move`,`rotate`,`scale`])$(`#tool-${t}`).classList.toggle(`active`,t===e)},onPlayStateChanged:e=>{document.body.classList.toggle(`playing`,e),l.classList.toggle(`stop`,e),l.innerHTML=e?`<svg width="11" height="11" viewBox="0 0 24 24" fill="currentColor"><rect x="6" y="6" width="12" height="12"/></svg> stop`:`<svg width="11" height="11" viewBox="0 0 24 24" fill="currentColor"><path d="M8 5v14l11-7z"/></svg> play`,d.hidden=!e,e&&(d.textContent=`▶ simulating — physics + input live; game scripts run only in your real game. Esc or stop to return.`)},onError:b}),S=de().sort(),C=``,w=e=>{if(e===C)return;C=e,f.textContent=``;let t=e===`3d`?[`3D`,`3D Physics`,`Core`,`Network`,`2D`,`2D Physics`]:[`2D`,`2D Physics`,`Core`,`Network`,`3D`,`3D Physics`];for(let[e]of VO)t.includes(e)||t.push(e);for(let e of t){let t=VO.find(([t])=>t===e)?.[1];if(!t)continue;let n=document.createElement(`optgroup`);n.label=e;for(let e of S.filter(t)){let t=document.createElement(`option`);t.value=e,t.textContent=e,n.appendChild(t)}n.children.length>0&&f.appendChild(n)}f.value=e===`3d`?`MeshInstance3D`:`Sprite2D`};w(`2d`);let T=()=>{let e=i.selection??[];for(let t of f.querySelectorAll(`option`)){let n=structuredClone(i.working),r=n.root;if(!n.root)n.root={name:`__probe`,type:t.value};else{for(let t of e)r=r?.children?.[t];if(!r)continue;r.children||=[],r.children.push({name:`__probe`,type:t.value})}t.disabled=x.validate(n)!==null}};f.addEventListener(`mousedown`,T),f.addEventListener(`focus`,T);let E=!1,D=()=>{if(!y)return;Yj(a,i),_O($(`#asset-tree`),i),o.textContent=``,vO(o,i,se)||UA(o,i,{modelRefs:()=>zM(i,`model`),animationsForSelection:()=>i.selection?x.modelAnimationsAt(i.selection):[],assetRefs:e=>BM(i,e),addAsset:(e,t)=>VM(i,e,t),confirm:se}),w(i.working.dimension??`2d`),$(`#hints`).hidden=(i.working.dimension??`2d`)===`3d`,$(`#hints-3d`).hidden=(i.working.dimension??`2d`)!==`3d`,x.setSelection(i.selection,i.extra);let e=i.pendingLivePatch;if(i.pendingLivePatch=null,!x.playing)if(e&&x.patchProp(i.working,e.path,e.key,e.value))u.hidden=!0;else{let e=x.apply(i.working);u.hidden=e===null,e!==null&&b(e)}s.disabled=!i.dirty||!i.working.root,s.title=i.working.root?``:`Add a root node before saving`,c.disabled=!i.canUndo,i.dirty!==E&&(E=i.dirty,zO.change(i.dirty))};i.onChange(D),i.validator=e=>x.validate(e),i.onError=b;let O=(e,t,n,r)=>{let a=i.nodeAt(e);a&&i.mutate(()=>{a.props||={},n===r?(delete a.props[t],Object.keys(a.props).length===0&&delete a.props):a.props[t]=n},{path:e,key:t,value:n})},k=(e,t,n,r)=>{let a=i.nodeAt(e);a&&i.mutate(()=>{a.props||={},JSON.stringify(n)===JSON.stringify(r)?(delete a.props[t],Object.keys(a.props).length===0&&delete a.props):a.props[t]=n},{path:e,key:t,value:n})},A=async(t,n,r)=>{x.playing&&x.stop();let a=await QD(t);v=t,g=n,_=r,y=!0,$(`#file-path`).textContent=t??n,$(`#file-chip`).title=`input: ${n}\noutput: ${r}`,p.hidden=!0,i.reset(a),e.mode===`project`&&zO.open(n,r)},ee=async()=>{let e=await YD();if(m.textContent=``,e.length===0){let e=document.createElement(`div`);e.className=`tree-row`,e.textContent=`no *.scene.json found — create one below`,m.appendChild(e)}for(let t of e){let e=document.createElement(`div`);e.className=`tree-row`,e.textContent=t.rel,e.addEventListener(`click`,()=>{A(t.rel,t.abs,t.abs).catch(e=>b(e instanceof Error?e.message:String(e)))}),m.appendChild(e)}p.hidden=!1},te=()=>{p.hidden=!0};if($(`#picker-close`).addEventListener(`click`,te),p.addEventListener(`pointerdown`,e=>{e.target===p&&te()}),$(`#picker-create`).addEventListener(`click`,()=>{(async()=>{let e=$(`#picker-path`).value.trim();if(e)try{let t=await XD(e.endsWith(`.scene.json`)?e:`${e}.scene.json`);await A(t.rel,t.abs,t.abs)}catch(e){b(e instanceof Error?e.message:String(e))}})()}),e.mode===`project`){h.hidden=!1,h.addEventListener(`click`,()=>void ee());let e=await YD(),t=e.length===1?e[0]:void 0;t?await A(t.rel,t.abs,t.abs):await ee()}else await A(void 0,e.input,e.output);$(`#add-btn`).addEventListener(`click`,()=>{let e=i.selection??[],t=f.value,n=i.insertNode({name:t,type:t},e);n&&i.select(n)});let ne=$(`#generate`);NA(i),$(`#generate-btn`).addEventListener(`click`,()=>jA(i));let re=$(`#confirm`),ie=e=>{if(i.selection===null&&e.length===0){b(`The scene itself cannot be deleted — it IS the file.`);return}if(e.length===0){let e=i.working.root;if(!e)return;let t=RM(e);se(`This deletes the ROOT '${String(e.name??``)}'${t>0?` AND its ${t} descendant node${t===1?``:`s`}`:``} — the scene goes empty, and the next node you add becomes the new root.`,`delete root`,()=>i.deleteRoot());return}let t=e[0]?.slice(0,-1)??[],n=i.findRemovalReferences(e);if(n.length===0){let n=e.reduce((e,t)=>{let n=i.nodeAt(t);return e+(n?RM(n):0)},0);if(n>0){se(`This deletes ${e.length>1?`${e.length} nodes`:`'${String(i.nodeAt(e[0]??[])?.name??``)}'`} AND ${n} descendant node${n===1?``:`s`}.`,`delete ${e.length+n} nodes`,()=>{i.removeNodes(e)&&i.select(t)});return}i.removeNodes(e)&&i.select(t);return}$(`#confirm-text`).textContent=`${n.length} reference${n.length>1?`s`:``} still point at the node(s) you are deleting. Unlink them and delete, or cancel.`;let r=$(`#confirm-list`);r.textContent=``;for(let e of n){let t=document.createElement(`div`);t.textContent=`${e.kind===`uid`?`◆ uid`:`⇄ connection`} ${e.where}`,r.appendChild(t)}$(`#confirm-title`).textContent=`still referenced`,$(`#confirm-delete`).textContent=`unlink & delete`,re.hidden=!1,ae={selections:e,parentOfFirst:t}},ae=null,oe=null;function se(e,t,n){$(`#confirm-title`).textContent=`are you sure?`,$(`#confirm-text`).textContent=e,$(`#confirm-list`).textContent=``,$(`#confirm-delete`).textContent=t,oe=n,re.hidden=!1}function j(){re.hidden=!0,ae=null,oe=null}$(`#confirm-close`).addEventListener(`click`,j),$(`#confirm-cancel`).addEventListener(`click`,j),re.addEventListener(`pointerdown`,e=>{e.target===re&&j()}),$(`#confirm-delete`).addEventListener(`click`,()=>{if(oe){oe(),j();return}ae&&i.removeNodesUnlinking(ae.selections)&&i.select(ae.parentOfFirst),j()}),Wj(ie),$(`#asset-add-btn`).addEventListener(`click`,e=>{e.stopPropagation(),i.startAddingAsset()}),$(`#group-add-btn`).addEventListener(`click`,e=>{e.stopPropagation(),i.startAddingGroup()});for(let e of document.querySelectorAll(`.section-toggle`)){let t=e.dataset.section??``,n=document.querySelector(`[data-body="${t}"]`),r=`incanto-editor-section-${t}`,i=t=>{e.classList.toggle(`collapsed`,t),n&&(n.hidden=t),localStorage.setItem(r,t?`1`:``)};i(localStorage.getItem(r)===`1`),e.addEventListener(`click`,()=>i(!e.classList.contains(`collapsed`)))}$(`#delete-btn`).addEventListener(`click`,()=>{ie(i.allSelections().filter(e=>e.length>0))}),l.addEventListener(`click`,()=>{if(x.playing){x.stop(),D();return}x.play(i.working).then(e=>{e&&b(e)})});for(let e of[`move`,`rotate`,`scale`])$(`#tool-${e}`).addEventListener(`click`,()=>x.setMode(e));x.setMode(`move`);let ce=$(`#tool-colliders`),le=`incanto-editor-show-colliders`,M=e=>{x.showColliders=e,ce.classList.toggle(`active`,e),localStorage.setItem(le,e?``:`0`)};M(localStorage.getItem(le)!==`0`),ce.addEventListener(`click`,()=>M(!x.showColliders));let ue=$(`#hints-dock`),fe=`incanto-editor-hints-open`;ue.classList.toggle(`open`,localStorage.getItem(fe)===`1`),$(`#hints-toggle`).addEventListener(`click`,()=>{let e=ue.classList.toggle(`open`);localStorage.setItem(fe,e?`1`:``)}),$(`#fit-btn`).addEventListener(`click`,()=>x.zoomToFit()),$(`#gameview-btn`).addEventListener(`click`,()=>x.gameView()),s.addEventListener(`click`,()=>{(async()=>{try{await $D(i.working,v),i.markSaved(),zO.save(g,_,i.working)}catch(e){b(e instanceof Error?e.message:String(e))}})()}),c.addEventListener(`click`,()=>i.undo()),window.addEventListener(`keydown`,e=>{if(e.key===`Escape`&&x.cancelActiveDrag(),e.key===`Escape`&&!$(`#docs`).hidden){qO();return}if(e.key===`Escape`&&!re.hidden){j();return}if(e.key===`Escape`&&!ne.hidden){MA();return}if(e.key===`Escape`&&!p.hidden){te();return}if(e.key===`Escape`&&x.playing){x.stop(),D();return}if(x.playing)return;let t=e.target.matches(`input, textarea, select`);(e.metaKey||e.ctrlKey)&&e.key===`z`?(e.preventDefault(),i.undo()):(e.metaKey||e.ctrlKey)&&e.key===`s`?(e.preventDefault(),s.disabled||s.click()):!t&&e.code===`KeyF`?x.zoomToFit():!t&&(e.code===`Digit0`||e.code===`Numpad0`)?x.gameView():!t&&e.code===`KeyW`?x.setMode(`move`):!t&&e.code===`KeyE`?x.setMode(`rotate`):!t&&e.code===`KeyR`&&x.setMode(`scale`)}),D(),zO.ready(g,_,e.version)}function RM(e){let t=0;for(let n of e.children??[])t+=1+RM(n);return t}function zM(e,t){let n=e.working.assets;return n?Object.entries(n).filter(([,e])=>e?.type===t).map(([e])=>`$${e}`):[]}function BM(e,t){let n=e.working.assets;return n?Object.entries(n).filter(([,e])=>!t||e?.type!==void 0&&t.includes(e.type)).map(([e])=>`$${e}`):[]}function VM(e,t,n){let r=e.working.assets??{};for(let[e,t]of Object.entries(r))if(t?.url===n.url)return`$${e}`;let i=t;for(let e=2;i in r;e++)i=`${t}-${e}`;return e.mutate(()=>{e.working.assets||(e.working.assets={}),e.working.assets[i]=n}),`$${i}`}LM().catch(e=>{let t=document.querySelector(`#error-banner`);t&&(t.hidden=!1,t.textContent=e instanceof Error?e.message:String(e))});export{Xf as t};
|