# Centre of Gravity

## Description

You can simulate pointing gravity towards a 3D point by cancelling out the world gravity and applying a force towards the point where you want the new simulated gravity to be.

This example creates many spheres all attracted to the point `0,0,0`. In the example, each body is applied a force opposite to its own world position relative to `0,0,0`.

Note that in this example, world gravity is set to `0,-1,0` rather than cancelling it out completely to `0,0,0`. This is so that bodies will still react to surface friction and the spheres will appear to rotate as they roll over or smash against each other. If you do set world gravity to `0,0,0`, then the surface friction calculations within Cannon will equate to `0` and the spheres won't appear rotate when they collide. In Cannon, when bodies collide, If either body has mass `0` or the world gravity is `0,0,0`, then any friction multiplications equate to 0.

## Example Script

### ./src/client/client.ts

 ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151``` ``````import * as THREE from 'three' import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls' import Stats from 'three/examples/jsm/libs/stats.module' import { GUI } from 'dat.gui' import * as CANNON from 'cannon-es' const scene = new THREE.Scene() scene.add(new THREE.AxesHelper(5)) const light1 = new THREE.SpotLight(0xffffff, 100) light1.position.set(2.5, 5, 5) light1.angle = Math.PI / 4 light1.penumbra = 0.5 light1.castShadow = true light1.shadow.mapSize.width = 1024 light1.shadow.mapSize.height = 1024 light1.shadow.camera.near = 0.5 light1.shadow.camera.far = 20 scene.add(light1) const light2 = new THREE.SpotLight(0xffffff, 100) light2.position.set(-2.5, 5, 5) light2.angle = Math.PI / 4 light2.penumbra = 0.5 light2.castShadow = true light2.shadow.mapSize.width = 1024 light2.shadow.mapSize.height = 1024 light2.shadow.camera.near = 0.5 light2.shadow.camera.far = 20 scene.add(light2) scene.background = new THREE.CubeTextureLoader().load([ 'img/px_eso0932a.jpg', 'img/nx_eso0932a.jpg', 'img/py_eso0932a.jpg', 'img/ny_eso0932a.jpg', 'img/pz_eso0932a.jpg', 'img/nz_eso0932a.jpg', ]) const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 ) camera.position.set(0.5, 0.5, 6) const renderer = new THREE.WebGLRenderer() renderer.setSize(window.innerWidth, window.innerHeight) renderer.shadowMap.enabled = true document.body.appendChild(renderer.domElement) const controls = new OrbitControls(camera, renderer.domElement) controls.enableDamping = true controls.target.y = 0.5 const world = new CANNON.World() world.gravity.set(0, -1, 0) // setting minimal gravity otherwise you lose friction calculations const moonMaterial = new THREE.MeshStandardMaterial() let texture = new THREE.TextureLoader().load('img/moon_540x270.jpg') moonMaterial.map = texture const sphereMeshes: THREE.Mesh[] = [] const sphereBodies: CANNON.Body[] = [] for (let x = 0; x < 100; x++) { const sphereGeometry = new THREE.SphereGeometry(0.5) sphereMeshes.push(new THREE.Mesh(sphereGeometry, moonMaterial)) sphereMeshes[x].position.x = Math.random() * 100 - 50 sphereMeshes[x].position.y = Math.random() * 100 - 50 sphereMeshes[x].position.z = Math.random() * 100 - 50 sphereMeshes[x].castShadow = true sphereMeshes[x].receiveShadow = true scene.add(sphereMeshes[x]) const sphereShape = new CANNON.Sphere(0.5) sphereBodies.push(new CANNON.Body({ mass: 1 })) sphereBodies[x].addShape(sphereShape) sphereBodies[x].position.x = sphereMeshes[x].position.x sphereBodies[x].position.y = sphereMeshes[x].position.y sphereBodies[x].position.z = sphereMeshes[x].position.z world.addBody(sphereBodies[x]) } world.addEventListener('postStep', function () { // Gravity towards (0,0,0) sphereBodies.forEach((s) => { const v = new CANNON.Vec3() v.set(-s.position.x, -s.position.y, -s.position.z).normalize() v.scale(9.8, s.force) s.applyLocalForce(v) s.force.y += s.mass //cancel out world gravity }) }) const button = { explode: function () { sphereBodies.forEach((s) => { s.force.set(s.position.x, s.position.y, s.position.z).normalize() s.velocity = s.force.scale(Math.random() * 50) }) }, } window.addEventListener('resize', onWindowResize, false) function onWindowResize() { camera.aspect = window.innerWidth / window.innerHeight camera.updateProjectionMatrix() renderer.setSize(window.innerWidth, window.innerHeight) render() } const stats = new Stats() document.body.appendChild(stats.dom) const gui = new GUI() gui.add(button, 'explode') const clock = new THREE.Clock() let delta function animate() { requestAnimationFrame(animate) controls.update() delta = Math.min(clock.getDelta(), 0.1) world.step(delta) sphereBodies.forEach((s, i) => { sphereMeshes[i].position.set(s.position.x, s.position.y, s.position.z) sphereMeshes[i].quaternion.set( s.quaternion.x, s.quaternion.y, s.quaternion.z, s.quaternion.w ) }) render() stats.update() } function render() { renderer.render(scene, camera) } animate() ``````