| 27 | * @param {string} [o.defaultUrl] GLB to load on start |
| 28 | */ |
| 29 | export function createModelSystem({ scene, sharedUniforms, mossUniforms, defaultUrl }) { |
| 30 | const loader = new GLTFLoader(); |
| 31 | |
| 32 | // Transform wrapper — user controls live here; the GLB is recentered inside. |
| 33 | const group = new THREE.Group(); |
| 34 | group.visible = false; // hidden until the user toggles it on |
| 35 | scene.add(group); |
| 36 | let current = null; // the loaded GLB scene |
| 37 | const processed = new Set(); // materials we've already made mossy |
| 38 | |
| 39 | /* ---- moss-accumulation uniforms (shared bits + own knobs) -------------- */ |
| 40 | const moss = { |
| 41 | uMossEnabled: mossUniforms.uMossEnabled, // shared master on/off gate |
| 42 | uModelInv: { value: new THREE.Matrix4() }, // world -> model space (locks the pattern) |
| 43 | uMossSeed: { value: new THREE.Vector2(3.0, 7.0) }, // pan the coverage noise |
| 44 | uMossScale: { value: 0.9 }, // coverage noise frequency |
| 45 | uMossCoverage: { value: 0.7 }, // 0 = bare model, 1 = fully capped |
| 46 | uMossEdge: { value: 0.15 }, // coverage shoreline softness |
| 47 | uMossThickness: { value: 0.05 }, // displaced layer depth (world units) |
| 48 | uMossFlatThreshold: { value: 0.35 }, // how upward a face must be to collect |
| 49 | // --- texture & look (maps + tint shared from the ground moss) ---------- |
| 50 | uMossMap: mossUniforms.uMossMap, |
| 51 | uMossRoughnessMap: mossUniforms.uMossRoughnessMap, |
| 52 | uMossNormalMap: mossUniforms.uMossNormalMap, |
| 53 | uMossAoMap: mossUniforms.uMossAoMap, |
| 54 | uMossColor: mossUniforms.uMossColor, |
| 55 | uMossTexScale: { value: 2.0 }, // model-space moss tiling (finer than the ground) |
| 56 | uMossRoughness: { value: 1.0 }, // scales the sampled moss roughness |
| 57 | uMossAoStrength: { value: 1.0 }, // moss ambient-occlusion strength |
| 58 | uMossBump: { value: 0.5 }, // micro surface relief strength |
| 59 | uMossBumpScale: { value: 3.0 }, // micro relief frequency |
| 60 | }; |
| 61 | |
| 62 | // Shared GLSL (uniforms + noise + the coverage/relief helpers), injected into |
| 63 | // BOTH stages: the vertex stage grows the layer, the fragment stage shades it. |
| 64 | const MOSS_GLSL = /* glsl */ ` |
| 65 | varying vec3 vWorldNormalW; |
| 66 | varying vec3 vModelPosW; |
| 67 | uniform float uMossEnabled; |
| 68 | uniform mat4 uModelInv; |
| 69 | uniform vec2 uMossSeed; |
| 70 | uniform float uMossScale; |
| 71 | uniform float uMossCoverage; |
| 72 | uniform float uMossEdge; |
| 73 | uniform float uMossThickness; |
| 74 | uniform float uMossFlatThreshold; |
| 75 | uniform sampler2D uMossMap; |
| 76 | uniform sampler2D uMossRoughnessMap; |
| 77 | uniform sampler2D uMossNormalMap; |
| 78 | uniform sampler2D uMossAoMap; |
| 79 | uniform vec3 uMossColor; |
| 80 | uniform float uMossTexScale; |
| 81 | uniform float uMossRoughness; |
| 82 | uniform float uMossAoStrength; |
| 83 | uniform float uMossBump; |
| 84 | uniform float uMossBumpScale; |
| 85 | |
| 86 | vec3 permute(vec3 x) { return mod(((x * 34.0) + 1.0) * x, 289.0); } |