@hology/core 0.0.209 → 0.0.211

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.
Files changed (46) hide show
  1. package/dist/data/surface-definition.d.ts +3 -0
  2. package/dist/data/surface-definition.js +4 -0
  3. package/dist/effects/sequence/sequence-player.js +1 -1
  4. package/dist/effects/vfx/vfx-materializer.js +1 -1
  5. package/dist/gameplay/actors/builtin/components/character/character-movement.js +1 -1
  6. package/dist/gameplay/actors/builtin/navmesh-actor.js +1 -1
  7. package/dist/gameplay/services/physics/abstract-physics-system.d.ts +1 -1
  8. package/dist/gameplay/services/physics/physics-system.d.ts +2 -1
  9. package/dist/gameplay/services/physics/physics-system.js +1 -1
  10. package/dist/index.d.ts +2 -0
  11. package/dist/index.js +1 -1
  12. package/dist/rendering.js +1 -1
  13. package/dist/scene/batched-mesh-2.d.ts +2 -0
  14. package/dist/scene/batched-mesh-2.js +1 -1
  15. package/dist/scene/custom-param-runtime-types.js +1 -1
  16. package/dist/scene/landscape/landscape-manager.d.ts +2 -1
  17. package/dist/scene/landscape/landscape-manager.js +1 -1
  18. package/dist/scene/materializer.d.ts +48 -1
  19. package/dist/scene/materializer.js +1 -1
  20. package/dist/scene/model.d.ts +3 -0
  21. package/dist/scene/scatter/painted-scatter-manager.d.ts +45 -0
  22. package/dist/scene/scatter/painted-scatter-manager.js +4 -0
  23. package/dist/scene/scatter/scatter-limits.d.ts +2 -0
  24. package/dist/scene/scatter/scatter-limits.js +4 -0
  25. package/dist/scene/scatter/surface-scatter-manager.js +1 -1
  26. package/dist/scene/storage/storage.js +1 -1
  27. package/dist/scene/surface-query.d.ts +4 -0
  28. package/dist/scene/surface-query.js +4 -0
  29. package/dist/shader/builtin/landscape-composite-shader.d.ts +1 -0
  30. package/dist/shader/builtin/landscape-composite-shader.js +1 -1
  31. package/dist/shader/builtin/standard-shader.js +1 -1
  32. package/dist/shader/graph/compiler.d.ts +7 -4
  33. package/dist/shader/graph/compiler.js +1 -1
  34. package/dist/shader/graph/model.d.ts +1 -1
  35. package/dist/shader/graph/model.js +1 -1
  36. package/dist/shader/graph/parameters.js +1 -1
  37. package/dist/shader/sprite-shader.js +1 -1
  38. package/dist/test/material-assignment.test.js +1 -1
  39. package/dist/test/materializer-prefetch.test.js +1 -1
  40. package/dist/test/painted-scatter-manager.test.d.ts +2 -0
  41. package/dist/test/painted-scatter-manager.test.js +4 -0
  42. package/dist/test/runtime-param-type-inference.test.js +1 -1
  43. package/dist/test/sequence-post-process.test.js +1 -1
  44. package/dist/test/shader-graph.test.js +1 -1
  45. package/package.json +2 -2
  46. package/tsconfig.tsbuildinfo +1 -1
