JIT
More realistic mountains
Make it look more like actual mountains /* @nwWrld name: AudioDreamMountains @nwWrld category: Audio @nwWrld imports: BaseThreeJsModule, THREE, Noise, AudioAnalyzer */ class AudioDreamMountains extends BaseThreeJsModule { static methods = [ { name: "start", executeOnLoad: true, options: [ { name: "sensitivity", defaultVal: 2.0, type: "number", min: 0.1, max: 5.0 }, { name: "mountainIntensity", defaultVal: 1.0, type: "number", min: 0.1, max: 100.0 }, { name: "speed", defaultVal: 1.0, type: "number", min: 0.1, max: 10.0 }, ], }, { name: "setSensitivity", executeOnLoad: false, options: [{ name: "value", defaultVal: 2.0, type: "number", min: 0.1, max: 5.0 }], }, { name: "setMountainIntensity", executeOnLoad: false, options: [{ name: "value", defaultVal: 1.0, type: "number", min: 0.1, max: 100.0 }], }, { name: "setSpeed", executeOnLoad: false, options: [{ name: "value", defaultVal: 1.0, type: "number", min: 0.1, max: 3.0 }], }, { name: "setAudioReactive", executeOnLoad: false, options: [{ name: "enabled", defaultVal: true, type: "boolean" }], }, { name: "setSnowIntensity", executeOnLoad: false, options: [{ name: "value", defaultVal: 1.0, type: "number", min: 0.0, max: 3.0 }], }, ]; constructor(container) { super(container); if (!THREE || !Noise) return; this.name = AudioDreamMountains.name; this.customGroup = new THREE.Group(); this.mountains = []; this.mountainSegments = []; this.snowParticles = null; this.noise = new Noise(Math.random()); this.analyzer = null; this.audioReady = false; this.pollInterval = null; this.sensitivity = 2.0; this.mountainIntensity = 1.0; this.speed = 1.0; this.audioReactive = true; this.volume = 0; this.bass = 0; this.mid = 0; this.treble = 0; this.time = 0; this.cameraX = 0; this.currentMountainHeight = 0; this.snowIntensity = 1.0; this.sky = null; this.moon = null; this.moonRings = []; this.spotlight = null; this.rimLight = null; this.ambientLight = null; this.init(); } async start({ sensitivity = 2.0, mountainIntensity = 1.0, speed = 1.0 } = {}) { const sensVal = Number(sensitivity); const intensityVal = Number(mountainIntensity); const speedVal = Number(speed); this.sensitivity = Math.max(0.1, Math.min(5.0, Number.isFinite(sensVal) ? sensVal : 2.0)); this.mountainIntensity = Math.max(0.1, Math.min(3.0, Number.isFinite(intensityVal) ? intensityVal : 1.0)); this.speed = Math.max(0.1, Math.min(3.0, Number.isFinite(speedVal) ? speedVal : 1.0)); this.snowIntensity = 1.0; await this.tryInitializeAudio(); if (!this.audioReady) this.startStreamPolling(); } async tryInitializeAudio() { if (this.audioReady || this.destroyed) return; const sdk = globalThis.nwWrldSdk; const stream = sdk?.audio?.getStream?.(); if (stream) { this.analyzer = new AudioAnalyzer(); const initialized = await this.analyzer.init(stream); if (initialized) { this.audioReady = true; if (this.pollInterval) { clearInterval(this.pollInterval); this.pollInterval = null; } } } } startStreamPolling() { if (this.pollInterval) return; this.pollInterval = setInterval(() => { if (this.destroyed) { clearInterval(this.pollInterval); this.pollInterval = null; return; } this.tryInitializeAudio(); }, 1000); } init() { if (!this.renderer || !this.scene || !this.camera || this.destroyed) return; this.scene.fog = new THREE.FogExp2(0x1a1a2e, 0.008); this.renderer.setClearColor(0x1a1a2e); this.setupLighting(); this.createMountains(); this.createSnowParticles(); this.createMountainSky(); this.setModel(this.customGroup); this.setCustomAnimate(this.audioAnimate.bind(this)); } setupLighting() { const ambientLight = new THREE.AmbientLight(0xffffff, 0.6); this.scene.add(ambientLight); this.ambientLight = ambientLight; const spotlight = new THREE.SpotLight(0xffffff, 20, 40, Math.PI * 0.2, 0.4); spotlight.position.set(0, 20, 10); spotlight.target.position.set(0, 0, 0); spotlight.castShadow = true; this.scene.add(spotlight); this.scene.add(spotlight.target); this.spotlight = spotlight; const rimLight = new THREE.SpotLight(0x88ccff, 12, 30, Math.PI * 0.25, 0.5); rimLight.position.set(-15, 15, -10); rimLight.target.position.set(0, 0, 0); this.scene.add(rimLight); this.scene.add(rimLight.target); this.rimLight = rimLight; } createMountains() { this.mountainSegments = []; const segmentSize = 200; const segmentCount = 3; const mountainsPerSegment = 15; for (let segIndex = 0; segIndex < segmentCount; segIndex++) { const segmentGroup = new THREE.Group(); segmentGroup.position.set((segIndex - 1) * segmentSize, 0, 0); for (let i = 0; i < mountainsPerSegment; i++) { const width = 8 + Math.random() * 15; const height = 4 + Math.random() * 12; const depth = 8 + Math.random() * 15; const geometry = new THREE.ConeGeometry(width, height, 8, 1); const originalPositions = geometry.attributes.position.array.slice(); const material = new THREE.MeshPhysicalMaterial({ color: 0xffffff, metalness: 0.2, roughness: 0.7, side: THREE.DoubleSide, emissive: 0xffffff, emissiveIntensity: 0.1, }); const mountain = new THREE.Mesh(geometry, material); mountain.position.set( (i - mountainsPerSegment / 2) * 12, height / 2, (Math.random() - 0.5) * 100 - 30 ); mountain.rotation.y = (Math.random() - 0.5) * 0.5; mountain.userData = { originalPositions: originalPositions, baseHeight: height, baseWidth: width, }; segmentGroup.add(mountain); this.mountains.push(mountain); } segmentGroup.userData = { segmentIndex: segIndex, }; this.customGroup.add(segmentGroup); this.mountainSegments.push(segmentGroup); } } createSnowParticles() { const positions = []; const colors = []; for (let i = 0; i < 800; i++) { positions.push( (Math.random() - 0.5) * 600, Math.random() * 60, (Math.random() - 0.5) * 300 ); colors.push(1, 1, 1); } const geometry = new THREE.BufferGeometry(); geometry.setAttribute("position", new THREE.Float32BufferAttribute(positions, 3)); geometry.setAttribute("color", new THREE.Float32BufferAttribute(colors, 3)); const material = new THREE.PointsMaterial({ size: 0.25, vertexColors: true, transparent: true, opacity: 0.9, sizeAttenuation: true, }); this.snowParticles = new THREE.Points(geometry, material); this.customGroup.add(this.snowParticles); } createMountainSky() { const skyGeo = new THREE.SphereGeometry(500, 32, 32); const skyMat = new THREE.ShaderMaterial({ side: THREE.BackSide, uniforms: { topColor: { value: new THREE.Color(0x1a1a2e) }, bottomColor: { value: new THREE.Color(0x16213e) }, offset: { value: 33 }, exponent: { value: 0.6 } }, vertexShader: ` varying vec3 vWorldPosition; void main() { vec4 worldPosition = modelMatrix * vec4(position, 1.0); vWorldPosition = worldPosition.xyz; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); } `, fragmentShader: ` uniform vec3 topColor; uniform vec3 bottomColor; uniform float offset; uniform float exponent; varying vec3 vWorldPosition; void main() { float h = normalize(vWorldPosition).y + offset * 0.01; gl_FragColor = vec4(mix(bottomColor, topColor, max(pow(max(h, 0.0), exponent), 0.0)), 1.0); } ` }); this.sky = new THREE.Mesh(skyGeo, skyMat); this.scene.add(this.sky); const moonGeo = new THREE.SphereGeometry(15, 32, 32); const moonMat = new THREE.MeshBasicMaterial({ color: 0xe8e8e8, transparent: true, opacity: 0.95 }); this.moon = new THREE.Mesh(moonGeo, moonMat); this.moon.position.set(0, 30, -120); this.scene.add(this.moon); this.moonRings = []; for (let i = 0; i < 8; i++) { const ringGeo = new THREE.RingGeometry(15 + i * 2.5, 15 + i * 2.5 + 0.4, 64); const ringMat = new THREE.MeshBasicMaterial({ color: 0x88ccff, side: THREE.DoubleSide, transparent: true, opacity: 0.7 - i * 0.08 }); const ring = new THREE.Mesh(ringGeo, ringMat); ring.position.copy(this.moon.position); ring.lookAt(0, 0, 0); this.scene.add(ring); this.moonRings.push(ring); } } audioAnimate() { if (this.destroyed) return; this.time += 0.016; if (this.audioReactive && this.analyzer && this.audioReady) { this.volume = this.analyzer.getVolume() * this.sensitivity; this.bass = this.analyzer.getBass() * this.sensitivity; this.mid = this.analyzer.getMid() * this.sensitivity; this.treble = this.analyzer.getTreble() * this.sensitivity; } else if (!this.audioReactive) { this.volume = 0.3 * this.sensitivity; this.bass = 0.2 * this.sensitivity; this.mid = 0.3 * this.sensitivity; this.treble = 0.2 * this.sensitivity; } const flySpeed = this.speed * (1 + this.volume * 0.5); this.cameraX += flySpeed * 0.3; this.updateMountains(); this.updateSnow(); this.updateLighting(); this.updateCamera(); } updateMountains() { if (!this.mountainSegments || this.mountainSegments.length === 0) return; const mountainHeight = this.mountainIntensity * 1.5 * (0.8 + this.bass * 0.6); this.currentMountainHeight = mountainHeight; const segmentSize = 200; this.mountainSegments.forEach((segmentGroup) => { const relativeX = segmentGroup.position.x - this.cameraX; if (relativeX < -segmentSize * 1.5) { segmentGroup.position.x += segmentSize * 3; } segmentGroup.children.forEach((mountain) => { const positions = mountain.geometry.attributes.position; const originalPositions = mountain.userData.originalPositions; const baseHeight = mountain.userData.baseHeight; for (let i = 0; i < positions.count; i++) { const i3 = i * 3; const ox = originalPositions[i3]; const oy = originalPositions[i3 + 1]; const oz = originalPositions[i3 + 2]; const worldX = ox + mountain.position.x + segmentGroup.position.x; const worldZ = oz + mountain.position.z; const noise1 = this.noise.simplex2(worldX * 0.05, worldZ * 0.05 + this.time * 0.3) * mountainHeight * 0.3; const noise2 = this.noise.simplex2(worldX * 0.1, worldZ * 0.1 + this.time * 0.5) * mountainHeight * 0.2; const wave = Math.sin(worldX * 0.1 + this.time * 1.5) * mountainHeight * 0.1; const heightMod = oy > 0 ? 1 + (noise1 + noise2 + wave) / baseHeight : 1; positions.array[i3] = ox * (1 + (noise1 + noise2) * 0.05); positions.array[i3 + 1] = oy * heightMod; positions.array[i3 + 2] = oz * (1 + (noise1 + noise2) * 0.05); } positions.needsUpdate = true; mountain.geometry.computeVertexNormals(); const brightness = 0.7 + this.mid * 0.3; const snowBrightness = 0.9 + this.treble * 0.1; mountain.material.color.setRGB(brightness, brightness, snowBrightness); mountain.material.emissiveIntensity = 0.1 + this.treble * 0.2; }); }); } updateSnow() { if (!this.snowParticles || this.snowIntensity === 0) return; const positions = this.snowParticles.geometry.attributes.position; const colors = this.snowParticles.geometry.attributes.color; for (let i = 0; i < positions.count; i++) { const i3 = i * 3; positions.array[i3 + 1] -= 0.1 * this.snowIntensity * (1 + this.treble * 0.3); positions.array[i3] += Math.sin(this.time * 2 + i) * 0.02; positions.array[i3 + 2] += Math.cos(this.time * 1.5 + i) * 0.02; if (positions.array[i3 + 1] < -5) { positions.array[i3] = this.cameraX + (Math.random() - 0.5) * 400; positions.array[i3 + 1] = 40 + Math.random() * 20; positions.array[i3 + 2] = (Math.random() - 0.5) * 300; } const brightness = 0.7 + this.treble * 0.3; colors.array[i3] = brightness; colors.array[i3 + 1] = brightness; colors.array[i3 + 2] = brightness; } positions.needsUpdate = true; colors.needsUpdate = true; this.snowParticles.material.size = 0.2 * this.snowIntensity + this.treble * 0.15; this.snowParticles.material.opacity = 0.9 * Math.min(this.snowIntensity, 1.0); } updateLighting() { if (this.spotlight) { this.spotlight.intensity = 20 + this.bass * 25; this.spotlight.position.x = this.cameraX; this.spotlight.target.position.x = this.cameraX; } if (this.rimLight) { this.rimLight.intensity = 12 + this.mid * 18; this.rimLight.position.x = this.cameraX - 15; this.rimLight.target.position.x = this.cameraX; } if (this.moon) { this.moon.position.x = this.cameraX; } if (this.moonRings) { this.moonRings.forEach((ring) => { ring.position.x = this.cameraX; }); } } updateCamera() { const baseHeight = 18; const mountainHeightOffset = this.currentMountainHeight * 0.3; const height = baseHeight + mountainHeightOffset + Math.sin(this.time * 0.15) * 2; this.camera.position.x = this.cameraX; this.camera.position.y = height; this.camera.position.z = 12; this.camera.lookAt(this.cameraX + 25, height - 6, 0); } setSensitivity({ value = 2.0 } = {}) { const val = Number(value); this.sensitivity = Math.max(0.1, Math.min(5.0, Number.isFinite(val) ? val : 2.0)); } setMountainIntensity({ value = 1.0 } = {}) { const val = Number(value); this.mountainIntensity = Math.max(0.1, Math.min(3.0, Number.isFinite(val) ? val : 1.0)); } setSpeed({ value = 1.0 } = {}) { const val = Number(value); this.speed = Math.max(0.1, Math.min(3.0, Number.isFinite(val) ? val : 1.0)); } setAudioReactive({ enabled = true } = {}) { this.audioReactive = Boolean(enabled); } setSnowIntensity({ value = 1.0 } = {}) { const val = Number(value); this.snowIntensity = Math.max(0.0, Math.min(3.0, Number.isFinite(val) ? val : 1.0)); } destroy() { this.destroyed = true; if (this.pollInterval) { clearInterval(this.pollInterval); this.pollInterval = null; } if (this.analyzer) { this.analyzer.destroy(); this.analyzer = null; } if (this.mountains) { this.mountains.forEach((mountain) => { mountain.geometry.dispose(); mountain.material.dispose(); this.customGroup.remove(mountain); }); this.mountains = []; } if (this.snowParticles) { this.snowParticles.geometry.dispose(); this.snowParticles.material.dispose(); this.customGroup.remove(this.snowParticles); } if (this.sky) { this.sky.geometry.dispose(); this.sky.material.dispose(); this.scene.remove(this.sky); } if (this.moon) { this.moon.geometry.dispose(); this.moon.material.dispose(); this.scene.remove(this.moon); } if (this.moonRings) { this.moonRings.forEach((ring) => { ring.geometry.dispose(); ring.material.dispose(); this.scene.remove(ring); }); this.moonRings = []; } if (this.spotlight) { this.scene.remove(this.spotlight); this.scene.remove(this.spotlight.target); } if (this.rimLight) { this.scene.remove(this.rimLight); this.scene.remove(this.rimLight.target); } if (this.ambientLight) { this.scene.remove(this.ambientLight); } if (this.customGroup) { this.scene.remove(this.customGroup); } super.destroy(); } } export default AudioDreamMountains;
Read file
Trey again
Read file
Write file
Edit file
Edit file
Edit file
Edit file
Edit file
Edit file
Edit file
Enhanced mountain visual realism
v1