[claude] Dynamic shadow system from energy sources (#252) (#342)
Some checks failed
Deploy Nexus / deploy (push) Has been cancelled

This commit was merged in pull request #342.
This commit is contained in:
2026-03-24 05:09:20 +00:00
parent 82e67960e2
commit 19fc983ef0

35
app.js
View File

@@ -101,14 +101,27 @@ const forwardVector = new THREE.Vector3();
const ambientLight = new THREE.AmbientLight(0x0a1428, 1.4);
scene.add(ambientLight);
const overheadLight = new THREE.PointLight(0x8899bb, 0.6, 60);
// SpotLight replaces PointLight so shadows can be cast with a single depth map
// (PointLights require 6 cube-face renders; SpotLights need only 1)
const overheadLight = new THREE.SpotLight(0x8899bb, 0.6, 80, Math.PI / 3.5, 0.5, 1.0);
overheadLight.position.set(0, 25, 0);
overheadLight.target.position.set(0, 0, 0);
overheadLight.castShadow = true;
overheadLight.shadow.mapSize.set(2048, 2048);
overheadLight.shadow.camera.near = 5;
overheadLight.shadow.camera.far = 60;
overheadLight.shadow.bias = -0.001;
scene.add(overheadLight);
scene.add(overheadLight.target);
const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
renderer.setClearColor(0x000000, 0);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
// === SHADOW SYSTEM ===
// PCFSoftShadowMap provides smooth penumbra edges matching the holographic aesthetic.
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
document.body.appendChild(renderer.domElement);
// === STAR FIELD ===
@@ -216,12 +229,16 @@ const platformFrameMat = new THREE.MeshStandardMaterial({
const platformRimGeo = new THREE.RingGeometry(4.7, 5.3, 64);
const platformRim = new THREE.Mesh(platformRimGeo, platformFrameMat);
platformRim.rotation.x = -Math.PI / 2;
platformRim.castShadow = true;
platformRim.receiveShadow = true;
glassPlatformGroup.add(platformRim);
// Raised border torus for visible 3-D thickness
const borderTorusGeo = new THREE.TorusGeometry(5.0, 0.1, 6, 64);
const borderTorus = new THREE.Mesh(borderTorusGeo, platformFrameMat);
borderTorus.rotation.x = Math.PI / 2;
borderTorus.castShadow = true;
borderTorus.receiveShadow = true;
glassPlatformGroup.add(borderTorus);
// Glass tile material — highly transmissive to reveal the void below
@@ -388,12 +405,15 @@ const perlin = createPerlinNoise();
metalness: 0.04,
});
const topMesh = new THREE.Mesh(geo, topMat);
topMesh.castShadow = true;
topMesh.receiveShadow = true;
// Underside — tapered cylinder giving the island its rocky underbelly
const bottomGeo = new THREE.CylinderGeometry(ISLAND_RADIUS * 0.82, ISLAND_RADIUS * 0.35, 2.0, 64, 1);
const bottomMat = new THREE.MeshStandardMaterial({ color: 0x0c0a08, roughness: 0.92, metalness: 0.03 });
const bottomMesh = new THREE.Mesh(bottomGeo, bottomMat);
bottomMesh.position.y = -1.0;
bottomMesh.castShadow = true;
const islandGroup = new THREE.Group();
islandGroup.add(topMesh);
@@ -2730,7 +2750,13 @@ const tomeSpine = new THREE.Mesh(new THREE.BoxGeometry(0.06, 0.12, 1.4), tomeSpi
tomeSpine.position.set(-0.52, 0, 0);
tomeGroup.add(tomeSpine);
tomeGroup.traverse(o => { if (o.isMesh) o.userData.zoomLabel = 'The Oath'; });
tomeGroup.traverse(o => {
if (o.isMesh) {
o.userData.zoomLabel = 'The Oath';
o.castShadow = true;
o.receiveShadow = true;
}
});
scene.add(tomeGroup);
// Gentle glow beneath the tome
@@ -2742,6 +2768,11 @@ scene.add(tomeGlow);
const oathSpot = new THREE.SpotLight(0xffd700, 0, 40, Math.PI / 7, 0.4, 1.2);
oathSpot.position.set(0, 22, 0);
oathSpot.target.position.set(0, 0, 0);
oathSpot.castShadow = true;
oathSpot.shadow.mapSize.set(1024, 1024);
oathSpot.shadow.camera.near = 1;
oathSpot.shadow.camera.far = 50;
oathSpot.shadow.bias = -0.002;
scene.add(oathSpot);
scene.add(oathSpot.target);