BusinessFlowsVR / main.js
Russell Gomersall
Update space
ac3f654
// main.js
import * as THREE from "https://unpkg.com/three@0.164.0/build/three.module.js";
import { VRButton } from "https://unpkg.com/three@0.164.0/examples/jsm/webxr/VRButton.js";
// ----- 1. Basic setup -----
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x000000);
const camera = new THREE.PerspectiveCamera(
70,
window.innerWidth / window.innerHeight,
0.1,
1000
);
camera.position.set(0, 1.6, 3); // typical VR eye height
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.xr.enabled = true;
document.body.appendChild(renderer.domElement);
// Add VR button
document.body.appendChild(VRButton.createButton(renderer));
// Simple light setup
const light = new THREE.DirectionalLight(0xffffff, 0.8);
light.position.set(5, 10, 7);
scene.add(light);
const ambient = new THREE.AmbientLight(0xffffff, 0.3);
scene.add(ambient);
// ----- 2. Sample graph data (replace with Neo4j later) -----
// Minimal example: 6 nodes, some edges
const graphData = {
nodes: [
{ id: "A", label: "Node A" },
{ id: "B", label: "Node B" },
{ id: "C", label: "Node C" },
{ id: "D", label: "Node D" },
{ id: "E", label: "Node E" },
{ id: "F", label: "Node F" }
],
edges: [
{ source: "A", target: "B" },
{ source: "A", target: "C" },
{ source: "B", target: "D" },
{ source: "C", target: "D" },
{ source: "C", target: "E" },
{ source: "E", target: "F" }
]
};
// ----- 3. Layout: place nodes around a circle -----
const nodeMap = new Map(); // id -> mesh
const radius = 2;
const nodeGeometry = new THREE.SphereGeometry(0.08, 16, 16);
const nodeMaterial = new THREE.MeshStandardMaterial({ color: 0x00bcd4 });
graphData.nodes.forEach((node, index) => {
const angle = (index / graphData.nodes.length) * Math.PI * 2;
const x = radius * Math.cos(angle);
const z = radius * Math.sin(angle);
const y = 0;
const mesh = new THREE.Mesh(nodeGeometry, nodeMaterial.clone());
mesh.position.set(x, y, z);
mesh.userData = { id: node.id, label: node.label };
scene.add(mesh);
nodeMap.set(node.id, mesh);
});
// ----- 4. Draw edges as lines between node positions -----
const edgeMaterial = new THREE.LineBasicMaterial({ color: 0xffffff, linewidth: 1 });
graphData.edges.forEach((edge) => {
const sourceMesh = nodeMap.get(edge.source);
const targetMesh = nodeMap.get(edge.target);
if (!sourceMesh || !targetMesh) return;
const points = [];
points.push(sourceMesh.position.clone());
points.push(targetMesh.position.clone());
const edgeGeometry = new THREE.BufferGeometry().setFromPoints(points);
const line = new THREE.Line(edgeGeometry, edgeMaterial);
scene.add(line);
});
// ----- 5. Subtle animation for visual interest -----
function animate() {
renderer.setAnimationLoop(renderScene);
}
function renderScene() {
// Slowly rotate the whole graph
scene.rotation.y += 0.0015;
renderer.render(scene, camera);
}
animate();
// ----- 6. Handle resize -----
window.addEventListener("resize", () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});