@hology/core 0.0.235 → 0.0.237
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/effects/sequence/sequence-player.d.ts +3 -0
- package/dist/effects/sequence/sequence-player.js +1 -1
- package/dist/effects/vfx/vfx-defs.d.ts +2 -0
- package/dist/effects/vfx/vfx-defs.js +1 -1
- package/dist/gameplay/actors/builtin/components/character/character-animation.d.ts +5 -0
- package/dist/gameplay/actors/builtin/components/character/character-animation.js +1 -1
- package/dist/gameplay/actors/builtin/components/character/character-movement-like.d.ts +2 -0
- package/dist/gameplay/actors/builtin/components/character/character-movement.d.ts +13 -0
- package/dist/gameplay/actors/builtin/components/character/character-movement.js +1 -1
- package/dist/gameplay/actors/builtin/components/character/root-motion-constraint.d.ts +7 -0
- package/dist/gameplay/actors/builtin/components/character/root-motion-constraint.js +4 -0
- package/dist/gameplay/actors/index.d.ts +1 -0
- package/dist/gameplay/actors/index.js +1 -1
- package/dist/rendering/node-material-webgpu-resolver.js +1 -1
- package/dist/test/character-animation-transition.test.js +1 -1
- package/dist/test/character-root-motion-constraint.test.d.ts +2 -0
- package/dist/test/character-root-motion-constraint.test.js +4 -0
- package/dist/test/node-material-webgpu-resolver.test.js +1 -1
- package/dist/test/sequence-animation-retiming.test.js +1 -1
- package/package.json +2 -2
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{describe as e,expect as t,it as o}from"vitest";import{DataTexture as a,Material as r,MeshStandardMaterial as n,RGBAFormat as s,SpriteMaterial as i,Vector4 as l}from"three";import{batchingUniformVec4 as d,float as h,NodeShaderMaterial as p,rgb as u,rgba as m,standardMaterial as c,translateX as f,uniformFloat as v,uniformVec4 as S,uniformSampler2d as g,varyingAttributes as w,vec3 as x,vec4 as y}from"three-shader-graph";import{createNodeMaterialSignature as C,markMaterialUniformValuesNeedUpdate as B,NodeMaterialWebGpuResolver as M,setMaterialUniformValuesAutoUpdate as T,withThreeDefaultVertexAttributeValues as W}from"../rendering/node-material-webgpu-resolver.js";import{LandscapeCompositeShader as L}from"../shader/builtin/landscape-composite-shader.js";import{DecalUnlitShader as b}from"../shader/builtin/decal-unlit-shader.js";import{getSpritePosition as _}from"../shader/sprite-shader.js";import{depthNormal as G,depthWorldPosition as A,fragmentLinearEyeDepth as U,sampleSceneDepth as V,screenUV as j}from"../shader-nodes/depth.js";import{sampleSceneColor as E}from"../shader-nodes/scene-sample.js";import{applyDithering as F}from"../shader-nodes/dither.js";import{parallaxOcclusionMapping as O}from"../shader-nodes/pom.js";import{elapsedTimeUniformName as q}from"../shader-nodes/time.js";import{Trail as I}from"../effects/vfx/trail-renderer.js";import{particleUniforms as D}from"../shader-nodes/particle.js";class P extends r{set uniformValuesNeedsUpdate(e){e&&this.uniformValuesAutoVersion++}constructor(e){super(),this.disposed=!1,this.uniformValuesAutoUpdate=!0,this.uniformValuesAutoVersion=0,this.vertexShader=e.vertexShader,this.fragmentShader=e.fragmentShader,this.depthVertexShader=e.depthVertexShader??null,this.depthFragmentShader=e.depthFragmentShader??null,this.pointShadowVertexShader=e.pointShadowVertexShader??null,this.pointShadowFragmentShader=e.pointShadowFragmentShader??null,this.uniforms=e.uniforms,this.sceneTextures=e.sceneTextures??{},this.graphTextures=e.graphTextures??{},this.vertexAttributes=e.vertexAttributes??[],this.transparent=e.transparent??!1,this.alphaTest=e.alphaTest??0,this.depthTest=e.depthTest??!0,this.depthWrite=e.depthWrite??!0,this.side=e.side??this.side}dispose(){this.disposed=!0,super.dispose()}}e("NodeMaterialWebGpuResolver",()=>{o("compiles decal surface rejection without undefined WGSL expressions",()=>{const e=(new b).build(),o=new M({WGSLShaderMaterial:P}).resolve(e);t(o?.fragmentShader).not.toContain("undefined"),t(o?.fragmentShader).toContain("hology_scene_mapTexture")}),o("recompiles an opaque variant when receiveDecals changes",()=>{const e=new p({color:m(16777215)});e.userData.receiveDecals=!0;const o=new M({WGSLShaderMaterial:P},e=>[m(16777215,!1===e.userData.receiveDecals?0:2)]),a=o.resolve(e,"opaque");e.userData.receiveDecals=!1,e.needsUpdate=!0;const r=o.resolve(e,"opaque");t(r).not.toBe(a),t(a.disposed).toBe(!0),t(a.fragmentShader).not.toBe(r.fragmentShader)}),o("maps Hology elapsed time to the renderer frame clock",()=>{const e=v(q),o=new p({color:m(x(e)),transform:f(e)}),a=new M({WGSLShaderMaterial:P}).resolve(o);t(a?.vertexShader).toContain("rendererElapsedTime()"),t(a?.fragmentShader).toContain("rendererElapsedTime()"),t(a?.depthVertexShader).toContain("rendererElapsedTime()"),t(a?.pointShadowVertexShader).toContain("rendererElapsedTime()"),t(a?.uniforms).not.toHaveProperty(q)}),o("compiles the native sprite billboard position without a GLSL fallback",()=>{const e=new p({color:m(16777215),position:_(v("rotation",.25),v("screenSpaceSize",0),v("lockYAxis",1))}),o=new M({WGSLShaderMaterial:P}).resolve(e);t(o).toBeInstanceOf(P),t(o?.vertexShader).toContain("cos("),t(o?.vertexShader).toContain("sin("),t(o?.vertexShader).toContain("materialUniforms.lockYAxis"),t(o?.vertexShader).not.toContain("customFunction_")}),o("uses the per-instance rotation attribute for instanced sprites",()=>{const e=new p({color:m(16777215),position:_(v("rotation",0),void 0,void 0,!0)}),o=new M({WGSLShaderMaterial:P}).resolve(e);t(o?.vertexAttributes).toEqual(t.arrayContaining([t.objectContaining({name:"rotation",format:"float32",stepMode:"instance"})])),t(o?.vertexShader).toContain("input.rotation"),t(o?.vertexShader).not.toContain("materialUniforms.rotation")}),o("uses per-instance particle opacity in WebGPU fragment shaders",()=>{const e=new p({color:m(u(16777215),D.opacity)}),o=new M({WGSLShaderMaterial:P}).resolve(e);t(o?.vertexAttributes).toEqual(t.arrayContaining([t.objectContaining({name:"particleData",format:"float32x3",stepMode:"instance"})])),t(o?.fragmentShader).toContain(".x"),t(o?.fragmentShader).toContain("> 0.5")}),o("maps THREE.SpriteMaterial to the native sprite shader",()=>{const e=new a(new Uint8Array([255,255,255,255]),1,1,s),o=new i({map:e,color:8429760,opacity:.6,rotation:.25,sizeAttenuation:!1,alphaTest:.2,transparent:!0}),r=new M({WGSLShaderMaterial:P}),n=r.resolve(o);t(n).toBeInstanceOf(P),t(n?.vertexShader).toContain("materialUniforms.rotation"),t(n?.vertexShader).toContain("materialUniforms.screenSpaceSize"),t(n?.fragmentShader).toContain("textureSample("),t(n?.alphaTest).toBe(.2),t(n?.transparent).toBe(!0),t(n?.uniforms.rotation.value).toBe(.25),t(n?.uniforms.screenSpaceSize.value).toBe(1),o.rotation=.75;const l=r.resolve(o);t(l).toBe(n),t(l?.uniforms.rotation.value).toBe(.75)}),o("memoizes node-material signatures for repeated lookups",()=>{const e=["version","vertexShader","fragmentShader","alphaTest","alphaToCoverage","blending","colorWrite","depthFunc","depthTest","depthWrite","polygonOffset","polygonOffsetFactor","polygonOffsetUnits","premultipliedAlpha","side","stencilWrite","transparent"],o=new Map([["version",1],["vertexShader","#vertex"],["fragmentShader","#fragment"],["alphaTest",0],["alphaToCoverage",!1],["blending",1],["colorWrite",!0],["depthFunc",3],["depthTest",!0],["depthWrite",!0],["polygonOffset",!1],["polygonOffsetFactor",0],["polygonOffsetUnits",0],["premultipliedAlpha",!1],["side",0],["stencilWrite",!1],["transparent",!1]]);let a=0;const r=new Proxy({},{get:(t,r)=>"string"==typeof r&&e.includes(r)?(a+=1,o.get(r)):Reflect.get(t,r)});C(r),C(r),t(a).toBe(e.length)}),o("adapts a NodeShaderMaterial without replacing its scene material",()=>{const e=v("intensity",.75),o=new p({color:c({color:u(5210052).multiplyScalar(e),roughness:.42,metalness:.18}),transparent:!0,alphaTest:.25}),a=new M({WGSLShaderMaterial:P}),r=a.resolve(o);t(r).toBeInstanceOf(P),t(r).not.toBe(o),t(r?.transparent).toBe(!0),t(r?.alphaTest).toBe(.25),t(r?.fragmentShader).toContain("rendererPointLightForLight"),t(o.isShaderMaterial).toBe(!0),e.value=.25,t(a.resolve(o)).toBe(r),t(r?.uniforms.intensity?.value).toBe(.25)}),o("syncs direct NodeShaderMaterial uniform mutations to the resolved material",()=>{const e=S("trailTint",new l(0,0,0,0)),o=v("hology_particle_energy",1),a=new p({color:e.multiplyScalar(o)}),r=new M({WGSLShaderMaterial:P});a.uniforms.trailTint.value=new l(1,.25,.5,.75),a.uniforms.hology_particle_energy.value=.4;const n=r.resolve(a);t(n?.uniforms.trailTint.value).toEqual(new l(1,.25,.5,.75)),t(n?.uniforms.hology_particle_energy.value).toBe(.4),a.uniforms.trailTint.value.set(.2,.4,.6,.8),a.uniforms.hology_particle_energy.value=.1,t(r.resolve(a)).toBe(n),t(n?.uniforms.trailTint.value).toEqual(new l(.2,.4,.6,.8)),t(n?.uniforms.hology_particle_energy.value).toBe(.1)}),o("only syncs explicitly invalidated uniforms when auto update is disabled",()=>{const e=v("intensity",.75),o=new p({color:m(x(e))});o.uniformValuesAutoUpdate=!1;const a=new M({WGSLShaderMaterial:P}),r=a.resolve(o),n=r.uniformValuesAutoVersion;e.value=.25,t(a.resolve(o)).toBe(r),t(r?.uniforms.intensity.value).toBe(.75),t(r?.uniformValuesAutoVersion).toBe(n),o.uniformValuesNeedsUpdate=!0,t(a.resolve(o)).toBe(r),t(r?.uniforms.intensity.value).toBe(.25),t(r?.uniformValuesAutoVersion).toBe(n+1)}),o("supports explicit invalidation for mapped Three.js materials",()=>{const e=new n({roughness:.8});T(e,!1);const o=new M({WGSLShaderMaterial:P}),a=o.resolve(e);e.roughness=.2,t(o.resolve(e)).toBe(a),t(a?.uniforms.roughness.value).toBe(.8),B(e),t(o.resolve(e)).toBe(a),t(a?.uniforms.roughness.value).toBe(.2)}),o("binds uniforms referenced only by an MRT material output",()=>{const e=v("roughness",.7),o=new p({color:m(5601177),alphaTest:.1});o.outputRoughness=e;const a=new M({WGSLShaderMaterial:P},e=>[e.outputColor,y(x(0,0,1),e.outputRoughness)]).resolve(o);t(a?.fragmentShader).toContain("materialUniforms.roughness"),t(a?.fragmentShader).not.toContain("graphColor"),t(a?.fragmentShader).toMatch(/if \(.+ < 0\.1\) \{ discard; \}/),t(a?.uniforms.roughness?.value).toBe(.7)}),o("compiles a landscape composite built from node material semantic outputs",()=>{const e=new M({WGSLShaderMaterial:P}),o=new p({color:c({color:u(5147450),roughness:.82,metalness:0})}),a=new p({color:c({color:u(7432286),roughness:.6,metalness:.05})}),r=new L;r.layers=[o,a];const n=e.resolve(r.build());t(n).toBeInstanceOf(P),t(n?.fragmentShader).toContain("rendererDirectionalLightForLight"),t(n?.vertexShader).toContain("@location(6) material: vec4<f32>"),t(n?.vertexShader).toContain("@location(8) hole: f32")}),o("allocates the trail's authored vertex attributes",()=>{const e=new M({WGSLShaderMaterial:P}).resolve(I.createBaseMaterial());t(e).toBeInstanceOf(P),t(e?.vertexAttributes).toEqual(t.arrayContaining([t.objectContaining({name:"nodeID",shaderLocation:6,format:"float32"}),t.objectContaining({name:"nodeCenter",shaderLocation:7,format:"float32x3"})]))}),o("uses Three's default value when a geometry has no uv1 attribute",()=>{const e=W([{name:"uv1",shaderLocation:6,format:"float32x2"},{name:"custom",shaderLocation:7,format:"float32"}]);t(e).toEqual(t.arrayContaining([t.objectContaining({name:"uv1",defaultValue:[0,0]}),t.objectContaining({name:"custom"})])),t(e[1]).not.toHaveProperty("defaultValue")}),o("compiles the foliage transform and named texture-uniform pattern",()=>{const e=new M({WGSLShaderMaterial:P}),o=new a(new Uint8Array([255,255,255,255]),1,1,s),r=new p({color:g("map").sample(w.uv),transform:f(v("windOffset",.25)),uniforms:{map:{value:o}},alphaTest:.5}),n=e.resolve(r);t(n).toBeInstanceOf(P),t(n?.vertexShader).toContain("rendererClipPosition(_ng_localPosition"),t(n?.depthVertexShader).toContain("materialUniforms.windOffset"),t(n?.depthFragmentShader).toContain("textureSample("),t(n?.depthFragmentShader).toContain("< 0.5) { discard;"),t(n?.pointShadowFragmentShader).toContain("distanceToLight")}),o("rebuilds cached materials when scene-local texture bindings change",()=>{const e=new M({WGSLShaderMaterial:P}),o=new a(new Uint8Array([255,255,255,255]),1,1,s),r=new a(new Uint8Array([32,64,128,255]),1,1,s),n=new p({color:g("map").sample(w.uv),uniforms:{map:{value:o}},alphaTest:.5}),i=e.resolve(n);n.uniforms.map.value=r;const l=e.resolve(n);t(l).not.toBe(i),t(i.disposed).toBe(!0),n.dispose();const d=e.resolve(n);t(d).not.toBe(l),t(l.disposed).toBe(!0)}),o("uses the renderer batch id without Three’s batchingIdTexture",()=>{const e=new M({WGSLShaderMaterial:P}),o=new a(new Uint8Array([255,96,24,255]),1,1,s),r=new p({color:m(d("vp0")),uniforms:{vp0:{value:o}}}),n=e.resolve(r);t(n).toBeInstanceOf(P),t(n?.vertexShader).toContain("rendererBatchingId(input.instanceIndex)")}),o("compiles depth nodes that use fragment-coordinate expressions",()=>{const e=new M({WGSLShaderMaterial:P}),o=new p({color:m(x(j.x,j.y,U))}),a=e.resolve(o);t(a).toBeInstanceOf(P),t(a?.fragmentShader).toContain("input.position.xy"),t(a?.fragmentShader).toContain("input.position.z"),t(a?.fragmentShader).toContain("rendererCamera.viewportAndTime.xy"),t(a?.fragmentShader).not.toContain("gl_FragCoord")}),o("maps scene color and depth nodes to renderer-owned textures",()=>{const e=new M({WGSLShaderMaterial:P}),o=new p({color:E(j).multiplyScalar(V(j))}),a=e.resolve(o);t(a.sceneTextures).toEqual({hology_scene_map:"sceneColor",hology_depth_map:"sceneDepth"}),t(a.graphTextures).toEqual({}),t(a.fragmentShader).toContain("hology_scene_mapTexture"),t(a.fragmentShader).toContain("hology_depth_mapTexture"),t(a.fragmentShader).toContain("vec4<f32>(textureSample(hology_depth_mapTexture")}),o("caches distinct material variants for opaque and late draw stages",()=>{const e=new M({WGSLShaderMaterial:P},(e,t)=>"opaque"===t?[m(x(1)),m(x(0))]:void 0),o=new p({color:m(x(1))}),a=e.resolve(o,"opaque"),r=e.resolve(o,"late");t(a).not.toBe(r),t(e.resolve(o,"opaque")).toBe(a),t(e.resolve(o,"late")).toBe(r),t(a?.fragmentShader).toContain("@location(1)"),t(r?.fragmentShader).not.toContain("@location(1)")}),o("reconstructs WebGPU depth positions with inverted NDC Y",()=>{const e=new M({WGSLShaderMaterial:P}),o=new p({color:m(A.add(G.multiplyScalar(0)))}),a=e.resolve(o);t(a.fragmentShader).toContain("vec2<f32>(2.0, -2.0)"),t(a.fragmentShader).toMatch(/-\(float_op_\d+\)/)}),o("emits dither and parallax nodes without GLSL custom functions",()=>{const e=new M({WGSLShaderMaterial:P}),o=new a(new Uint8Array([128,128,128,255]),1,1,s),r=O(w.uv,g("heightMap",o),h(.03)),n=new p({color:y(r.x,r.y,F(x(.2,.4,.8)).z,1)}),i=e.resolve(n);t(i).toBeInstanceOf(P),t(i?.fragmentShader).toContain("textureSampleLevel(heightMapTexture, heightMapSampler"),t(i?.fragmentShader).toMatch(/= -dpdy\(-pom_\d+ViewPosition\)/),t(i?.fragmentShader).toMatch(/= -dpdy\(pom_\d+Uv\)/),t(i?.fragmentShader).not.toContain("customFunction_"),t(i?.fragmentShader).not.toContain("gl_FragCoord")}),o("inherits, updates, and clears the WebGPU world environment",()=>{const e=new a(new Uint8Array([255,255,255,255]),1,1,s),o=new p({color:c({color:u(8421504),roughness:.5,metalness:.1})}),r=new M({WGSLShaderMaterial:P});r.setWorldEnvironment({texture:e,intensity:.75});const n=r.resolve(o);t(n.fragmentShader).toContain("envMapTexture"),t(n.uniforms.envMapIntensity?.value).toBe(.75),t(o.envMap).toBeUndefined(),r.setWorldEnvironment({texture:e,intensity:.25});const i=r.resolve(o);t(i).toBe(n),t(i.uniforms.envMapIntensity?.value).toBe(.25),r.setWorldEnvironment(null);const l=r.resolve(o);t(l).not.toBe(n),t(n.disposed).toBe(!0),t(l.fragmentShader).not.toContain("envMapTexture")}),o("keeps an authored material environment ahead of the world environment",()=>{const e=new a(new Uint8Array([255,255,255,255]),1,1,s),o=new a(new Uint8Array([64,96,128,255]),1,1,s),r=new p({color:c({color:u(8421504),roughness:.5,metalness:.1}),envMap:o}),n=new M({WGSLShaderMaterial:P});n.setWorldEnvironment({texture:e,intensity:.2}),n.resolve(r),t(r.uniforms.envMap.value).toBe(o),t(r.envMap).toBe(o)})});/*
|
|
1
|
+
import{describe as e,expect as t,it as o}from"vitest";import{DataTexture as a,Material as r,MeshStandardMaterial as n,RGBAFormat as s,SpriteMaterial as i,Vector4 as l}from"three";import{batchingUniformVec4 as d,float as h,NodeShaderMaterial as p,rgb as u,rgba as m,standardMaterial as c,translateX as f,uniformFloat as v,uniformVec4 as S,uniformSampler2d as g,varyingAttributes as w,vec3 as x,vec4 as y}from"three-shader-graph";import{createNodeMaterialSignature as C,markMaterialUniformValuesNeedUpdate as B,NodeMaterialWebGpuResolver as M,setMaterialUniformValuesAutoUpdate as T,withThreeDefaultVertexAttributeValues as W}from"../rendering/node-material-webgpu-resolver.js";import{LandscapeCompositeShader as L}from"../shader/builtin/landscape-composite-shader.js";import{DecalUnlitShader as b}from"../shader/builtin/decal-unlit-shader.js";import{getSpritePosition as _}from"../shader/sprite-shader.js";import{depthNormal as G,depthWorldPosition as U,fragmentLinearEyeDepth as A,sampleSceneDepth as V,screenUV as j}from"../shader-nodes/depth.js";import{sampleSceneColor as E}from"../shader-nodes/scene-sample.js";import{applyDithering as F}from"../shader-nodes/dither.js";import{parallaxOcclusionMapping as O}from"../shader-nodes/pom.js";import{elapsedTimeUniformName as q}from"../shader-nodes/time.js";import{Trail as I}from"../effects/vfx/trail-renderer.js";import{particleUniforms as D}from"../shader-nodes/particle.js";class P extends r{set uniformValuesNeedsUpdate(e){e&&this.uniformValuesAutoVersion++}constructor(e){super(),this.disposed=!1,this.uniformValuesAutoUpdate=!0,this.uniformValuesAutoVersion=0,this.vertexShader=e.vertexShader,this.fragmentShader=e.fragmentShader,this.depthVertexShader=e.depthVertexShader??null,this.depthFragmentShader=e.depthFragmentShader??null,this.pointShadowVertexShader=e.pointShadowVertexShader??null,this.pointShadowFragmentShader=e.pointShadowFragmentShader??null,this.uniforms=e.uniforms,this.sceneTextures=e.sceneTextures??{},this.graphTextures=e.graphTextures??{},this.vertexAttributes=e.vertexAttributes??[],this.transparent=e.transparent??!1,this.alphaTest=e.alphaTest??0,this.depthTest=e.depthTest??!0,this.depthWrite=e.depthWrite??!0,this.side=e.side??this.side}dispose(){this.disposed=!0,super.dispose()}}e("NodeMaterialWebGpuResolver",()=>{o("compiles decal surface rejection without undefined WGSL expressions",()=>{const e=(new b).build(),o=new M({WGSLShaderMaterial:P}).resolve(e);t(o?.fragmentShader).not.toContain("undefined"),t(o?.fragmentShader).toContain("hology_scene_mapTexture")}),o("recompiles an opaque variant when receiveDecals changes",()=>{const e=new p({color:m(16777215)});e.userData.receiveDecals=!0;const o=new M({WGSLShaderMaterial:P},e=>[m(16777215,!1===e.userData.receiveDecals?0:2)]),a=o.resolve(e,"opaque");e.userData.receiveDecals=!1,e.needsUpdate=!0;const r=o.resolve(e,"opaque");t(r).not.toBe(a),t(a.disposed).toBe(!0),t(a.fragmentShader).not.toBe(r.fragmentShader)}),o("maps Hology elapsed time to the renderer frame clock",()=>{const e=v(q),o=new p({color:m(x(e)),transform:f(e)}),a=new M({WGSLShaderMaterial:P}).resolve(o);t(a?.vertexShader).toContain("rendererElapsedTime()"),t(a?.fragmentShader).toContain("rendererElapsedTime()"),t(a?.depthVertexShader).toContain("rendererElapsedTime()"),t(a?.pointShadowVertexShader).toContain("rendererElapsedTime()"),t(a?.uniforms).not.toHaveProperty(q)}),o("compiles the native sprite billboard position without a GLSL fallback",()=>{const e=new p({color:m(16777215),position:_(v("rotation",.25),v("screenSpaceSize",0),v("lockYAxis",1))}),o=new M({WGSLShaderMaterial:P}).resolve(e);t(o).toBeInstanceOf(P),t(o?.vertexShader).toContain("cos("),t(o?.vertexShader).toContain("sin("),t(o?.vertexShader).toContain("materialUniforms.lockYAxis"),t(o?.vertexShader).not.toContain("customFunction_")}),o("uses the per-instance rotation attribute for instanced sprites",()=>{const e=new p({color:m(16777215),position:_(v("rotation",0),void 0,void 0,!0)}),o=new M({WGSLShaderMaterial:P}).resolve(e);t(o?.vertexAttributes).toEqual(t.arrayContaining([t.objectContaining({name:"rotation",format:"float32",stepMode:"instance"})])),t(o?.vertexShader).toContain("input.rotation"),t(o?.vertexShader).not.toContain("materialUniforms.rotation")}),o("uses per-instance particle opacity in WebGPU fragment shaders",()=>{const e=new p({color:m(u(16777215),D.opacity)}),o=new M({WGSLShaderMaterial:P}).resolve(e);t(o?.vertexAttributes).toEqual(t.arrayContaining([t.objectContaining({name:"particleData",format:"float32x3",stepMode:"instance"})])),t(o?.fragmentShader).toContain(".x"),t(o?.fragmentShader).toContain("> 0.5")}),o("maps THREE.SpriteMaterial to the native sprite shader",()=>{const e=new a(new Uint8Array([255,255,255,255]),1,1,s),o=new i({map:e,color:8429760,opacity:.6,rotation:.25,sizeAttenuation:!1,alphaTest:.2,transparent:!0}),r=new M({WGSLShaderMaterial:P}),n=r.resolve(o);t(n).toBeInstanceOf(P),t(n?.vertexShader).toContain("materialUniforms.rotation"),t(n?.vertexShader).toContain("materialUniforms.screenSpaceSize"),t(n?.fragmentShader).toContain("textureSample("),t(n?.alphaTest).toBe(.2),t(n?.transparent).toBe(!0),t(n?.uniforms.rotation.value).toBe(.25),t(n?.uniforms.screenSpaceSize.value).toBe(1),o.rotation=.75;const l=r.resolve(o);t(l).toBe(n),t(l?.uniforms.rotation.value).toBe(.75)}),o("memoizes node-material signatures for repeated lookups",()=>{const e=["version","vertexShader","fragmentShader","alphaTest","alphaToCoverage","blending","colorWrite","depthFunc","depthTest","depthWrite","polygonOffset","polygonOffsetFactor","polygonOffsetUnits","premultipliedAlpha","side","stencilWrite","transparent"],o=new Map([["version",1],["vertexShader","#vertex"],["fragmentShader","#fragment"],["alphaTest",0],["alphaToCoverage",!1],["blending",1],["colorWrite",!0],["depthFunc",3],["depthTest",!0],["depthWrite",!0],["polygonOffset",!1],["polygonOffsetFactor",0],["polygonOffsetUnits",0],["premultipliedAlpha",!1],["side",0],["stencilWrite",!1],["transparent",!1]]);let a=0;const r=new Proxy({},{get:(t,r)=>"string"==typeof r&&e.includes(r)?(a+=1,o.get(r)):Reflect.get(t,r)});C(r),C(r),t(a).toBe(e.length)}),o("adapts a NodeShaderMaterial without replacing its scene material",()=>{const e=v("intensity",.75),o=new p({color:c({color:u(5210052).multiplyScalar(e),roughness:.42,metalness:.18}),transparent:!0,alphaTest:.25}),a=new M({WGSLShaderMaterial:P}),r=a.resolve(o);t(r).toBeInstanceOf(P),t(r).not.toBe(o),t(r?.transparent).toBe(!0),t(r?.alphaTest).toBe(.25),t(r?.fragmentShader).toContain("rendererPointLightForLight"),t(o.isShaderMaterial).toBe(!0),e.value=.25,t(a.resolve(o)).toBe(r),t(r?.uniforms.intensity?.value).toBe(.25)}),o("syncs direct NodeShaderMaterial uniform mutations to the resolved material",()=>{const e=S("trailTint",new l(0,0,0,0)),o=v("hology_particle_energy",1),a=new p({color:e.multiplyScalar(o)}),r=new M({WGSLShaderMaterial:P});a.uniforms.trailTint.value=new l(1,.25,.5,.75),a.uniforms.hology_particle_energy.value=.4;const n=r.resolve(a);t(n?.uniforms.trailTint.value).toEqual(new l(1,.25,.5,.75)),t(n?.uniforms.hology_particle_energy.value).toBe(.4),a.uniforms.trailTint.value.set(.2,.4,.6,.8),a.uniforms.hology_particle_energy.value=.1,t(r.resolve(a)).toBe(n),t(n?.uniforms.trailTint.value).toEqual(new l(.2,.4,.6,.8)),t(n?.uniforms.hology_particle_energy.value).toBe(.1)}),o("only syncs explicitly invalidated uniforms when auto update is disabled",()=>{const e=v("intensity",.75),o=new p({color:m(x(e))});o.uniformValuesAutoUpdate=!1;const a=new M({WGSLShaderMaterial:P}),r=a.resolve(o),n=r.uniformValuesAutoVersion;e.value=.25,t(a.resolve(o)).toBe(r),t(r?.uniforms.intensity.value).toBe(.75),t(r?.uniformValuesAutoVersion).toBe(n),o.uniformValuesNeedsUpdate=!0,t(a.resolve(o)).toBe(r),t(r?.uniforms.intensity.value).toBe(.25),t(r?.uniformValuesAutoVersion).toBe(n+1)}),o("supports explicit invalidation for mapped Three.js materials",()=>{const e=new n({roughness:.8});T(e,!1);const o=new M({WGSLShaderMaterial:P}),a=o.resolve(e);e.roughness=.2,t(o.resolve(e)).toBe(a),t(a?.uniforms.roughness.value).toBe(.8),B(e),t(o.resolve(e)).toBe(a),t(a?.uniforms.roughness.value).toBe(.2)}),o("binds uniforms referenced only by an MRT material output",()=>{const e=v("roughness",.7),o=new p({color:m(5601177),alphaTest:.1});o.outputRoughness=e;const a=new M({WGSLShaderMaterial:P},e=>[e.outputColor,y(x(0,0,1),e.outputRoughness)]).resolve(o);t(a?.fragmentShader).toContain("materialUniforms.roughness"),t(a?.fragmentShader).not.toContain("graphColor"),t(a?.fragmentShader).toMatch(/if \(.+ < 0\.1\) \{ discard; \}/),t(a?.uniforms.roughness?.value).toBe(.7)}),o("compiles a landscape composite built from node material semantic outputs",()=>{const e=new M({WGSLShaderMaterial:P}),o=new p({color:c({color:u(5147450),roughness:.82,metalness:0})}),a=new p({color:c({color:u(7432286),roughness:.6,metalness:.05})}),r=new L;r.layers=[o,a];const n=e.resolve(r.build());t(n).toBeInstanceOf(P),t(n?.fragmentShader).toContain("rendererDirectionalLightForLight"),t(n?.vertexShader).toContain("@location(6) material: vec4<f32>"),t(n?.vertexShader).toContain("@location(8) hole: f32")}),o("allocates the trail's authored vertex attributes",()=>{const e=new M({WGSLShaderMaterial:P}).resolve(I.createBaseMaterial());t(e).toBeInstanceOf(P),t(e?.vertexAttributes).toEqual(t.arrayContaining([t.objectContaining({name:"nodeID",shaderLocation:6,format:"float32"}),t.objectContaining({name:"nodeCenter",shaderLocation:7,format:"float32x3"})]))}),o("uses Three's default value when a geometry has no uv1 attribute",()=>{const e=W([{name:"uv1",shaderLocation:6,format:"float32x2"},{name:"custom",shaderLocation:7,format:"float32"}]);t(e).toEqual(t.arrayContaining([t.objectContaining({name:"uv1",defaultValue:[0,0]}),t.objectContaining({name:"custom"})])),t(e[1]).not.toHaveProperty("defaultValue")}),o("compiles the foliage transform and named texture-uniform pattern",()=>{const e=new M({WGSLShaderMaterial:P}),o=new a(new Uint8Array([255,255,255,255]),1,1,s),r=new p({color:g("map").sample(w.uv),transform:f(v("windOffset",.25)),uniforms:{map:{value:o}},alphaTest:.5}),n=e.resolve(r);t(n).toBeInstanceOf(P),t(n?.vertexShader).toContain("rendererClipPosition(_ng_localPosition"),t(n?.depthVertexShader).toContain("materialUniforms.windOffset"),t(n?.depthFragmentShader).toContain("textureSample("),t(n?.depthFragmentShader).toContain("< 0.5) { discard;"),t(n?.pointShadowFragmentShader).toContain("distanceToLight")}),o("rebuilds cached materials when scene-local texture bindings change",()=>{const e=new M({WGSLShaderMaterial:P}),o=new a(new Uint8Array([255,255,255,255]),1,1,s),r=new a(new Uint8Array([32,64,128,255]),1,1,s),n=new p({color:g("map").sample(w.uv),uniforms:{map:{value:o}},alphaTest:.5}),i=e.resolve(n);n.uniforms.map.value=r;const l=e.resolve(n);t(l).not.toBe(i),t(i.disposed).toBe(!0),n.dispose();const d=e.resolve(n);t(d).not.toBe(l),t(l.disposed).toBe(!0)}),o("uses the renderer batch id without Three’s batchingIdTexture",()=>{const e=new M({WGSLShaderMaterial:P}),o=new a(new Uint8Array([255,96,24,255]),1,1,s),r=new p({color:m(d("vp0")),uniforms:{vp0:{value:o}}}),n=e.resolve(r);t(n).toBeInstanceOf(P),t(n?.vertexShader).toContain("rendererBatchingId(input.instanceIndex)")}),o("compiles depth nodes that use fragment-coordinate expressions",()=>{const e=new M({WGSLShaderMaterial:P}),o=new p({color:m(x(j.x,j.y,A))}),a=e.resolve(o);t(a).toBeInstanceOf(P),t(a?.fragmentShader).toContain("input.position.xy"),t(a?.fragmentShader).toContain("input.position.z"),t(a?.fragmentShader).toContain("rendererViewportSize()"),t(a?.fragmentShader).not.toContain("gl_FragCoord")}),o("maps scene color and depth nodes to renderer-owned textures",()=>{const e=new M({WGSLShaderMaterial:P}),o=new p({color:E(j).multiplyScalar(V(j))}),a=e.resolve(o);t(a.sceneTextures).toEqual({hology_scene_map:"sceneColor",hology_depth_map:"sceneDepth"}),t(a.graphTextures).toEqual({}),t(a.fragmentShader).toContain("hology_scene_mapTexture"),t(a.fragmentShader).toContain("hology_depth_mapTexture"),t(a.fragmentShader).toContain("vec4<f32>(textureSample(hology_depth_mapTexture")}),o("caches distinct material variants for opaque and late draw stages",()=>{const e=new M({WGSLShaderMaterial:P},(e,t)=>"opaque"===t?[m(x(1)),m(x(0))]:void 0),o=new p({color:m(x(1))}),a=e.resolve(o,"opaque"),r=e.resolve(o,"late");t(a).not.toBe(r),t(e.resolve(o,"opaque")).toBe(a),t(e.resolve(o,"late")).toBe(r),t(a?.fragmentShader).toContain("@location(1)"),t(r?.fragmentShader).not.toContain("@location(1)")}),o("reconstructs WebGPU depth positions with inverted NDC Y",()=>{const e=new M({WGSLShaderMaterial:P}),o=new p({color:m(U.add(G.multiplyScalar(0)))}),a=e.resolve(o);t(a.fragmentShader).toContain("vec2<f32>(2.0, -2.0)"),t(a.fragmentShader).toMatch(/-\(float_op_\d+\)/)}),o("emits dither and parallax nodes without GLSL custom functions",()=>{const e=new M({WGSLShaderMaterial:P}),o=new a(new Uint8Array([128,128,128,255]),1,1,s),r=O(w.uv,g("heightMap",o),h(.03)),n=new p({color:y(r.x,r.y,F(x(.2,.4,.8)).z,1)}),i=e.resolve(n);t(i).toBeInstanceOf(P),t(i?.fragmentShader).toContain("textureSampleLevel(heightMapTexture, heightMapSampler"),t(i?.fragmentShader).toMatch(/= -dpdy\(-pom_\d+ViewPosition\)/),t(i?.fragmentShader).toMatch(/= -dpdy\(pom_\d+Uv\)/),t(i?.fragmentShader).not.toContain("customFunction_"),t(i?.fragmentShader).not.toContain("gl_FragCoord")}),o("inherits, updates, and clears the WebGPU world environment",()=>{const e=new a(new Uint8Array([255,255,255,255]),1,1,s),o=new p({color:c({color:u(8421504),roughness:.5,metalness:.1})}),r=new M({WGSLShaderMaterial:P});r.setWorldEnvironment({texture:e,intensity:.75});const n=r.resolve(o);t(n.fragmentShader).toContain("envMapTexture"),t(n.uniforms.envMapIntensity?.value).toBe(.75),t(o.envMap).toBeUndefined(),r.setWorldEnvironment({texture:e,intensity:.25});const i=r.resolve(o);t(i).toBe(n),t(i.uniforms.envMapIntensity?.value).toBe(.25),r.setWorldEnvironment(null);const l=r.resolve(o);t(l).not.toBe(n),t(n.disposed).toBe(!0),t(l.fragmentShader).not.toContain("envMapTexture")}),o("keeps an authored material environment ahead of the world environment",()=>{const e=new a(new Uint8Array([255,255,255,255]),1,1,s),o=new a(new Uint8Array([64,96,128,255]),1,1,s),r=new p({color:c({color:u(8421504),roughness:.5,metalness:.1}),envMap:o}),n=new M({WGSLShaderMaterial:P});n.setWorldEnvironment({texture:e,intensity:.2}),n.resolve(r),t(r.uniforms.envMap.value).toBe(o),t(r.envMap).toBe(o)})});/*
|
|
2
2
|
* Copyright (©) 2026 Hology Interactive AB. All rights reserved.
|
|
3
3
|
* See the LICENSE.md file for details.
|
|
4
4
|
*/
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{expect as e,test as t,vi as o}from"vitest";import*as i from"three";import{BaseActor as n}from"../gameplay/actors/actor.js";import{CharacterAnimationComponent as s,OldCharacterMovementComponent as
|
|
1
|
+
import{expect as e,test as t,vi as o}from"vitest";import*as i from"three";import{BaseActor as n}from"../gameplay/actors/actor.js";import{CharacterAnimationComponent as s,OldCharacterMovementComponent as a}from"../gameplay/actors/index.js";import{RootMotionClip as r}from"../gameplay/animation/root-motion.js";import{createAnimationClipInstance as c,createAnimationSubTrack as l,createSpawnClipInstance as m,createSpawnTrack as p}from"../effects/sequence/sequence-data.js";import{evaluateAnimationClipOutputProgress as u,evaluateAnimationClipSourceProgress as d,normalizeAnimationClipRetiming as h,buildRetimedAnimationClip as f}from"../effects/sequence/sequence-animation-retiming.js";import{SequencePlayer as g}from"../effects/sequence/sequence-player.js";function A(e){return Array.from(e).map(e=>Math.round(1e3*e)/1e3)}o.mock("../gameplay/actors/builtin/index.js",()=>({builtInActors:{},default:{}})),o.mock("../gameplay/actors/index.js",()=>({CharacterAnimationComponent:class{},OldCharacterMovementComponent:class{},getCharacterMovementLike:e=>e.movement??void 0})),o.mock("../effects/vfx/vfx-actor.js",()=>({VfxActor:class{}})),o.mock("../effects/vfx/vfx-param.js",()=>({VisualEffect:class{}})),o.mock("../effects/vfx/vfx-service.js",()=>({VfxService:class{}})),o.hoisted(()=>{if("undefined"==typeof AudioBuffer&&Object.defineProperty(globalThis,"AudioBuffer",{configurable:!0,value:class{}}),"undefined"==typeof HTMLCanvasElement)return;const e=new Proxy({},{get:(e,t)=>(t in e||(e[t]=("string"!=typeof t||!t.startsWith("is"))&&o.fn()),e[t]),set:(e,t,o)=>(e[t]=o,!0)});Object.defineProperty(HTMLCanvasElement.prototype,"getContext",{configurable:!0,value:()=>e})}),t("uniform retiming mapping stays linear",()=>{e(d(void 0,.25)).toBeCloseTo(.25),e(u(void 0,.75)).toBeCloseTo(.75),e(d({mode:"uniform",points:[]},.6)).toBeCloseTo(.6)}),t("impact retiming redistributes time around the impact marker",()=>{const t={mode:"impact",points:[{id:"impact",clipProgress:.35,sourceProgress:.55}]};e(d(t,.35)).toBeCloseTo(.55),e(u(t,.55)).toBeCloseTo(.35),e(d(t,.7)).toBeGreaterThan(.7)}),t("advanced retiming sanitization stays monotonic and bounded",()=>{const t=h({mode:"advanced",points:[{id:"a",clipProgress:.9,sourceProgress:.2},{id:"b",clipProgress:-4,sourceProgress:.8},{id:"c",clipProgress:.9,sourceProgress:2},{id:"d",clipProgress:.4,sourceProgress:.1}]});e(t.points).toHaveLength(3);for(let o=1;o<t.points.length;o++)e(t.points[o].clipProgress).toBeGreaterThan(t.points[o-1].clipProgress),e(t.points[o].sourceProgress).toBeGreaterThan(t.points[o-1].sourceProgress);e(t.points[0].clipProgress).toBeGreaterThan(0),e(t.points[t.points.length-1].sourceProgress).toBeLessThan(1)}),t("retimed clip baking preserves trim boundary poses and remaps track times to clip duration",()=>{const t=new i.AnimationClip("trim-test",2,[new i.NumberKeyframeTrack("weight",[0,1,2],[0,10,20])]),o=c("anim",0,1);o.clipStartOffset=.5,o.clipEndOffset=.25,o.retiming={mode:"advanced",points:[]};const n=f(t,o),s=n.tracks[0];e(n.duration).toBeCloseTo(1),e(A(s.times)).toEqual([0,.4,1]),e(A(s.values)).toEqual([5,10,17.5])}),t("root motion track baking stays aligned with the retimed visual clip",()=>{const t=new i.AnimationClip("root-motion-test",2,[new i.VectorKeyframeTrack("hips.position",[0,1,2],[0,0,0,1,0,0,2,0,0])]),o=c("anim",0,1);o.retiming={mode:"impact",points:[{id:"impact",clipProgress:.25,sourceProgress:.5}]};const n=f(t,o),s=n.tracks[0],a=r.fromClip(n,!1);e(A(s.times)).toEqual([0,.25,1]),e(A(a.motionTrack.times)).toEqual([0,.25,1]),e(A(a.motionTrack.values)).toEqual([0,0,0,1,0,0,2,0,0])}),t("sequence player keeps uniform clips on the existing playback path",()=>{const t=new g,o=l(),n=c("anim",0,1);n.playbackRate=1.75,n.clipStartOffset=.25,o.clips.push(n);const s=new i.AnimationClip("uniform-test",2,[new i.NumberKeyframeTrack("weight",[0,1,2],[0,1,2])]);t.animationClips.set("anim",s);const a=new i.Object3D,r={target:a,mixer:new i.AnimationMixer(a),activeActions:new Map};t.evaluateAnimationSubTrack(o,r,.1);const m=r.activeActions.get(`${o.id}-${n.id}`);e(m).toBeDefined(),e(m.getClip()).toBe(s),e(m.timeScale).toBeCloseTo(1.75),e(m.time).toBeCloseTo(.25)}),t("sequence player uses baked retimed clips and still wires root motion through the action",()=>{const t=new g,o=l(),n=c("anim",0,1.5);n.rootMotion=!0,n.playbackRate=2,n.retiming={mode:"impact",points:[{id:"impact",clipProgress:.3,sourceProgress:.55}]},o.clips.push(n);const s=new i.AnimationClip("retimed-root",2,[new i.VectorKeyframeTrack("hips.position",[0,1,2],[0,0,0,1,0,0,2,0,0]),new i.NumberKeyframeTrack("weight",[0,1,2],[0,1,2])]);t.animationClips.set("anim",s);const a=new y,m=new v(a.object),p=new w;a.charAnim=m,a.movement=p;const u={target:a,activeActions:new Map};t.evaluateAnimationSubTrack(o,u,.1);const d=u.activeActions.get(`${o.id}-${n.id}`);e(d).toBeDefined(),e(d.timeScale).toBeCloseTo(1),e(d.getClip()).toBeInstanceOf(r),e(d.getClip().duration).toBeCloseTo(1.5),e(p.lastRootMotionAction).toBe(d),e(t.retimedAnimationClips.size).toBe(1)}),t("external sequence control previews root motion deterministically while scrubbing",()=>{const t=new g;t.externalTimeControl=!0;const o=l(),n=c("anim",0,2);n.rootMotion=!0,o.clips.push(n);const s=new i.AnimationClip("root-motion-preview",2,[new i.VectorKeyframeTrack("hips.position",[0,1,2],[0,0,0,1,1,0,2,2,0])]);t.animationClips.set("anim",s);const a=new y;a.object.position.set(10,3,0),a.object.scale.setScalar(2);const r=new i.Bone;r.name="hips",a.object.add(r),a.charAnim=new v(a.object);const m={target:a,activeActions:new Map};t.evaluateSubTracks([o],m,.25),e(a.object.position.x).toBeCloseTo(10.5),e(a.object.position.y).toBeCloseTo(3),t.evaluateSubTracks([o],m,1.5),e(a.object.position.x).toBeCloseTo(13),e(a.object.position.y).toBeCloseTo(3),t.evaluateSubTracks([o],m,.5),e(a.object.position.x).toBeCloseTo(11),e(a.object.position.y).toBeCloseTo(3)}),t("ending a role-bound spawn proxy releases its clamped character animation",()=>{const t=new g,o=p(),n=m(0,1),s=l(),a=c("anim",0,1);o.clips.push(n),o.subTracks.push(s),s.clips.push(a);const r=new y,u=new v(r.object);r.charAnim=u,u.play(new i.AnimationClip("ability",1,[]));const d=`${s.id}-${a.id}`,h=`${o.id}-${n.id}`,f={track:o,target:r,activeActions:new Map([[d,u.getFullBodyAction()]]),charAnimComponent:u,charAnimActionKeys:new Set([d])};t.activeClips.add(h),t.spawnedInstances.set(h,{type:"proxy",target:r}),t.evaluateSpawnTrack(o,f,1),e(f.activeActions.size).toBe(0),e(f.charAnimActionKeys.size).toBe(0),e(u.sequenceReleaseRequests).toBe(1)}),t("sequence completion releases character animation when its proxy overhangs the timeline",()=>{const t=new g,o=p(),n=l(),s=c("anim",0,2.06);o.clips.push(m(0,2.07)),o.subTracks.push(n),n.clips.push(s);const a=new y,r=new v(a.object);a.charAnim=r,r.play(new i.AnimationClip("overhanging-ability",2.06,[]));const u=`${n.id}-${s.id}`,d={track:o,target:a,activeActions:new Map([[u,r.getFullBodyAction()]]),charAnimComponent:r,charAnimActionKeys:new Set([u])};t.bindings.set(o.id,d),t.deactivateCharacterAnimationActions(),e(d.activeActions.size).toBe(0),e(d.charAnimActionKeys.size).toBe(0),e(r.sequenceReleaseRequests).toBe(1)});class y extends n{constructor(){super(...arguments),this.charAnim=null,this.movement=null}getComponent(e){return e===s?this.charAnim:e===a?this.movement:void 0}}class v{constructor(e){this.fullBodyAction=null,this.sequenceReleaseRequests=0,this.mixer=new i.AnimationMixer(e)}play(e,t={}){this.fullBodyAction=this.mixer.clipAction(e),this.fullBodyAction.setLoop(i.LoopOnce,1),this.fullBodyAction.clampWhenFinished=!0,this.fullBodyAction.timeScale=t.timeScale??1,null!=t.offset&&(this.fullBodyAction.time=t.offset),this.fullBodyAction.play()}beginExternalAnimationControl(e,t={}){return this.play(e,t),this.getFullBodyAction()}getFullBodyAction(){if(null==this.fullBodyAction)throw new Error("No full body action is active");return this.fullBodyAction}getMixer(){return this.mixer}beginExternalControl(){}endExternalControl(){}stopSequenceAnimation(){}requestSequenceAnimationRelease(){this.sequenceReleaseRequests++}}class w{constructor(){this.lastRootMotionAction=null}setRootMotionAction(e){this.lastRootMotionAction=e}}/*
|
|
2
2
|
* Copyright (©) 2026 Hology Interactive AB. All rights reserved.
|
|
3
3
|
* See the LICENSE.md file for details.
|
|
4
4
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hology/core",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.237",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -198,7 +198,7 @@
|
|
|
198
198
|
"dependencies": {
|
|
199
199
|
"@babel/runtime": "^7.24.8",
|
|
200
200
|
"@dimforge/rapier3d-simd-compat": "^0.19.3",
|
|
201
|
-
"@hology/nebula": "^0.0.
|
|
201
|
+
"@hology/nebula": "^0.0.160",
|
|
202
202
|
"@plumier/reflect": "^1.1.0",
|
|
203
203
|
"@recast-navigation/three": "0.39.0",
|
|
204
204
|
"recast-navigation": "0.39.0",
|