Spaces:
Running
Running
Codex CLI
feat(world): switch tree materials to simpler Lambert shading for performance optimization
09a3a37
import * as THREE from 'three'; | |
import { CFG } from './config.js'; | |
import { G } from './globals.js'; | |
function makeRadialCircleTexture(size = 256, innerAlpha = 1, outerAlpha = 0) { | |
const canvas = document.createElement('canvas'); | |
canvas.width = canvas.height = size; | |
const ctx = canvas.getContext('2d'); | |
const r = size / 2; | |
ctx.clearRect(0, 0, size, size); | |
const grad = ctx.createRadialGradient(r, r, 0, r, r, r); | |
grad.addColorStop(0, `rgba(255,255,255,${innerAlpha})`); | |
grad.addColorStop(1, `rgba(255,255,255,${outerAlpha})`); | |
ctx.fillStyle = grad; | |
ctx.beginPath(); | |
ctx.arc(r, r, r, 0, Math.PI * 2); | |
ctx.fill(); | |
const tex = new THREE.CanvasTexture(canvas); | |
tex.generateMipmaps = false; | |
tex.minFilter = THREE.LinearFilter; | |
tex.magFilter = THREE.LinearFilter; | |
return tex; | |
} | |
export function setupLights() { | |
// Ambient | |
const ambientLight = new THREE.HemisphereLight(0x6a7f9a, 0x203050, 0.3); | |
G.scene.add(ambientLight); | |
G.ambientLight = ambientLight; | |
// Sun light (key during day) | |
const sun = new THREE.DirectionalLight(0xfff1cc, 1.2); | |
sun.position.set(0, 100, 0); | |
sun.castShadow = true; | |
sun.shadow.camera.left = -60; | |
sun.shadow.camera.right = 60; | |
sun.shadow.camera.top = 60; | |
sun.shadow.camera.bottom = -60; | |
sun.shadow.camera.near = 0.1; | |
sun.shadow.camera.far = 240; | |
// Smaller shadow map for performance | |
sun.shadow.mapSize.width = 512; | |
sun.shadow.mapSize.height = 512; | |
sun.target = new THREE.Object3D(); | |
G.scene.add(sun); | |
G.scene.add(sun.target); | |
G.sunLight = sun; | |
// Moon light (key during night) | |
const moon = new THREE.DirectionalLight(0x6a8fc5, 0.8); | |
moon.position.set(50, 80, -50); | |
// Disable moon shadows to reduce shadow pass cost | |
moon.castShadow = false; | |
moon.target = new THREE.Object3D(); | |
G.scene.add(moon); | |
G.scene.add(moon.target); | |
G.moonLight = moon; | |
// Simple sun sprite (billboard, round via radial alpha) | |
const sunTex = makeRadialCircleTexture(256, 1, 0); | |
const sunMat = new THREE.SpriteMaterial({ color: 0xfff1cc, map: sunTex, transparent: true, opacity: 0.95, depthTest: true, depthWrite: false, fog: false }); | |
const sunSprite = new THREE.Sprite(sunMat); | |
sunSprite.scale.set(10, 10, 1); | |
sunSprite.renderOrder = 1; | |
G.scene.add(sunSprite); | |
G.sunSprite = sunSprite; | |
// Simple moon sprite | |
const moonTex = makeRadialCircleTexture(256, 1, 0); | |
const moonMat = new THREE.SpriteMaterial({ color: 0xaec7ff, map: moonTex, transparent: true, opacity: 0.8, depthTest: true, depthWrite: false, fog: false }); | |
const moonSprite = new THREE.Sprite(moonMat); | |
moonSprite.scale.set(6, 6, 1); | |
moonSprite.renderOrder = 1; | |
G.scene.add(moonSprite); | |
G.moonSprite = moonSprite; | |
// Flashlight | |
const flashlight = new THREE.SpotLight(0xffffff, CFG.flashlight.intensity); | |
flashlight.angle = CFG.flashlight.angle; | |
flashlight.penumbra = 0.2; | |
flashlight.distance = CFG.flashlight.distance; | |
flashlight.decay = 1.5; | |
// Flashlight shadowing is expensive; disable for performance | |
flashlight.castShadow = false; | |
flashlight.shadow.camera.near = 0.1; | |
flashlight.shadow.camera.far = CFG.flashlight.distance; | |
flashlight.visible = CFG.flashlight.on; | |
G.camera.add(flashlight); | |
flashlight.position.set(0, 0, 0); | |
flashlight.target.position.set(0, 0, -1); | |
G.camera.add(flashlight.target); | |
G.scene.add(G.camera); | |
G.flashlight = flashlight; | |
} | |