World/Live: 3D ball nodes (spheres) instead of flat discs
ci / gates (push) Has been cancelled
ci / rust (push) Has been cancelled
ci / sandbox-k8s (push) Has been cancelled
ci / frontend (push) Has been cancelled
ci / e2e (push) Has been cancelled

Switch the Live view's structure nodes + agent pawns from flat CircleGeometry
to lit SphereGeometry (MeshStandardMaterial with emissive + ambient/directional
lights), so they read as 3D balls from any orbit angle instead of going edge-on.
The additive glow sprites + particle effects are unlit, so the neon look is kept.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
This commit is contained in:
Omar Sobh
2026-06-24 05:27:17 -07:00
co-authored by Claude Opus 4.8
parent db7569e5d3
commit 928e27cde5
+18 -8
View File
@@ -229,7 +229,13 @@ export function WorldCanvas({ roots, selectedId, onSelect, expanded, onToggleExp
}); });
const glow = makeGlowTexture(); const glow = makeGlowTexture();
const circle = new THREE.CircleGeometry(1, 28); const sphere = new THREE.SphereGeometry(1, 18, 14);
// lights so the Live nodes/pawns read as shaded 3D balls (the additive glow
// sprites + particles stay unlit, so the neon effects are unaffected)
scene.add(new THREE.AmbientLight(0xffffff, 0.5));
const keyLight = new THREE.DirectionalLight(0xffffff, 0.95);
keyLight.position.set(0.4, 1, 0.8);
scene.add(keyLight);
// Two render groups so the formation tabs switch whole views cleanly. // Two render groups so the formation tabs switch whole views cleanly.
const gourceGroup = new THREE.Group(); const gourceGroup = new THREE.Group();
@@ -557,17 +563,19 @@ export function WorldCanvas({ roots, selectedId, onSelect, expanded, onToggleExp
liveNodeIds.add(n.id); liveNodeIds.add(n.id);
let m = nodeMeshes.get(n.id); let m = nodeMeshes.get(n.id);
if (!m) { if (!m) {
m = new THREE.Mesh(circle, new THREE.MeshBasicMaterial({ transparent: true, depthWrite: false })); m = new THREE.Mesh(sphere, new THREE.MeshStandardMaterial({ transparent: true, depthWrite: false, roughness: 0.45, metalness: 0.15 }));
m.userData.id = n.id; m.userData.id = n.id;
nodeMeshes.set(n.id, m); nodeMeshes.set(n.id, m);
gourceGroup.add(m); gourceGroup.add(m);
} }
const r = n.r * (1 + n.heat * 0.4); const r = n.r * (1 + n.heat * 0.4);
m.position.set(n.x, n.y, 0); m.position.set(n.x, n.y, 0);
m.scale.set(r, r, 1); m.scale.set(r, r, r);
const mat = m.material as THREE.MeshBasicMaterial; const mat = m.material as THREE.MeshStandardMaterial;
col.set(n.color).lerp(white, n.heat * 0.6); col.set(n.color).lerp(white, n.heat * 0.6);
mat.color.copy(col); mat.color.copy(col);
mat.emissive.copy(col);
mat.emissiveIntensity = 0.35 + n.heat * 0.6;
mat.opacity = n.alpha * (n.tier === "root" ? 1 : 0.6 + n.heat * 0.4); mat.opacity = n.alpha * (n.tier === "root" ? 1 : 0.6 + n.heat * 0.4);
let g = nodeGlows.get(n.id); let g = nodeGlows.get(n.id);
if (!g) { if (!g) {
@@ -635,16 +643,18 @@ export function WorldCanvas({ roots, selectedId, onSelect, expanded, onToggleExp
livePawnIds.add(p.id); livePawnIds.add(p.id);
let m = pawnMeshes.get(p.id); let m = pawnMeshes.get(p.id);
if (!m) { if (!m) {
m = new THREE.Mesh(circle, new THREE.MeshBasicMaterial({ transparent: true, depthWrite: false })); m = new THREE.Mesh(sphere, new THREE.MeshStandardMaterial({ transparent: true, depthWrite: false, roughness: 0.45, metalness: 0.15 }));
m.userData.id = p.id; m.userData.id = p.id;
pawnMeshes.set(p.id, m); pawnMeshes.set(p.id, m);
gourceGroup.add(m); gourceGroup.add(m);
} }
const pa = p.alpha * (showPawns ? 1 : 0.5); const pa = p.alpha * (showPawns ? 1 : 0.5);
m.position.set(p.x, p.y, 2); m.position.set(p.x, p.y, 2);
m.scale.set(8, 8, 1); m.scale.set(8, 8, 8);
const mat = m.material as THREE.MeshBasicMaterial; const mat = m.material as THREE.MeshStandardMaterial;
mat.color.set(p.color); mat.color.set(p.color);
mat.emissive.set(p.color);
mat.emissiveIntensity = 0.7;
mat.opacity = pa; mat.opacity = pa;
let g = pawnGlows.get(p.id); let g = pawnGlows.get(p.id);
if (!g) { if (!g) {
@@ -766,7 +776,7 @@ export function WorldCanvas({ roots, selectedId, onSelect, expanded, onToggleExp
pawnMeshes.forEach((m) => (m.material as THREE.Material).dispose()); pawnMeshes.forEach((m) => (m.material as THREE.Material).dispose());
pawnGlows.forEach((s) => (s.material as THREE.Material).dispose()); pawnGlows.forEach((s) => (s.material as THREE.Material).dispose());
neuronGlows.forEach((s) => (s.material as THREE.Material).dispose()); neuronGlows.forEach((s) => (s.material as THREE.Material).dispose());
circle.dispose(); sphere.dispose();
edgeGeom.dispose(); edgeGeom.dispose();
beamGeom.dispose(); beamGeom.dispose();
brainEdgeGeom.dispose(); brainEdgeGeom.dispose();