[claude] Add glass-floor sections showing the void below the platform (#123) (#190)
Some checks failed
Deploy Nexus / deploy (push) Has been cancelled
Some checks failed
Deploy Nexus / deploy (push) Has been cancelled
This commit was merged in pull request #190.
This commit is contained in:
107
app.js
107
app.js
@@ -21,7 +21,16 @@ const scene = new THREE.Scene();
|
||||
scene.background = new THREE.Color(NEXUS.colors.bg);
|
||||
|
||||
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 2000);
|
||||
camera.position.set(0, 0, 5);
|
||||
camera.position.set(0, 6, 11);
|
||||
|
||||
// === LIGHTING ===
|
||||
// Required for MeshStandardMaterial / MeshPhysicalMaterial used on the platform.
|
||||
const ambientLight = new THREE.AmbientLight(0x0a1428, 1.4);
|
||||
scene.add(ambientLight);
|
||||
|
||||
const overheadLight = new THREE.PointLight(0x8899bb, 0.6, 60);
|
||||
overheadLight.position.set(0, 25, 0);
|
||||
scene.add(overheadLight);
|
||||
|
||||
const renderer = new THREE.WebGLRenderer({ antialias: true });
|
||||
renderer.setPixelRatio(window.devicePixelRatio);
|
||||
@@ -116,6 +125,92 @@ function buildConstellationLines() {
|
||||
const constellationLines = buildConstellationLines();
|
||||
scene.add(constellationLines);
|
||||
|
||||
// === GLASS PLATFORM ===
|
||||
// Central floating platform with transparent glass-floor sections revealing the void (star field) below.
|
||||
|
||||
const glassPlatformGroup = new THREE.Group();
|
||||
|
||||
// Dark metallic frame material
|
||||
const platformFrameMat = new THREE.MeshStandardMaterial({
|
||||
color: 0x0a1828,
|
||||
metalness: 0.9,
|
||||
roughness: 0.1,
|
||||
emissive: new THREE.Color(NEXUS.colors.accent).multiplyScalar(0.06),
|
||||
});
|
||||
|
||||
// Outer solid rim (flat ring)
|
||||
const platformRimGeo = new THREE.RingGeometry(4.7, 5.3, 64);
|
||||
const platformRim = new THREE.Mesh(platformRimGeo, platformFrameMat);
|
||||
platformRim.rotation.x = -Math.PI / 2;
|
||||
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;
|
||||
glassPlatformGroup.add(borderTorus);
|
||||
|
||||
// Glass tile material — highly transmissive to reveal the void below
|
||||
const glassTileMat = new THREE.MeshPhysicalMaterial({
|
||||
color: new THREE.Color(NEXUS.colors.accent),
|
||||
transparent: true,
|
||||
opacity: 0.09,
|
||||
roughness: 0.0,
|
||||
metalness: 0.0,
|
||||
transmission: 0.92,
|
||||
thickness: 0.06,
|
||||
side: THREE.DoubleSide,
|
||||
depthWrite: false,
|
||||
});
|
||||
|
||||
// Edge glow — bright accent outline on each tile
|
||||
const glassEdgeBaseMat = new THREE.LineBasicMaterial({
|
||||
color: NEXUS.colors.accent,
|
||||
transparent: true,
|
||||
opacity: 0.55,
|
||||
});
|
||||
|
||||
const GLASS_TILE_SIZE = 0.85;
|
||||
const GLASS_TILE_GAP = 0.14;
|
||||
const GLASS_TILE_STEP = GLASS_TILE_SIZE + GLASS_TILE_GAP;
|
||||
const GLASS_RADIUS = 4.55;
|
||||
|
||||
const tileGeo = new THREE.PlaneGeometry(GLASS_TILE_SIZE, GLASS_TILE_SIZE);
|
||||
const tileEdgeGeo = new THREE.EdgesGeometry(tileGeo);
|
||||
|
||||
/** @type {Array<{mat: THREE.LineBasicMaterial, distFromCenter: number}>} */
|
||||
const glassEdgeMaterials = [];
|
||||
|
||||
for (let row = -5; row <= 5; row++) {
|
||||
for (let col = -5; col <= 5; col++) {
|
||||
const x = col * GLASS_TILE_STEP;
|
||||
const z = row * GLASS_TILE_STEP;
|
||||
const distFromCenter = Math.sqrt(x * x + z * z);
|
||||
if (distFromCenter > GLASS_RADIUS) continue;
|
||||
|
||||
// Transparent glass tile
|
||||
const tile = new THREE.Mesh(tileGeo, glassTileMat.clone());
|
||||
tile.rotation.x = -Math.PI / 2;
|
||||
tile.position.set(x, 0, z);
|
||||
glassPlatformGroup.add(tile);
|
||||
|
||||
// Glowing edge lines
|
||||
const mat = glassEdgeBaseMat.clone();
|
||||
const edges = new THREE.LineSegments(tileEdgeGeo, mat);
|
||||
edges.rotation.x = -Math.PI / 2;
|
||||
edges.position.set(x, 0.002, z);
|
||||
glassPlatformGroup.add(edges);
|
||||
glassEdgeMaterials.push({ mat, distFromCenter });
|
||||
}
|
||||
}
|
||||
|
||||
// Void shimmer — faint point light below the glass, emphasising the infinite depth
|
||||
const voidLight = new THREE.PointLight(NEXUS.colors.accent, 0.5, 14);
|
||||
voidLight.position.set(0, -3.5, 0);
|
||||
glassPlatformGroup.add(voidLight);
|
||||
|
||||
scene.add(glassPlatformGroup);
|
||||
|
||||
// === MOUSE-DRIVEN ROTATION ===
|
||||
let mouseX = 0;
|
||||
let mouseY = 0;
|
||||
@@ -131,7 +226,7 @@ document.addEventListener('mousemove', (/** @type {MouseEvent} */ e) => {
|
||||
let overviewMode = false;
|
||||
let overviewT = 0; // 0 = normal view, 1 = overview
|
||||
|
||||
const NORMAL_CAM = new THREE.Vector3(0, 0, 5);
|
||||
const NORMAL_CAM = new THREE.Vector3(0, 6, 11);
|
||||
const OVERVIEW_CAM = new THREE.Vector3(0, 200, 0.1); // overhead; tiny Z offset avoids gimbal lock
|
||||
|
||||
const overviewIndicator = document.getElementById('overview-indicator');
|
||||
@@ -255,6 +350,14 @@ function animate() {
|
||||
// Subtle pulse on constellation opacity
|
||||
constellationLines.material.opacity = 0.12 + Math.sin(elapsed * 0.5) * 0.06;
|
||||
|
||||
// Glass platform — ripple edge glow outward from centre
|
||||
for (const { mat, distFromCenter } of glassEdgeMaterials) {
|
||||
const phase = elapsed * 1.1 - distFromCenter * 0.18;
|
||||
mat.opacity = 0.25 + Math.sin(phase) * 0.22;
|
||||
}
|
||||
// Pulse the void light below
|
||||
voidLight.intensity = 0.35 + Math.sin(elapsed * 1.4) * 0.2;
|
||||
|
||||
if (photoMode) {
|
||||
orbitControls.update();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user