WEBLEB
Home
Editor
Login
Pro
English
English
Français
Español
Português
Deutsch
Italiano
हिंदी
3D Physics Cube Room with Three.js and Cannon.js
13
iroger7
Open In Editor
Publish Your Code
Recommended
26 March 2025
Multi Step Form with Progress Bar using jQuery and CSS3
14 October 2024
A web page Template for "Anes Development" with a welcome message and navigation.
14 June 2024
Fireworks with mouse
HTML
Copy
3D Physics Cube Room
3D Physics Cube Room
Spawn Cube
Reset Scene
CSS
Copy
body { background: #181c20; color: #fff; font-family: sans-serif; text-align: center; margin: 0; padding: 0; } #viewer-container { width: 100vw; height: 80vh; display: flex; justify-content: center; align-items: center; background: #111; } #controls { margin: 20px 0; } button { margin: 0 10px; padding: 12px 24px; font-size: 1.1em; border: none; border-radius: 6px; background: #61dafb; color: #222; cursor: pointer; transition: background 0.2s; } button:hover { background: #21a1f3; color: #fff; }
JS
Copy
// --- SCENE, CAMERA, RENDERER, CONTROLS --- let scene, camera, renderer, controls; // --- CANNON PHYSICS WORLD --- let world; // --- CUBES: Arrays of {mesh, body} --- let cubes = []; // --- ROOM SIZE --- const ROOM_SIZE = 10; const HALF_ROOM = ROOM_SIZE / 2; // --- PHYSICS MATERIALS --- let cubeMaterial, groundMaterial, contactMaterial; function init() { // Scene & Camera scene = new THREE.Scene(); camera = new THREE.PerspectiveCamera( 75, window.innerWidth / (window.innerHeight * 0.8), 0.1, 1000 ); camera.position.set(8, 8, 8); renderer = new THREE.WebGLRenderer({ antialias: true }); renderer.setSize(window.innerWidth, window.innerHeight * 0.8); document.getElementById("viewer-container").appendChild(renderer.domElement); controls = new THREE.OrbitControls(camera, renderer.domElement); controls.enableDamping = true; controls.dampingFactor = 0.1; // Lighting scene.add(new THREE.AmbientLight(0xffffff, 0.8)); let dirLight = new THREE.DirectionalLight(0xffffff, 0.5); dirLight.position.set(10, 20, 10); scene.add(dirLight); // Physics world world = new CANNON.World(); world.gravity.set(0, -9.82, 0); world.broadphase = new CANNON.NaiveBroadphase(); world.solver.iterations = 12; // Materials for realistic collision cubeMaterial = new CANNON.Material("cubeMaterial"); groundMaterial = new CANNON.Material("groundMaterial"); contactMaterial = new CANNON.ContactMaterial( cubeMaterial, groundMaterial, { friction: 0.4, restitution: 0.5, } ); world.addContactMaterial(contactMaterial); // --- ROOM: Walls, Floor, Ceiling in Three.js and Cannon.js --- createRoom(); // --- BUTTONS --- document.getElementById("spawn-cube").onclick = spawnCube; document.getElementById("reset-scene").onclick = resetScene; // --- RENDER LOOP --- animate(); } // --- ROOM CREATION: Visual and Physics Walls --- function createRoom() { // Floor (THREE) let floorGeo = new THREE.PlaneGeometry(ROOM_SIZE, ROOM_SIZE); let floorMat = new THREE.MeshStandardMaterial({ color: 0x888888 }); let floorMesh = new THREE.Mesh(floorGeo, floorMat); floorMesh.rotation.x = -Math.PI / 2; floorMesh.position.y = 0; scene.add(floorMesh); // Floor (CANNON) let floorBody = new CANNON.Body({ mass: 0, material: groundMaterial, shape: new CANNON.Plane() }); floorBody.quaternion.setFromEuler(-Math.PI / 2, 0, 0); floorBody.position.set(0, 0, 0); world.addBody(floorBody); // Ceiling (visual only) let ceilGeo = new THREE.PlaneGeometry(ROOM_SIZE, ROOM_SIZE); let ceilMat = new THREE.MeshStandardMaterial({ color: 0x444444, side: THREE.BackSide, transparent: true, opacity: 0.4 }); let ceilMesh = new THREE.Mesh(ceilGeo, ceilMat); ceilMesh.rotation.x = Math.PI / 2; ceilMesh.position.y = ROOM_SIZE; scene.add(ceilMesh); // Walls (THREE + CANNON) addWall("z", HALF_ROOM, 0, 0); // back addWall("z", -HALF_ROOM, Math.PI, 0); // front addWall("x", HALF_ROOM, -Math.PI / 2, 0); // right addWall("x", -HALF_ROOM, Math.PI / 2, 0); // left } // --- ADD A WALL (visual and physics) --- function addWall(axis, pos, rotY, rotZ) { // Visual let wallGeo = new THREE.PlaneGeometry(ROOM_SIZE, ROOM_SIZE); let wallMat = new THREE.MeshStandardMaterial({ color: 0x666666, side: THREE.DoubleSide, transparent: true, opacity: 0.25 }); let wallMesh = new THREE.Mesh(wallGeo, wallMat); if (axis === "z") { wallMesh.position.z = pos; wallMesh.position.y = HALF_ROOM; wallMesh.rotation.y = rotY; } else { wallMesh.position.x = pos; wallMesh.position.y = HALF_ROOM; wallMesh.rotation.y = rotY; } scene.add(wallMesh); // Physics let wallShape = new CANNON.Plane(); let wallBody = new CANNON.Body({ mass: 0, material: groundMaterial }); if (axis === "z") { wallBody.quaternion.setFromEuler(-Math.PI / 2, rotY, 0); wallBody.position.set(0, HALF_ROOM, pos); } else { wallBody.quaternion.setFromEuler(-Math.PI / 2, rotY, 0); wallBody.position.set(pos, HALF_ROOM, 0); } world.addBody(wallBody); } // --- SPAWN CUBE --- function spawnCube() { const size = 1; // THREE let geometry = new THREE.BoxGeometry(size, size, size); let material = new THREE.MeshStandardMaterial({ color: Math.random() * 0xffffff, metalness: 0.4, roughness: 0.7 }); let mesh = new THREE.Mesh(geometry, material); // Random position near top mesh.position.set( (Math.random() - 0.5) * (ROOM_SIZE - 2), ROOM_SIZE - 1.5, (Math.random() - 0.5) * (ROOM_SIZE - 2) ); scene.add(mesh); // CANNON let shape = new CANNON.Box(new CANNON.Vec3(size / 2, size / 2, size / 2)); let body = new CANNON.Body({ mass: 1, shape: shape, material: cubeMaterial, position: new CANNON.Vec3(mesh.position.x, mesh.position.y, mesh.position.z) }); // Give a small random velocity for fun body.velocity.set( (Math.random() - 0.5) * 3, Math.random() * 2, (Math.random() - 0.5) * 3 ); world.addBody(body); cubes.push({ mesh, body }); } // --- RESET SCENE: Remove all cubes --- function resetScene() { cubes.forEach(({ mesh, body }) => { scene.remove(mesh); world.remove(body); }); cubes = []; } // --- ANIMATION LOOP --- function animate() { requestAnimationFrame(animate); // Step physics world.step(1 / 60); // Sync cube meshes with their physics bodies cubes.forEach(({ mesh, body }) => { mesh.position.copy(body.position); mesh.quaternion.copy(body.quaternion); }); controls.update(); renderer.render(scene, camera); } // --- RESPONSIVE --- window.addEventListener("resize", () => { camera.aspect = window.innerWidth / (window.innerHeight * 0.8); camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight * 0.8); }); // --- INIT --- init();