Skip to content


 Zabbix
 Grafana
 Prometheus
 React Three Fiber
 Threejs and TypeScript
 SocketIO and TypeScript
 Blender Topological Earth
 Sweet Home 3D
 Design Patterns Python
 Design Patterns TypeScript
   
 Course Coupon Codes
Three.js and TypeScript
Kindle Edition
$6.99 $9.99 Paperback 
$22.99 $29.99




Design Patterns in TypeScript
Kindle Edition
$6.99 $9.99 Paperback
$11.99 $19.99




Design Patterns in Python
Kindle Edition
$6.99 $9.99 Paperback
$11.99 $19.99




Examples : Point to Point Constraints

Description

This example demonstrates,

  • Using Cannon.js as the Physics engine.
  • Using CANNON.PointToPointConstraint to restrict the ball movement.
  • Using the Drag Controls on a CANNON.Body.

Code

./src/client/client.ts

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'
import CannonDebugRenderer from './utils/cannonDebugRenderer'
import { DragControls } from 'three/examples/jsm/controls/DragControls'

const scene = new THREE.Scene()

const light1 = new THREE.SpotLight(0xffffff, 300)
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, 300)
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)

const camera = new THREE.PerspectiveCamera(
    75,
    window.innerWidth / window.innerHeight,
    0.1,
    1000
)
camera.position.y = 10
camera.position.z = 3

const renderer = new THREE.WebGLRenderer()
renderer.setSize(window.innerWidth, window.innerHeight)
renderer.shadowMap.enabled = true
renderer.shadowMap.type = THREE.PCFSoftShadowMap
document.body.appendChild(renderer.domElement)

const controls = new OrbitControls(camera, renderer.domElement)
controls.screenSpacePanning = true
controls.target.y = 1

const world = new CANNON.World()
world.gravity.set(0, -9.82, 0)

const normalMaterial = new THREE.MeshNormalMaterial()
const phongMaterial = new THREE.MeshPhongMaterial()

const planeGeometry = new THREE.PlaneGeometry(25, 25)
const planeMesh = new THREE.Mesh(planeGeometry, phongMaterial)
planeMesh.rotateX(-Math.PI / 2)
planeMesh.receiveShadow = true
scene.add(planeMesh)
const planeShape = new CANNON.Plane()
const planeBody = new CANNON.Body({ mass: 0 })
planeBody.addShape(planeShape)
planeBody.quaternion.setFromAxisAngle(new CANNON.Vec3(1, 0, 0), -Math.PI / 2)
world.addBody(planeBody)

const sphereMeshes: THREE.Object3D[] = []
const sphereBodies: CANNON.Body[] = []

const sphereGeometry = new THREE.SphereGeometry()
const sphereMesh = new THREE.Mesh(sphereGeometry, normalMaterial)
let i = 0
for (let x = -5; x <= 6; x += 2.5) {
    for (let z = -5; z <= 6; z += 2.5) {
        const sphereMeshClone = sphereMesh.clone()
        sphereMeshClone.position.x = x
        sphereMeshClone.position.y = 3
        sphereMeshClone.position.z = z
        sphereMeshClone.castShadow = true
        sphereMeshClone.userData.i = i
        scene.add(sphereMeshClone)
        sphereMeshes.push(sphereMeshClone)

        const sphereBody = new CANNON.Body({ mass: 1 })
        sphereBody.addShape(new CANNON.Sphere(1))
        sphereBody.position.x = sphereMeshClone.position.x
        sphereBody.position.y = sphereMeshClone.position.y
        sphereBody.position.z = sphereMeshClone.position.z
        world.addBody(sphereBody)
        sphereBodies.push(sphereBody)

        const localPivotSphere = new CANNON.Vec3(0, 0, 1)
        const localPivotPlane = new CANNON.Vec3(x, z, 0)
        const constraint = new CANNON.PointToPointConstraint(
            sphereBody,
            localPivotSphere,
            planeBody,
            localPivotPlane
        )
        world.addConstraint(constraint)

        i++
    }
}

let draggingId = -1
const dragControls = new DragControls(sphereMeshes, camera, renderer.domElement)
dragControls.addEventListener('dragstart', function (event: THREE.Event) {
    draggingId = event.object.userData.i
    console.log(draggingId)
    event.object.material.opacity = 0.33
    controls.enabled = false
})
dragControls.addEventListener('dragend', function (event: THREE.Event) {
    draggingId = -1
    event.object.material.opacity = 1
    controls.enabled = true
})
dragControls.addEventListener('drag', function (event) {
    event.object.position.y = 1
})

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()
const physicsFolder = gui.addFolder('Physics')
physicsFolder.add(world.gravity, 'x', -10.0, 10.0, 0.1)
physicsFolder.add(world.gravity, 'y', -10.0, 10.0, 0.1)
physicsFolder.add(world.gravity, 'z', -10.0, 10.0, 0.1)
physicsFolder.open()

const clock = new THREE.Clock()
let delta

const cannonDebugRenderer = new CannonDebugRenderer(scene, world)

function animate() {
    requestAnimationFrame(animate)

    controls.update()

    delta = Math.min(clock.getDelta(), 0.1)
    world.step(delta)

    cannonDebugRenderer.update()

    // Copy coordinates from Cannon to Three.js

    sphereMeshes.forEach((m, i) => {
        if (i === draggingId) {
            sphereBodies[i].position.x = m.position.x
            sphereBodies[i].position.y = m.position.y
            sphereBodies[i].position.z = m.position.z
            sphereBodies[i].quaternion.x = m.quaternion.x
            sphereBodies[i].quaternion.y = m.quaternion.y
            sphereBodies[i].quaternion.z = m.quaternion.z
            sphereBodies[i].quaternion.w = m.quaternion.w
            sphereBodies[i].velocity.set(0, 0, 0)
            sphereBodies[i].angularVelocity.set(0, 0, 0)
        } else {
            m.position.set(
                sphereBodies[i].position.x,
                sphereBodies[i].position.y,
                sphereBodies[i].position.z
            )
            m.quaternion.set(
                sphereBodies[i].quaternion.x,
                sphereBodies[i].quaternion.y,
                sphereBodies[i].quaternion.z,
                sphereBodies[i].quaternion.w
            )
        }
    })

    render()

    stats.update()
}

function render() {
    renderer.render(scene, camera)
}

animate()