@@ -1,4 +1,4 @@
1
- import{Color as e,Euler as r,Material as t,Object3D as n,Texture as u,Vector2 as o,Vector3 as a,Vector4 as c,AnimationClip as l}from"three";import{BooleanNode as i,FloatNode as s,RgbNode as f,Sampler2DNode as y,Texture2dLookupNode as p,Vec2Node as m,Vec3Node as d,Vec4Node as b}from"three-shader-graph";import{getParameterDefinitionId as A,getParameterDefinitionType as g,isParameterDefinitionStructAssignableTo as S}from"../shader/parameter.js";import{Curve2 as N}from"../utils/curve.js";import{SerializedParamType as V}from"./model.js";export const applyRuntimeParamTypeInference=!0;export function convertConfiguredParamsToRuntimeTypes(e,r={}){const t={},n=[],u=Object.entries(e),o=r.applyRuntimeParamTypeInference??true;if(0===u.length||!o)return o?{params:t,skipped:n}:{params:e,skipped:n};const a=getRuntimeParameterTypeHints(e,r);for(const[e,r]of u){if(!1===r.override){t[e]=r;continue}const u=a.get(e),o=null!=u?convertConfiguredParamValueToRuntimeType(r,u):r;null!=o?t[e]=o:n.push({key:e,sourceType:r.type,targetType:u.type})}return{params:t,skipped:n}}export function getRuntimeParameterTypeHints(e,r={}){const t=new Map,n=Object.keys(e).filter(r=>!1!==e[r]?.override);if(0===n.length)return t;const u=new Set(n),o=r.parameterTarget??function(e){if(null==e)return null;try{return new e}catch{return null}}(r.parameterType),a=function(e,r){if(null!=r.propertyParams)return r.propertyParams;if(null==r.extractPropertyParameters)return[];if(null!=r.parameterType)return h(r.parameterType,r.extractPropertyParameters);if(null!=e)return h(e.constructor,r.extractPropertyParameters);return[]}(o,r);for(const e of a){if(!u.has(e.name))continue;const n=R(e,r.toSerializedParamType);null!=n&&t.set(e.name,n);const a=inferRuntimeSerializedParamTypeHint(C(o,e.name))??inferRuntimeSerializedParamTypeHint(e.options.defaultValue);null!=a&&t.set(e.name,x(a,n))}if(null!=o)for(const e of n){const r=inferRuntimeSerializedParamTypeHint(C(o,e));if(null!=r){const n=t.get(e);t.set(e,x(r,n))}}return t}const v=new WeakMap,P=new WeakMap,T=Symbol("noCachedSerializedParamType");function h(e,r){let t=v.get(r);null==t&&(t=new WeakMap,v.set(r,t));const n=t.get(e);if(null!=n)return n;const u=function(e,r){try{return r(e)}catch{return[]}}(e,r);return t.set(e,u),u}function C(e,r){return null!=e?e[r]:void 0}function R(e,r){const t=function(e,r){if(null==e||null==r)return;const t=function(e,r){const t=P.get(r);if(null==t||!t.has(e))return T;const n=t.get(e);return null===n?void 0:n}(e,r);if(t!==T)return t;try{const t=r(e);return w(e,r,t),t}catch{return void w(e,r,void 0)}}(e.type,r),n=t===V.Struct?A(e.type):void 0;return!0===e.options.array?{type:V.Array,...null!=t?{element:t}:{},...null!=n?{elementStruct:n}:{}}:null!=t?{type:t,...null!=n?{struct:n}:{}}:void 0}function w(e,r,t){let n=P.get(r);null==n&&(n=new WeakMap,P.set(r,n)),n.set(e,t??null)}function x(e,r){return e.type!==V.Array?e:{type:V.Array,element:e.element??r?.element,elementStruct:e.elementStruct??r?.elementStruct}}export function inferRuntimeSerializedParamTypeHint(g){if(null==g)return;if(Array.isArray(g)){const e=g.map(e=>inferRuntimeSerializedParamTypeHint(e)).find(e=>null!=e);return{type:V.Array,...null!=e&&e.type!==V.Array?{element:e.type}:{},...null!=e?.struct?{elementStruct:e.struct}:{}}}switch(typeof g){case"number":return{type:V.Number};case"boolean":return{type:V.Boolean};case"string":return{type:V.String}}const S=function(e){const r=e;if(e instanceof s||"function"==typeof r?.isFloat||!0===r?.isFloat)return V.FloatNode;if(e instanceof i||"function"==typeof r?.isBool||!0===r?.isBool)return V.BooleanNode;if(e instanceof f||"function"==typeof r?.isRgb||!0===r?.isRgb)return V.RgbNode;if(e instanceof m||"function"==typeof r?.isVec2||!0===r?.isVec2)return V.Vec2Node;if(e instanceof d||"function"==typeof r?.isVec3||!0===r?.isVec3)return V.Vec3Node;if(e instanceof b||"function"==typeof r?.isVec4||!0===r?.isVec4)return V.Vec4Node;if(e instanceof y||e instanceof p||"function"==typeof r?.isSampler2D||!0===r?.isSampler2D)return V.Sampler2DNode;return}(g);if(null!=S)return{type:S};if(g instanceof o)return{type:V.Vector2};if(g instanceof a)return{type:V.Vector3};if(g instanceof c)return{type:V.Vector4};if(g instanceof e)return{type:V.Color};if(g instanceof r)return{type:V.Euler};if(g instanceof u)return{type:V.Texture};if(g instanceof t)return{type:V.Material};if(g instanceof n)return{type:V.Object3D};if("undefined"!=typeof AudioBuffer&&g instanceof AudioBuffer)return{type:V.AudioBuffer};if(W(g,"VisualEffect"))return{type:V.VisualEffect};if(W(g,"PrefabOf"))return{type:V.PrefabActor};if(W(g,"Prefab"))return{type:V.Prefab};if(W(g,"DataAssetRef"))return{type:V.DataAsset};if(g instanceof N)return{type:V.Curve};if(W(g,"ColorLayer"))return{type:V.ColorLayer};if(W(g,"MaskLayer"))return{type:V.MaskLayer};if(g instanceof l)return{type:V.AnimationClip};if(W(g,"Sequence"))return{type:V.Sequence};const v=A(g.constructor);return null!=v?{type:V.Struct,struct:v}:void 0}export function convertConfiguredParamValueToRuntimeType(e,r){if(null==e||null==r)return e;if(e.type===V.Array||r.type===V.Array)return function(e,r){if(r.type===V.Array){const t=B(e),n=r.element??(t?e.element:e.type);if(null==n)return t?e:null;const u=t?e.element:e.type,o=(t?Array.isArray(e.value)?e.value:[]:[e.value]).map(e=>M(e,u,n));return o.some(e=>e===D)?null:{...e,type:V.Array,element:n,...k(e,r),value:o}}if(!B(e))return null;const t=Array.isArray(e.value)?e.value[0]:void 0;if(null==t)return null;const n=M(t,e.element,r.type);if(n===D)return null;return{...e,type:r.type,...j(e,r),value:n}}(e,r);const t=M(e.value,e.type,r.type);return t===D?null:{...e,type:r.type,...j(e,r),value:t}}function B(e){return e.type===V.Array}function j(e,r){if(r.type!==V.Struct&&e.type!==V.Struct)return{};const t=function(e,r){if(null==r)return e;if(null==e)return r;if(e===r||S(e,r))return e;const t=g(e),n=g(r);if(null==t||null==n)return e;return r}(e.type===V.Struct?e.struct:void 0,r.struct);return null!=t?{struct:t}:{}}function k(e,r){if(r.type!==V.Array)return{};const t=B(e)?e.elementStruct:void 0,n=r.elementStruct??t;return null!=n?{elementStruct:n}:{}}const D=Symbol("skippedParamValue");function M(e,r,t){if(function(e,r){if(e===r)return!0;const t=F(e);return null!=t&&t===F(r)}(r,t)&&function(e,r){switch(r){case V.Number:case V.FloatNode:return"number"==typeof e||"string"==typeof e&&Number.isFinite(parseFloat(e));case V.Boolean:case V.BooleanNode:return"boolean"==typeof e;case V.Vector2:case V.Vec2Node:return z(e,2);case V.Vector3:case V.Vec3Node:return z(e,3);case V.Vector4:case V.Vec4Node:return z(e,4);case V.Euler:return Array.isArray(e)&&e.length>=3&&null!=E(e,3);case V.Color:case V.RgbNode:return null!=q(e);case V.String:return"string"==typeof e;case V.Array:return Array.isArray(e);case V.Texture:case V.Sampler2DNode:case V.BaseActor:case V.Object3D:case V.Material:case V.AudioBuffer:case V.VisualEffect:case V.Prefab:case V.PrefabActor:case V.DataAsset:case V.AnimationClip:case V.Sequence:return null==e||"string"==typeof e||"object"==typeof e;case V.Struct:return null!=e&&"object"==typeof e&&!Array.isArray(e);case V.Curve:case V.ColorLayer:case V.MaskLayer:return null!=e}return!0}(e,t))return e;switch(t){case V.Number:case V.FloatNode:{const r=L(e);return null!=r?r:D}case V.Boolean:case V.BooleanNode:{const r=function(e){if("boolean"==typeof e)return e;if("number"==typeof e)return 0!==e;if("string"==typeof e){const r=e.trim().toLowerCase();if(["true","1","yes","on"].includes(r))return!0;if(["false","0","no","off"].includes(r))return!1}return null}(e);return null!=r?r:D}case V.Vector2:case V.Vec2Node:return H(e,2,r)??D;case V.Vector3:case V.Vec3Node:return H(e,3,r)??D;case V.Vector4:case V.Vec4Node:return H(e,4,r)??D;case V.Color:case V.RgbNode:return function(e,r){if(r===V.Color||r===V.RgbNode)return"string"==typeof e||"number"==typeof e?e:O(e);return O(e)}(e,r)??D;case V.Euler:return function(e,r){if(Array.isArray(e)){const r=E(e,3);if(null==r)return null;const t="string"==typeof e[3]?e[3]:"XYZ";return[r[0],r[1],r[2],t]}const t=H(e,3,r);return null!=t?[t[0],t[1],t[2],"XYZ"]:null}(e,r)??D;case V.String:return"string"==typeof e?e:String(e);case V.Texture:case V.Sampler2DNode:case V.BaseActor:case V.Object3D:case V.Material:case V.AudioBuffer:case V.VisualEffect:case V.Curve:case V.ColorLayer:case V.MaskLayer:case V.Prefab:case V.PrefabActor:case V.DataAsset:case V.AnimationClip:case V.Sequence:case V.Struct:return D}return D}function z(e,r){if(Array.isArray(e))return e.length>=r&&null!=E(e,r);if(null!=e&&"object"==typeof e){const t=e;return["x","y","z","w"].slice(0,r).every(e=>null!=L(t[e]))}return!1}function F(e){switch(e){case V.Number:case V.FloatNode:return"number";case V.Boolean:case V.BooleanNode:return"boolean";case V.Vector2:case V.Vec2Node:return"vector2";case V.Vector3:case V.Vec3Node:return"vector3";case V.Vector4:case V.Vec4Node:return"vector4";case V.Color:case V.RgbNode:return"color";case V.Texture:case V.Sampler2DNode:return"texture";case V.Prefab:case V.PrefabActor:return"prefab";case V.DataAsset:return"dataAsset";case V.Struct:return"struct"}}function L(e){if("number"==typeof e)return Number.isFinite(e)?e:null;if("string"==typeof e){const r=parseFloat(e);return Number.isFinite(r)?r:null}if("boolean"==typeof e)return e?1:0;if(Array.isArray(e))return L(e[0]);if(null!=e&&"object"==typeof e){const r=e;return L(r.x??r.r??r.value)}return null}function H(e,r,t){if(t===V.Color||t===V.RgbNode){const t=q(e);if(null!=t)return E(t.toArray(),r,4===r?1:0)}if(Array.isArray(e))return E(e,r);if("number"==typeof e||"string"==typeof e||"boolean"==typeof e){const t=L(e);return null!=t?new Array(r).fill(t):null}if(null!=e&&"object"==typeof e){const t=e,n=["x","y","z","w"].slice(0,r).map(e=>t[e]);if(n.some(e=>null!=e))return E(n,r);const u=["r","g","b","a"].slice(0,r).map(e=>t[e]);if(u.some(e=>null!=e))return E(u,r,4===r?1:0)}return null}function E(e,r,t=0){const n=[];for(let u=0;u<r;u++){const r=L(e[u]??t);if(null==r)return null;n.push(r)}return n}function O(e){const r=q(e);return null!=r?"#"+r.getHexString():null}function q(r){try{if(r instanceof e)return r.clone();if("string"==typeof r)return function(e){return/^#([0-9a-f]{3}|[0-9a-f]{6}|[0-9a-f]{8})$/i.test(e.trim())||/^(rgb|hsl)a?\(/i.test(e.trim())}(r)?new e(r):null;if("number"==typeof r)return Number.isInteger(r)&&r>=0?new e(r):null;if(Array.isArray(r)){const t=E(r,3);return null!=t?(new e).fromArray(t):null}if(null!=r&&"object"==typeof r){const t=r;if(null!=t.r||null!=t.g||null!=t.b){const r=E([t.r,t.g,t.b],3);return null!=r?(new e).fromArray(r):null}if(null!=t.x||null!=t.y||null!=t.z){const r=E([t.x,t.y,t.z],3);return null!=r?(new e).fromArray(r):null}}}catch{return null}return null}function W(e,r){return null!=e&&"object"==typeof e&&e.constructor?.name===r}/*
1
+ import{Color as e,Euler as r,Material as t,Object3D as n,Texture as u,Vector2 as o,Vector3 as a,Vector4 as c,AnimationClip as l}from"three";import{BooleanNode as i,FloatNode as s,RgbNode as f,Sampler2DNode as y,Texture2dLookupNode as p,Vec2Node as m,Vec3Node as d,Vec4Node as b}from"three-shader-graph";import{getParameterDefinitionId as A,getParameterDefinitionType as g,isParameterDefinitionStructAssignableTo as S}from"../shader/parameter.js";import{Curve2 as N}from"../utils/curve.js";import{SerializedParamType as V}from"./model.js";export const applyRuntimeParamTypeInference=!0;export function convertConfiguredParamsToRuntimeTypes(e,r={}){const t={},n=[],u=Object.entries(e),o=r.applyRuntimeParamTypeInference??true;if(0===u.length||!o)return o?{params:t,skipped:n}:{params:e,skipped:n};const a=getRuntimeParameterTypeHints(e,r);for(const[e,r]of u){if(!1===r.override){t[e]=r;continue}const u=a.get(e),o=null!=u?convertConfiguredParamValueToRuntimeType(r,u):r;null!=o?t[e]=o:n.push({key:e,sourceType:r.type,targetType:u.type})}return{params:t,skipped:n}}export function getRuntimeParameterTypeHints(e,r={}){const t=new Map,n=Object.keys(e).filter(r=>!1!==e[r]?.override);if(0===n.length)return t;const u=new Set(n),o=r.parameterTarget??function(e){if(null==e)return null;try{return new e}catch{return null}}(r.parameterType),a=function(e,r){if(null!=r.propertyParams)return r.propertyParams;if(null==r.extractPropertyParameters)return[];if(null!=r.parameterType)return h(r.parameterType,r.extractPropertyParameters);if(null!=e)return h(e.constructor,r.extractPropertyParameters);return[]}(o,r);for(const e of a){if(!u.has(e.name))continue;const n=R(e,r.toSerializedParamType);null!=n&&t.set(e.name,n);const a=inferRuntimeSerializedParamTypeHint(C(o,e.name))??inferRuntimeSerializedParamTypeHint(e.options.defaultValue);null!=a&&t.set(e.name,x(a,n))}if(null!=o)for(const e of n){const r=inferRuntimeSerializedParamTypeHint(C(o,e));if(null!=r){const n=t.get(e);t.set(e,x(r,n))}}return t}const v=new WeakMap,P=new WeakMap,T=Symbol("noCachedSerializedParamType");function h(e,r){let t=v.get(r);null==t&&(t=new WeakMap,v.set(r,t));const n=t.get(e);if(null!=n)return n;const u=function(e,r){try{return r(e)}catch{return[]}}(e,r);return t.set(e,u),u}function C(e,r){return null!=e?e[r]:void 0}function R(e,r){const t=function(e,r){if(null==e||null==r)return;const t=function(e,r){const t=P.get(r);if(null==t||!t.has(e))return T;const n=t.get(e);return null===n?void 0:n}(e,r);if(t!==T)return t;try{const t=r(e);return w(e,r,t),t}catch{return void w(e,r,void 0)}}(e.type,r),n=t===V.Struct?A(e.type):void 0;return!0===e.options.array?{type:V.Array,...null!=t?{element:t}:{},...null!=n?{elementStruct:n}:{}}:null!=t?{type:t,...null!=n?{struct:n}:{}}:void 0}function w(e,r,t){let n=P.get(r);null==n&&(n=new WeakMap,P.set(r,n)),n.set(e,t??null)}function x(e,r){return e.type!==V.Array?e:{type:V.Array,element:e.element??r?.element,elementStruct:e.elementStruct??r?.elementStruct}}export function inferRuntimeSerializedParamTypeHint(g){if(null==g)return;if(Array.isArray(g)){const e=g.map(e=>inferRuntimeSerializedParamTypeHint(e)).find(e=>null!=e);return{type:V.Array,...null!=e&&e.type!==V.Array?{element:e.type}:{},...null!=e?.struct?{elementStruct:e.struct}:{}}}switch(typeof g){case"number":return{type:V.Number};case"boolean":return{type:V.Boolean};case"string":return{type:V.String}}const S=function(e){const r=e;if(e instanceof s||"function"==typeof r?.isFloat||!0===r?.isFloat)return V.FloatNode;if(e instanceof i||"function"==typeof r?.isBool||!0===r?.isBool)return V.BooleanNode;if(e instanceof f||"function"==typeof r?.isRgb||!0===r?.isRgb)return V.RgbNode;if(e instanceof m||"function"==typeof r?.isVec2||!0===r?.isVec2)return V.Vec2Node;if(e instanceof d||"function"==typeof r?.isVec3||!0===r?.isVec3)return V.Vec3Node;if(e instanceof b||"function"==typeof r?.isVec4||!0===r?.isVec4)return V.Vec4Node;if(e instanceof y||e instanceof p||"function"==typeof r?.isSampler2D||!0===r?.isSampler2D)return V.Sampler2DNode;return}(g);if(null!=S)return{type:S};if(g instanceof o)return{type:V.Vector2};if(g instanceof a)return{type:V.Vector3};if(g instanceof c)return{type:V.Vector4};if(g instanceof e)return{type:V.Color};if(g instanceof r)return{type:V.Euler};if(g instanceof u)return{type:V.Texture};if(g instanceof t)return{type:V.Material};if(g instanceof n)return{type:V.Object3D};if("undefined"!=typeof AudioBuffer&&g instanceof AudioBuffer)return{type:V.AudioBuffer};if(W(g,"VisualEffect"))return{type:V.VisualEffect};if(W(g,"PrefabOf"))return{type:V.PrefabActor};if(W(g,"Prefab"))return{type:V.Prefab};if(W(g,"DataAssetRef"))return{type:V.DataAsset};if(g instanceof N)return{type:V.Curve};if(W(g,"ColorLayer"))return{type:V.ColorLayer};if(W(g,"MaskLayer"))return{type:V.MaskLayer};if(g instanceof l)return{type:V.AnimationClip};if(W(g,"Sequence"))return{type:V.Sequence};const v=A(g.constructor);return null!=v?{type:V.Struct,struct:v}:void 0}export function convertConfiguredParamValueToRuntimeType(e,r){if(null==e||null==r)return e;if(e.type===V.Array||r.type===V.Array)return function(e,r){if(r.type===V.Array){const t=B(e),n=r.element??(t?e.element:e.type);if(null==n)return t?e:null;const u=t?e.element:e.type,o=(t?Array.isArray(e.value)?e.value:[]:[e.value]).map(e=>F(e,u,n));return o.some(e=>e===D)?null:{...e,type:V.Array,element:n,...k(e,r),value:o}}if(!B(e))return null;const t=Array.isArray(e.value)?e.value[0]:void 0;if(null==t)return null;const n=F(t,e.element,r.type);if(n===D)return null;return{...e,type:r.type,...j(e,r),value:n}}(e,r);const t=F(e.value,e.type,r.type);return t===D?null:{...e,type:r.type,...j(e,r),value:t}}function B(e){return e.type===V.Array}function j(e,r){if(r.type!==V.Struct&&e.type!==V.Struct)return{};const t=function(e,r){if(null==r)return e;if(null==e)return r;if(e===r||S(e,r))return e;const t=g(e),n=g(r);if(null==t||null==n)return e;return r}(e.type===V.Struct?e.struct:void 0,r.struct);return null!=t?{struct:t}:{}}function k(e,r){if(r.type!==V.Array)return{};const t=B(e)?e.elementStruct:void 0,n=r.elementStruct??t;return null!=n?{elementStruct:n}:{}}const D=Symbol("skippedParamValue");function F(e,r,t){if(function(e,r){if(e===r)return!0;const t=z(e);return null!=t&&t===z(r)}(r,t)&&function(e,r){switch(r){case V.Number:return"number"==typeof e||"string"==typeof e&&Number.isFinite(parseFloat(e));case V.FloatNode:return"number"==typeof e||"string"==typeof e&&Number.isFinite(parseFloat(e))||function(e){if(null==e||"object"!=typeof e||Array.isArray(e))return!1;const r=e;return"particle"===r.time&&"string"==typeof r.easing&&null!=L(r.a)&&null!=L(r.b)}(e);case V.Boolean:case V.BooleanNode:return"boolean"==typeof e;case V.Vector2:case V.Vec2Node:return M(e,2);case V.Vector3:case V.Vec3Node:return M(e,3);case V.Vector4:case V.Vec4Node:return M(e,4);case V.Euler:return Array.isArray(e)&&e.length>=3&&null!=E(e,3);case V.Color:case V.RgbNode:return null!=q(e);case V.String:return"string"==typeof e;case V.Array:return Array.isArray(e);case V.Texture:case V.Sampler2DNode:case V.BaseActor:case V.Object3D:case V.Material:case V.AudioBuffer:case V.VisualEffect:case V.Prefab:case V.PrefabActor:case V.DataAsset:case V.AnimationClip:case V.Sequence:return null==e||"string"==typeof e||"object"==typeof e;case V.Struct:return null!=e&&"object"==typeof e&&!Array.isArray(e);case V.Curve:case V.ColorLayer:case V.MaskLayer:return null!=e}return!0}(e,t))return e;switch(t){case V.Number:case V.FloatNode:{const r=L(e);return null!=r?r:D}case V.Boolean:case V.BooleanNode:{const r=function(e){if("boolean"==typeof e)return e;if("number"==typeof e)return 0!==e;if("string"==typeof e){const r=e.trim().toLowerCase();if(["true","1","yes","on"].includes(r))return!0;if(["false","0","no","off"].includes(r))return!1}return null}(e);return null!=r?r:D}case V.Vector2:case V.Vec2Node:return H(e,2,r)??D;case V.Vector3:case V.Vec3Node:return H(e,3,r)??D;case V.Vector4:case V.Vec4Node:return H(e,4,r)??D;case V.Color:case V.RgbNode:return function(e,r){if(r===V.Color||r===V.RgbNode)return"string"==typeof e||"number"==typeof e?e:O(e);return O(e)}(e,r)??D;case V.Euler:return function(e,r){if(Array.isArray(e)){const r=E(e,3);if(null==r)return null;const t="string"==typeof e[3]?e[3]:"XYZ";return[r[0],r[1],r[2],t]}const t=H(e,3,r);return null!=t?[t[0],t[1],t[2],"XYZ"]:null}(e,r)??D;case V.String:return"string"==typeof e?e:String(e);case V.Texture:case V.Sampler2DNode:case V.BaseActor:case V.Object3D:case V.Material:case V.AudioBuffer:case V.VisualEffect:case V.Curve:case V.ColorLayer:case V.MaskLayer:case V.Prefab:case V.PrefabActor:case V.DataAsset:case V.AnimationClip:case V.Sequence:case V.Struct:return D}return D}function M(e,r){if(Array.isArray(e))return e.length>=r&&null!=E(e,r);if(null!=e&&"object"==typeof e){const t=e;return["x","y","z","w"].slice(0,r).every(e=>null!=L(t[e]))}return!1}function z(e){switch(e){case V.Number:case V.FloatNode:return"number";case V.Boolean:case V.BooleanNode:return"boolean";case V.Vector2:case V.Vec2Node:return"vector2";case V.Vector3:case V.Vec3Node:return"vector3";case V.Vector4:case V.Vec4Node:return"vector4";case V.Color:case V.RgbNode:return"color";case V.Texture:case V.Sampler2DNode:return"texture";case V.Prefab:case V.PrefabActor:return"prefab";case V.DataAsset:return"dataAsset";case V.Struct:return"struct"}}function L(e){if("number"==typeof e)return Number.isFinite(e)?e:null;if("string"==typeof e){const r=parseFloat(e);return Number.isFinite(r)?r:null}if("boolean"==typeof e)return e?1:0;if(Array.isArray(e))return L(e[0]);if(null!=e&&"object"==typeof e){const r=e;return L(r.x??r.r??r.value)}return null}function H(e,r,t){if(t===V.Color||t===V.RgbNode){const t=q(e);if(null!=t)return E(t.toArray(),r,4===r?1:0)}if(Array.isArray(e))return E(e,r);if("number"==typeof e||"string"==typeof e||"boolean"==typeof e){const t=L(e);return null!=t?new Array(r).fill(t):null}if(null!=e&&"object"==typeof e){const t=e,n=["x","y","z","w"].slice(0,r).map(e=>t[e]);if(n.some(e=>null!=e))return E(n,r);const u=["r","g","b","a"].slice(0,r).map(e=>t[e]);if(u.some(e=>null!=e))return E(u,r,4===r?1:0)}return null}function E(e,r,t=0){const n=[];for(let u=0;u<r;u++){const r=L(e[u]??t);if(null==r)return null;n.push(r)}return n}function O(e){const r=q(e);return null!=r?"#"+r.getHexString():null}function q(r){try{if(r instanceof e)return r.clone();if("string"==typeof r)return function(e){return/^#([0-9a-f]{3}|[0-9a-f]{6}|[0-9a-f]{8})$/i.test(e.trim())||/^(rgb|hsl)a?\(/i.test(e.trim())}(r)?new e(r):null;if("number"==typeof r)return Number.isInteger(r)&&r>=0?new e(r):null;if(Array.isArray(r)){const t=E(r,3);return null!=t?(new e).fromArray(t):null}if(null!=r&&"object"==typeof r){const t=r;if(null!=t.r||null!=t.g||null!=t.b){const r=E([t.r,t.g,t.b],3);return null!=r?(new e).fromArray(r):null}if(null!=t.x||null!=t.y||null!=t.z){const r=E([t.x,t.y,t.z],3);return null!=r?(new e).fromArray(r):null}}}catch{return null}return null}function W(e,r){return null!=e&&"object"==typeof e&&e.constructor?.name===r}/*
2
2
  * Copyright (©) 2026 Hology Interactive AB. All rights reserved.
3
3
  * See the LICENSE.md file for details.
4
4
  */
@@ -5,7 +5,8 @@ import { SceneObject } from '../../scene/materializer.js';
5
5
  import { ShaderImpl } from '../../shader/shader.js';
6
6
  import { AssetResourceLoader } from '../asset-resource-loader.js';
7
7
  import { LandscapeGroup } from './landscape.js';
8
- export declare const grassGeometryTriangleLimit = 400;
8
+ import { grassGeometryTriangleLimit } from '../scatter/scatter-limits.js';
9
+ export { grassGeometryTriangleLimit };
9
10
  /**
10
11
  * Creates landscape with varying level of detail depending on the
11
12
  */
@@ -1,4 +1,4 @@
1
- import{Subject as e,debounceTime as t}from"rxjs";import{Box3 as s,BufferAttribute as n,InstancedMesh as o,MathUtils as a,Matrix4 as i,Mesh as r,MeshStandardMaterial as c,PerspectiveCamera as l,PlaneGeometry as h,ShaderMaterial as u,Triangle as f,Vector2 as m,Vector3 as p}from"three";import{materialFromAsset as d}from"../../scene/materializer.js";import{getMaterialAttribute as w}from"../../scene/materials/utils/material-painting";import{whenIdle as g}from"../../utils/async.js";import{indexBy as y}from"../../utils/collections.js";import{meanVectors3withWeight as M}from"../../utils/math.js";import{LandscapeMesh as b,defaultLandscapeMaterial as x}from"./landscape.js";import{smoothNormalsCrossMeshes as S}from"./utils.js";export const grassGeometryTriangleLimit=400;new p,new p;const A=new p,v=new p,z=new p;export class LandscapeManager{constructor(s,n,o,a,r,c,l){this.view=n,this.landscape=o,this.assetManagerService=a,this.assetService=r,this.shaders=c,this.applyMaterial=l,this.scatterMeshes=new Map,this.loadedScatterSquares=new Set,this.refreshRequests=new e,this.defaultLandscapeMaterial=x.clone(),this.scatterMeshPool=[],this.onLoopHandler=()=>this.update(),this.sectionCache=new Map,this._matrix=new i,this.scatterGeometryCache=new Map,this._lastUpdatePosition=new p,this._cameraPosition=new p,this.source=JSON.parse(JSON.stringify(s)),this.view.onLoop(this.onLoopHandler),this.defaultLandscapeMaterial.name=x.name,this.defaultLandscapeMaterial.color=x.color,this.refreshRequests.pipe(t(500)).subscribe(e=>this.refreshScatter(e.origin,e.force,e.predicate))}updateSource(e){this.source=JSON.parse(JSON.stringify(e))}updateShaders(e){this.shaders=e}async loadGrass(){const e=await this.assetService.getAsset("6ij937n72g");await this.assetManagerService.getMesh(e);this.grassGeometry=new h(2,2,3,3);const t=this.grassGeometry.getAttribute("normal");for(let e=0;e<t.count;e++)t.setXYZ(e,0,1,0);t.needsUpdate=!0,this.grassMaterial=new c({color:3765785})}refreshGeometry(){const e=this.source.landscape.options,t=(new p,new p);this.view.camera.getWorldPosition(t);const s=[];z.fromArray(this.source.position);const n=this.view.camera instanceof l?Math.min(this.view.camera.far,1e3):1e3,o=1.1*n,a=e.sections.y*e.sectionSize/-2,i=e.sections.x*e.sectionSize/-2;for(let r=0;r<e.sections.x;r++)if(A.x=i+r*e.sectionSize,!(Math.abs(t.x-A.x)>o))for(let c=0;c<e.sections.y;c++){A.z=a+c*e.sectionSize,v.copy(z).add(A);const l=v.distanceTo(t),h=`${r},${c}`,u=this.landscape.sections.find(e=>e.x===r&&e.y===c);if(l<=n){if(null==u){this.sectionCache.has(h)||this.sectionCache.set(h,this.createLandscapeMesh(this.source,e,i,a,r,c));const t=this.sectionCache.get(h);this.applyMaterial(t),this.landscape.add(t),s.push(t)}}else l>o&&this.landscape.remove(u)}S(s)}applyHeightMap(e,t,s,n=1){const o=Math.pow(s+1,2),a=e.getAttribute("position");if(1===n)for(const e of t.points)a.setY(e.i,e.y);else{const e=y(t.points??[],e=>e.i);for(let t=0;t<a.count;t++){const s=P(t,a.count,o);let n=0;n=s%1==0?e.get(s)?.y??0:Math.floor(e.get(s)?.y),a.setY(t,n)}}a.needsUpdate=!0,e.computeVertexNormals()}deleteOldScatterMeshes(){const e=new Set;for(const[t,s]of this.source.grass?.layers.entries()??[])for(const[n,o]of s.meshes.entries()){const s=`${t}-${n}`;e.add(s)}for(const t of this.scatterMeshes.keys())if(!e.has(t)){this.scatterMeshes.get(t).forEach(e=>{e.parent?.remove(e),e.dispose()}),this.scatterMeshes.delete(t)}}queueRefreshScatter(e,t=!1,s=()=>!0){this.refreshRequests.next({origin:e,force:t,predicate:s})}async refreshScatter(e,t=!1,s=()=>!0){t&&this.scatterGeometryCache.clear(),this.deleteOldScatterMeshes();for(const[n,i]of this.source.grass?.layers.entries()??[])for(const[c,l]of i.meshes.entries()){const i=`${n}-${c}`;this.scatterMeshes.has(i)||this.scatterMeshes.set(i,new Map);const h=this.scatterMeshes.get(i),u=await this.assetService.getAsset(l.assetId);if(null==u){console.error(`Can not find asset with id ${l.assetId}`);continue}let m;try{m=await this.assetManagerService.getMesh(u)}catch(e){console.error(`Failed to load mesh in landscape manager for asset with name ${u.name}`,e);continue}const w=[];if(m.scene.traverse(e=>{e instanceof r&&w.push(e)}),1!==w.length){console.log(m),console.warn("Dynamic grass only works for meshes with a single geometry.");continue}if(!(w[0]instanceof r)){console.warn("Only meshes can be used for dynamic grass. Found:",m.scene);continue}const b=w[0];let x=b.geometry;if(this.scatterGeometryCache.has(b.geometry.uuid))x=this.scatterGeometryCache.get(b.geometry.uuid);else if(x=b.geometry.clone(),!0===l.normalsUp&&F(x),this.scatterGeometryCache.set(b.geometry.uuid,x),null==x.userData.updatedMatrix){m.scene.updateMatrixWorld(),x.applyMatrix4(b.matrixWorld),x.userData.updatedMatrix=!0;const e=u.mesh?.rescale??1;1!==e&&x.scale(e,e,e)}const S=x.getIndex()??x.getAttribute("position"),A=null!=S?S.count/3:0;if(A>400){console.warn(`The triangle count of ${u.name} is too big ${A}. Keep it below 400`);continue}const v=null!=u.materialAssignments&&u.materialAssignments.length>0?u.materialAssignments[0].materialId:null,z=null!=v&&"null"!==v?await d(await this.assetService.getAsset(v),null,this.assetService,this.assetManagerService,this.shaders,!1):null;let P=null!=z?z:b.material;if(Array.isArray(P))for(const e of P)e.userData.disableAO=!0;else P.userData.disableAO=!0;const L=a.degToRad(l.maxSlope??90),T=Math.cos(L),_=this.landscape.sections,D=_.filter($(e,l.viewDistance)),q=D.filter(e=>!h.has(e.uuid)||t).filter(e=>s(e));_.filter(O(e,2*l.viewDistance)).forEach(e=>{const t=h.get(e.uuid);null!=t&&(t.visible=!1)});for(const e of D){const t=h.get(e.uuid);null!=t&&(t.visible=!0)}performance.now();const I=this.source.landscape.options,U=I.sectionSize,k=l.density??1??1,H=I.density,X=U/H,Y=k,E=X/Math.sqrt(Y),J=Math.pow(H,2),Z=E/X,K=Math.floor(J*Y),V=[0,0,0];for(const e of q)await g(async()=>{e.updateWorldMatrix(!0,!1);const s=this._matrix,a=new p,i=e.geometry.getAttribute("position"),r=e.geometry.getAttribute("normal"),c=(this.source.vertexMaterials??[]).filter(t=>t.m===e.name),u=y(c,e=>e.i);let m=h.get(e.uuid);if(null==m||m.count==K&&!t||(m.parent?.remove(m),this.scatterMeshPool.push(m),h.delete(e.uuid),m=null),null==m){const e=this.scatterMeshPool.findIndex(e=>e.count>=K);e>-1?(m=this.scatterMeshPool[e],m.geometry=x,m.material=P,this.scatterMeshPool.splice(e,1)):m=new o(x,P,K),m.raycast=()=>{},m.receiveShadow=!0}m.visible=!0;const d=new f(new p,new p,new p);let[w,g,b,S]=[new p,new p,new p,new p],[A,v,z]=[[],[],[]],[L,O,$]=[new p,new p,new p,new p];const _=new p,D=new p,q=new p,I=new p,U=new f(new p,new p,new p),k=new f(new p,new p,new p),F=new f(new p,new p,new p),X=new f(new p,new p,new p);let E=0;e:for(let t=0;t<J;t++){const o=Math.floor(t/H);w.fromBufferAttribute(i,t+o),I.copy(w).applyMatrix4(e.matrixWorld),U.a.copy(w),U.b.fromBufferAttribute(i,t+1+o),U.c.fromBufferAttribute(i,t+H+1+o),k.a.copy(U.b),k.b.copy(U.c),k.c.fromBufferAttribute(i,t+H+2+o),F.a.fromBufferAttribute(r,t+o),F.b.fromBufferAttribute(r,t+1+o),F.c.fromBufferAttribute(r,t+H+1+o),X.a.copy(F.b),X.b.copy(F.c),X.c.fromBufferAttribute(r,t+H+2+o);const c=[];c[0]=u.get(t+o)?.w,c[1]=u.get(t+1+o)?.w,c[2]=u.get(t+H+1+o)?.w,c[3]=u.get(t+H+2+o)?.w;let h=0;for(let e=0;e<=1+Z;e+=Z)for(let t=0;t<=1+Z;t+=Z){if(E>K)break e;if(h++,h>Y)continue e;1-e>t?(g=U.a,b=U.b,S=U.c,L=F.a,O=F.b,$=F.c,A=c[0],v=c[1],z=c[2]):(g=k.a,b=k.b,S=k.c,L=X.a,O=X.b,$=X.c,A=c[1],v=c[2],z=c[3]),d.a.copy(g),d.b.copy(b),d.c.copy(S),C(d),_.set(w.x,0,w.z),R(d,_),d.getBarycoord(_,a).toArray(V),G[0]=A,G[1]=v,G[2]=z;if(j(G,V,.2)!==n-1)continue;if(M([g,b,S],V,D),M([L,O,$],V,q),null!=l.maxSlope&&l.maxSlope<90&&q.y<T)continue;const o=D;o.y+=B(l.offsetMin,l.offsetMax);const i=B(l.scaleMin??1,l.scaleMax??1);s.makeScale(i,i,i);const r=s.elements;r[12]=o.x,r[13]=o.y,r[14]=o.z,!1!==l.randomRotation&&N(s,i),l.alignToNormal&&W(s,o,m.matrixWorld,q);const u=m.instanceMatrix.array,f=16*E;u[f]=r[0],u[f+1]=r[1],u[f+2]=r[2],u[f+3]=r[3],u[f+4]=r[4],u[f+5]=r[5],u[f+6]=r[6],u[f+7]=r[7],u[f+8]=r[8],u[f+9]=r[9],u[f+10]=r[10],u[f+11]=r[11],u[f+12]=r[12],u[f+13]=r[13],u[f+14]=r[14],u[f+15]=r[15],E++}}m.count=E,m.instanceMatrix.needsUpdate=!0,m.position.copy(e.position),m.updateMatrix(),h.has(e.uuid)||this.landscape?.add(m),h.set(e.uuid,m),m.userData.meshConfig=l});performance.now()}}stop(){this.view.removeOnLoop(this.onLoopHandler)}update(){this.view.camera&&(this.view.camera.getWorldPosition(this._cameraPosition),this._cameraPosition.distanceTo(this._lastUpdatePosition)>10&&(this._lastUpdatePosition.copy(this._cameraPosition),this.refreshGeometry(),this.refreshScatter(this._cameraPosition)))}clear(){this.scatterMeshes.forEach(e=>e.forEach(e=>e.parent?.remove(e)))}createLandscapeMesh(e,t,s,n,o,a){const i=new h(t.sectionSize,t.sectionSize,t.density,t.density);i.rotateX(Math.PI/-2);const r=this.defaultLandscapeMaterial,c=new b(i,r);c.position.x=s+o*t.sectionSize,c.position.z=n+a*t.sectionSize,c.receiveShadow=!0,c.castShadow=!1,c.userData.landscape={x:o,y:a},c.x=o,c.y=a,c.name=`${o},${a}`,w(c,0,!0),w(c,4,!0);const l=e.landscape.heightMaps.find(e=>e.x===o&&e.y===a);if(null!=l&&this.applyHeightMap(i,l,t.density,1),i.computeTangents(),i.computeBoundsTree(),null!=e.landscape.holes&&e.landscape.holes.length>0){const t=getHoleAttribute(c,!0);for(const s of e.landscape.holes)s.m===c.name&&t.setX(s.i,s.w[0])}return c}}export function getHoleAttribute(e,t=!1){if(!e.geometry.hasAttribute("hole")||t){const t=new Float32Array(e.geometry.getAttribute("position").array.length);e.geometry.setAttribute("hole",new n(t,1))}return e.geometry.getAttribute("hole")}function P(e,t,s){const n=Math.sqrt(t),o=Math.floor(e/n)/(n-1),a=e%n/(n-1),i=Math.sqrt(s);return(s-1)*o-(i-1)*o+(i-1)*a}new Map,new m(0,0),new m(1,0),new m(0,1),new m(1,0),new m(0,1),new m(1,1),new p;const L=new s;function O(e,t){return function(s){return L.setFromObject(s).distanceToPoint(e)>t}}function $(e,t){return function(s){return L.setFromObject(s).distanceToPoint(e)<t}}function C(e){e.a.y=0,e.b.y=0,e.c.y=0}const G=[];function j(e,t,s=.5){const n=G;let o=-1,a=-1;for(let e=0;e<n.length;e++)if(null!=n[e])for(let i=0;i<n[e].length;i++){const r=n[e][i]*t[e];r>s&&r>a&&(a=r,o=i)}return o}function B(e,t){let s=t-e,n=D();return n*=s,n+=e,n}const T=[];let _=1e3;for(;_--;)T.push(Math.random());function D(){return++_>=T.length?T[_=0]:T[_]}const q=[];let I=20;for(;I--;)q.push((new i).makeRotationY(D()*Math.PI/2));function R(e,t){let s=D(),n=D();s+n>1&&(s=1-s,n=1-n);const o=e.a,a=e.b,i=e.c;t.x=o.x+s*(a.x-o.x)+n*(i.x-o.x),t.z=o.z+s*(a.z-o.z)+n*(i.z-o.z)}new p;new p;const U=new p,k=new p(0,1,0),H=(new i).makeRotationX(Math.PI/-2);function W(e,t,s,n){e.lookAt(U,n,k).multiply(H)}new i;function N(e,t=1){const s=(++I>=q.length?q[I=0]:q[I]).elements,n=e.elements;n[0]=s[0]*t,n[4]=s[4]*t,n[8]=s[8]*t,n[1]=s[1]*t,n[5]=s[5]*t,n[9]=s[9]*t,n[2]=s[2]*t,n[6]=s[6]*t,n[10]=s[10]*t}function F(e){const t=e.getAttribute("normal");for(let e=0;e<t.count;e++)t.setXYZ(e,0,1,0);t.normalized=!0,t.needsUpdate=!0}/*
1
+ import{Subject as e,debounceTime as t}from"rxjs";import{Box3 as s,BufferAttribute as n,InstancedMesh as o,MathUtils as a,Matrix4 as i,Mesh as r,MeshStandardMaterial as c,PerspectiveCamera as l,PlaneGeometry as h,ShaderMaterial as u,Triangle as f,Vector2 as m,Vector3 as p}from"three";import{materialFromAsset as d}from"../../scene/materializer.js";import{getMaterialAttribute as w}from"../../scene/materials/utils/material-painting";import{whenIdle as g}from"../../utils/async.js";import{indexBy as y}from"../../utils/collections.js";import{meanVectors3withWeight as M}from"../../utils/math.js";import{LandscapeMesh as b,defaultLandscapeMaterial as x}from"./landscape.js";import{smoothNormalsCrossMeshes as S}from"./utils.js";import{grassGeometryTriangleLimit as A}from"../scatter/scatter-limits.js";export{A as grassGeometryTriangleLimit};new p,new p;const v=new p,z=new p,P=new p;export class LandscapeManager{constructor(s,n,o,a,r,c,l){this.view=n,this.landscape=o,this.assetManagerService=a,this.assetService=r,this.shaders=c,this.applyMaterial=l,this.scatterMeshes=new Map,this.loadedScatterSquares=new Set,this.refreshRequests=new e,this.defaultLandscapeMaterial=x.clone(),this.scatterMeshPool=[],this.onLoopHandler=()=>this.update(),this.sectionCache=new Map,this._matrix=new i,this.scatterGeometryCache=new Map,this._lastUpdatePosition=new p,this._cameraPosition=new p,this.source=JSON.parse(JSON.stringify(s)),this.view.onLoop(this.onLoopHandler),this.defaultLandscapeMaterial.name=x.name,this.defaultLandscapeMaterial.color=x.color,this.refreshRequests.pipe(t(500)).subscribe(e=>this.refreshScatter(e.origin,e.force,e.predicate))}updateSource(e){this.source=JSON.parse(JSON.stringify(e))}updateShaders(e){this.shaders=e}async loadGrass(){const e=await this.assetService.getAsset("6ij937n72g");await this.assetManagerService.getMesh(e);this.grassGeometry=new h(2,2,3,3);const t=this.grassGeometry.getAttribute("normal");for(let e=0;e<t.count;e++)t.setXYZ(e,0,1,0);t.needsUpdate=!0,this.grassMaterial=new c({color:3765785})}refreshGeometry(){const e=this.source.landscape.options,t=(new p,new p);this.view.camera.getWorldPosition(t);const s=[];P.fromArray(this.source.position);const n=this.view.camera instanceof l?Math.min(this.view.camera.far,1e3):1e3,o=1.1*n,a=e.sections.y*e.sectionSize/-2,i=e.sections.x*e.sectionSize/-2;for(let r=0;r<e.sections.x;r++)if(v.x=i+r*e.sectionSize,!(Math.abs(t.x-v.x)>o))for(let c=0;c<e.sections.y;c++){v.z=a+c*e.sectionSize,z.copy(P).add(v);const l=z.distanceTo(t),h=`${r},${c}`,u=this.landscape.sections.find(e=>e.x===r&&e.y===c);if(l<=n){if(null==u){this.sectionCache.has(h)||this.sectionCache.set(h,this.createLandscapeMesh(this.source,e,i,a,r,c));const t=this.sectionCache.get(h);this.applyMaterial(t),this.landscape.add(t),s.push(t)}}else l>o&&this.landscape.remove(u)}S(s)}applyHeightMap(e,t,s,n=1){const o=Math.pow(s+1,2),a=e.getAttribute("position");if(1===n)for(const e of t.points)a.setY(e.i,e.y);else{const e=y(t.points??[],e=>e.i);for(let t=0;t<a.count;t++){const s=$(t,a.count,o);let n=0;n=s%1==0?e.get(s)?.y??0:Math.floor(e.get(s)?.y),a.setY(t,n)}}a.needsUpdate=!0,e.computeVertexNormals()}deleteOldScatterMeshes(){const e=new Set;for(const[t,s]of this.source.grass?.layers.entries()??[])for(const[n,o]of s.meshes.entries()){const s=`${t}-${n}`;e.add(s)}for(const t of this.scatterMeshes.keys())if(!e.has(t)){this.scatterMeshes.get(t).forEach(e=>{e.parent?.remove(e),e.dispose()}),this.scatterMeshes.delete(t)}}queueRefreshScatter(e,t=!1,s=()=>!0){this.refreshRequests.next({origin:e,force:t,predicate:s})}async refreshScatter(e,t=!1,s=()=>!0){t&&this.scatterGeometryCache.clear(),this.deleteOldScatterMeshes();for(const[n,i]of this.source.grass?.layers.entries()??[])for(const[c,l]of i.meshes.entries()){const i=`${n}-${c}`;this.scatterMeshes.has(i)||this.scatterMeshes.set(i,new Map);const h=this.scatterMeshes.get(i),u=await this.assetService.getAsset(l.assetId);if(null==u){console.error(`Can not find asset with id ${l.assetId}`);continue}let m;try{m=await this.assetManagerService.getMesh(u)}catch(e){console.error(`Failed to load mesh in landscape manager for asset with name ${u.name}`,e);continue}const w=[];if(m.scene.traverse(e=>{e instanceof r&&w.push(e)}),1!==w.length){console.log(m),console.warn("Dynamic grass only works for meshes with a single geometry.");continue}if(!(w[0]instanceof r)){console.warn("Only meshes can be used for dynamic grass. Found:",m.scene);continue}const b=w[0];let x=b.geometry;if(this.scatterGeometryCache.has(b.geometry.uuid))x=this.scatterGeometryCache.get(b.geometry.uuid);else if(x=b.geometry.clone(),!0===l.normalsUp&&X(x),this.scatterGeometryCache.set(b.geometry.uuid,x),null==x.userData.updatedMatrix){m.scene.updateMatrixWorld(),x.applyMatrix4(b.matrixWorld),x.userData.updatedMatrix=!0;const e=u.mesh?.rescale??1;1!==e&&x.scale(e,e,e)}const S=x.getIndex()??x.getAttribute("position"),v=null!=S?S.count/3:0;if(v>A){console.warn(`The triangle count of ${u.name} is too big ${v}. Keep it below ${A}`);continue}const z=null!=u.materialAssignments&&u.materialAssignments.length>0?u.materialAssignments[0].materialId:null,P=null!=z&&"null"!==z?await d(await this.assetService.getAsset(z),null,this.assetService,this.assetManagerService,this.shaders,!1):null;let $=null!=P?P:b.material;if(Array.isArray($))for(const e of $)e.userData.disableAO=!0;else $.userData.disableAO=!0;const L=a.degToRad(l.maxSlope??90),D=Math.cos(L),T=this.landscape.sections,q=T.filter(j(e,l.viewDistance)),I=q.filter(e=>!h.has(e.uuid)||t).filter(e=>s(e));T.filter(O(e,2*l.viewDistance)).forEach(e=>{const t=h.get(e.uuid);null!=t&&(t.visible=!1)});for(const e of q){const t=h.get(e.uuid);null!=t&&(t.visible=!0)}performance.now();const R=this.source.landscape.options,k=R.sectionSize,H=l.density??1??1,W=R.density,Y=k/W,E=H,J=Y/Math.sqrt(E),Z=Math.pow(W,2),K=J/Y,V=Math.floor(Z*E),Q=[0,0,0];for(const e of I)await g(async()=>{e.updateWorldMatrix(!0,!1);const s=this._matrix,a=new p,i=e.geometry.getAttribute("position"),r=e.geometry.getAttribute("normal"),c=(this.source.vertexMaterials??[]).filter(t=>t.m===e.name),u=y(c,e=>e.i);let m=h.get(e.uuid);if(null==m||m.count==V&&!t||(m.parent?.remove(m),this.scatterMeshPool.push(m),h.delete(e.uuid),m=null),null==m){const e=this.scatterMeshPool.findIndex(e=>e.count>=V);e>-1?(m=this.scatterMeshPool[e],m.geometry=x,m.material=$,this.scatterMeshPool.splice(e,1)):m=new o(x,$,V),m.raycast=()=>{},m.receiveShadow=!0}m.visible=!0;const d=new f(new p,new p,new p);let[w,g,b,S]=[new p,new p,new p,new p],[A,v,z]=[[],[],[]],[P,L,O]=[new p,new p,new p,new p];const j=new p,T=new p,q=new p,I=new p,R=new f(new p,new p,new p),k=new f(new p,new p,new p),H=new f(new p,new p,new p),X=new f(new p,new p,new p);let Y=0;e:for(let t=0;t<Z;t++){const o=Math.floor(t/W);w.fromBufferAttribute(i,t+o),I.copy(w).applyMatrix4(e.matrixWorld),R.a.copy(w),R.b.fromBufferAttribute(i,t+1+o),R.c.fromBufferAttribute(i,t+W+1+o),k.a.copy(R.b),k.b.copy(R.c),k.c.fromBufferAttribute(i,t+W+2+o),H.a.fromBufferAttribute(r,t+o),H.b.fromBufferAttribute(r,t+1+o),H.c.fromBufferAttribute(r,t+W+1+o),X.a.copy(H.b),X.b.copy(H.c),X.c.fromBufferAttribute(r,t+W+2+o);const c=[];c[0]=u.get(t+o)?.w,c[1]=u.get(t+1+o)?.w,c[2]=u.get(t+W+1+o)?.w,c[3]=u.get(t+W+2+o)?.w;let h=0;for(let e=0;e<=1+K;e+=K)for(let t=0;t<=1+K;t+=K){if(Y>V)break e;if(h++,h>E)continue e;1-e>t?(g=R.a,b=R.b,S=R.c,P=H.a,L=H.b,O=H.c,A=c[0],v=c[1],z=c[2]):(g=k.a,b=k.b,S=k.c,P=X.a,L=X.b,O=X.c,A=c[1],v=c[2],z=c[3]),d.a.copy(g),d.b.copy(b),d.c.copy(S),C(d),j.set(w.x,0,w.z),U(d,j),d.getBarycoord(j,a).toArray(Q),B[0]=A,B[1]=v,B[2]=z;if(G(B,Q,.2)!==n-1)continue;if(M([g,b,S],Q,T),M([P,L,O],Q,q),null!=l.maxSlope&&l.maxSlope<90&&q.y<D)continue;const o=T;o.y+=_(l.offsetMin,l.offsetMax);const i=_(l.scaleMin??1,l.scaleMax??1);s.makeScale(i,i,i);const r=s.elements;r[12]=o.x,r[13]=o.y,r[14]=o.z,!1!==l.randomRotation&&F(s,i),l.alignToNormal&&N(s,o,m.matrixWorld,q);const u=m.instanceMatrix.array,f=16*Y;u[f]=r[0],u[f+1]=r[1],u[f+2]=r[2],u[f+3]=r[3],u[f+4]=r[4],u[f+5]=r[5],u[f+6]=r[6],u[f+7]=r[7],u[f+8]=r[8],u[f+9]=r[9],u[f+10]=r[10],u[f+11]=r[11],u[f+12]=r[12],u[f+13]=r[13],u[f+14]=r[14],u[f+15]=r[15],Y++}}m.count=Y,m.instanceMatrix.needsUpdate=!0,m.position.copy(e.position),m.updateMatrix(),h.has(e.uuid)||this.landscape?.add(m),h.set(e.uuid,m),m.userData.meshConfig=l});performance.now()}}stop(){this.view.removeOnLoop(this.onLoopHandler)}update(){this.view.camera&&(this.view.camera.getWorldPosition(this._cameraPosition),this._cameraPosition.distanceTo(this._lastUpdatePosition)>10&&(this._lastUpdatePosition.copy(this._cameraPosition),this.refreshGeometry(),this.refreshScatter(this._cameraPosition)))}clear(){this.scatterMeshes.forEach(e=>e.forEach(e=>e.parent?.remove(e)))}createLandscapeMesh(e,t,s,n,o,a){const i=new h(t.sectionSize,t.sectionSize,t.density,t.density);i.rotateX(Math.PI/-2);const r=this.defaultLandscapeMaterial,c=new b(i,r);c.position.x=s+o*t.sectionSize,c.position.z=n+a*t.sectionSize,c.receiveShadow=!0,c.castShadow=!1,c.userData.landscape={x:o,y:a},c.x=o,c.y=a,c.name=`${o},${a}`,w(c,0,!0),w(c,4,!0);const l=e.landscape.heightMaps.find(e=>e.x===o&&e.y===a);if(null!=l&&this.applyHeightMap(i,l,t.density,1),i.computeTangents(),i.computeBoundsTree(),null!=e.landscape.holes&&e.landscape.holes.length>0){const t=getHoleAttribute(c,!0);for(const s of e.landscape.holes)s.m===c.name&&t.setX(s.i,s.w[0])}return c}}export function getHoleAttribute(e,t=!1){if(!e.geometry.hasAttribute("hole")||t){const t=new Float32Array(e.geometry.getAttribute("position").array.length);e.geometry.setAttribute("hole",new n(t,1))}return e.geometry.getAttribute("hole")}function $(e,t,s){const n=Math.sqrt(t),o=Math.floor(e/n)/(n-1),a=e%n/(n-1),i=Math.sqrt(s);return(s-1)*o-(i-1)*o+(i-1)*a}new Map,new m(0,0),new m(1,0),new m(0,1),new m(1,0),new m(0,1),new m(1,1),new p;const L=new s;function O(e,t){return function(s){return L.setFromObject(s).distanceToPoint(e)>t}}function j(e,t){return function(s){return L.setFromObject(s).distanceToPoint(e)<t}}function C(e){e.a.y=0,e.b.y=0,e.c.y=0}const B=[];function G(e,t,s=.5){const n=B;let o=-1,a=-1;for(let e=0;e<n.length;e++)if(null!=n[e])for(let i=0;i<n[e].length;i++){const r=n[e][i]*t[e];r>s&&r>a&&(a=r,o=i)}return o}function _(e,t){let s=t-e,n=q();return n*=s,n+=e,n}const D=[];let T=1e3;for(;T--;)D.push(Math.random());function q(){return++T>=D.length?D[T=0]:D[T]}const I=[];let R=20;for(;R--;)I.push((new i).makeRotationY(q()*Math.PI/2));function U(e,t){let s=q(),n=q();s+n>1&&(s=1-s,n=1-n);const o=e.a,a=e.b,i=e.c;t.x=o.x+s*(a.x-o.x)+n*(i.x-o.x),t.z=o.z+s*(a.z-o.z)+n*(i.z-o.z)}new p;new p;const k=new p,H=new p(0,1,0),W=(new i).makeRotationX(Math.PI/-2);function N(e,t,s,n){e.lookAt(k,n,H).multiply(W)}new i;function F(e,t=1){const s=(++R>=I.length?I[R=0]:I[R]).elements,n=e.elements;n[0]=s[0]*t,n[4]=s[4]*t,n[8]=s[8]*t,n[1]=s[1]*t,n[5]=s[5]*t,n[9]=s[9]*t,n[2]=s[2]*t,n[6]=s[6]*t,n[10]=s[10]*t}function X(e){const t=e.getAttribute("normal");for(let e=0;e<t.count;e++)t.setXYZ(e,0,1,0);t.normalized=!0,t.needsUpdate=!0}/*
2
2
  * Copyright (©) 2026 Hology Interactive AB. All rights reserved.
3
3
  * See the LICENSE.md file for details.
4
4
  */
@@ -20,7 +20,7 @@ import type { ShaderGraphDocument } from "../shader/graph/model.js";
20
20
  export { applyRuntimeParamTypeInference, convertConfiguredParamsToRuntimeTypes, convertConfiguredParamValueToRuntimeType, inferRuntimeSerializedParamTypeHint };
21
21
  export type { RuntimeSerializedParamTypeHint } from './custom-param-runtime-types.js';
22
22
  export type SceneObjectId = string;
23
- export type SceneObjectType = "asset_mesh" | "light" | "shape_mesh" | "spline" | "landscape" | "particles" | "global_fog" | "global_light" | "actor" | "group" | "prefab" | "vfx" | "sky" | "world_env";
23
+ export type SceneObjectType = "asset_mesh" | "light" | "shape_mesh" | "spline" | "landscape" | "particles" | "global_fog" | "global_light" | "actor" | "group" | "prefab" | "vfx" | "sky" | "world_env" | "scatter";
24
24
  export type LightType = "point" | "spot" | "directional" | "ambient" | "rectArea";
25
25
  type DirectionalLightSettings = {
26
26
  color: string;
@@ -122,6 +122,10 @@ export type SceneObject = {
122
122
  * Surface scatter settings. Only relevant for object type asset_mesh in v1.
123
123
  */
124
124
  surfaceScatter?: SurfaceScatterSettings;
125
+ /**
126
+ * Artist-painted scatter settings. Only relevant for object type scatter.
127
+ */
128
+ paintedScatter?: PaintedScatterSettings;
125
129
  /**
126
130
  * Actor settings. Only relevant for object type actor
127
131
  */
@@ -233,6 +237,39 @@ export interface ScatterMeshSettings {
233
237
  }
234
238
  export interface GrassMesh extends ScatterMeshSettings {
235
239
  }
240
+ export type PaintedScatterEraseMode = 'enabled' | 'all';
241
+ export interface PaintedScatterSettings {
242
+ palette: PaintedScatterPaletteItem[];
243
+ brush: PaintedScatterBrushSettings;
244
+ cells: PaintedScatterCell[];
245
+ }
246
+ export interface PaintedScatterPaletteItem extends ScatterMeshSettings {
247
+ id: string;
248
+ enabled: boolean;
249
+ weight: number;
250
+ minSpacing: number;
251
+ castShadow: boolean;
252
+ receiveShadow: boolean;
253
+ }
254
+ export interface PaintedScatterBrushSettings {
255
+ radius: number;
256
+ density: number;
257
+ eraseMode?: PaintedScatterEraseMode;
258
+ }
259
+ export interface PaintedScatterCell {
260
+ key: string;
261
+ revision: number;
262
+ instances: PaintedScatterInstance[];
263
+ }
264
+ export interface PaintedScatterInstance {
265
+ id: string;
266
+ paletteId: string;
267
+ position: number[];
268
+ rotation: [number, number, number, THREE.EulerOrder?];
269
+ scale: number[];
270
+ surfaceNormal?: number[];
271
+ sourceObjectId?: string;
272
+ }
236
273
  export type FogType = "linear" | "density";
237
274
  export interface FogSettings {
238
275
  type: FogType;
@@ -309,6 +346,7 @@ export declare class SceneMaterializer {
309
346
  private createAssetSubscription;
310
347
  readonly components: GameComponent[];
311
348
  private readonly landscapeManagers;
349
+ private paintedScatterManager?;
312
350
  private surfaceScatterManager?;
313
351
  materializedActors: Map<string, BaseActor>;
314
352
  idToSceneObject: Map<string, SceneObject>;
@@ -344,6 +382,8 @@ export declare class SceneMaterializer {
344
382
  private prefetchAssets;
345
383
  private collectAssetMeshIds;
346
384
  private collectObjectAssetMeshIds;
385
+ private collectMaterialAssignments;
386
+ private collectObjectMaterialAssignments;
347
387
  init(): Promise<void>;
348
388
  /**
349
389
  * Materialize visual effects to upload textures
@@ -417,6 +457,12 @@ export declare class SceneMaterializer {
417
457
  * @returns
418
458
  */
419
459
  materialize(source: SceneObject, parent?: Object3D, anonymous?: boolean, context?: MaterializeContext): Promise<Object3D>;
460
+ private handlePaintedScatterSceneMutation;
461
+ private refreshPaintedScatterPresence;
462
+ private disposePaintedScatterManager;
463
+ private sceneContainsPaintedScatter;
464
+ private sceneObjectsContainPaintedScatter;
465
+ private sceneObjectContainsPaintedScatter;
420
466
  private handleSurfaceScatterSceneMutation;
421
467
  private refreshSurfaceScatterPresence;
422
468
  private disposeSurfaceScatterManager;
@@ -425,6 +471,7 @@ export declare class SceneMaterializer {
425
471
  private sceneObjectContainsSurfaceScatter;
426
472
  private sceneReferencesPrefabAsset;
427
473
  private prefabAssetContainsSurfaceScatter;
474
+ private prefabAssetContainsPaintedScatter;
428
475
  private pmremGenerator?;
429
476
  private pmremGeneratorResults;
430
477
  private applyTransform;