Files
aaf-systems-homepage/app/src/physics.ts

113 lines
4.0 KiB
TypeScript

import * as THREE from 'three'
import * as CANNON from 'cannon-es'
export class PhysicsManager {
static setupPhysicsWorld(): CANNON.World {
const world = new CANNON.World()
// Setup physics world
world.gravity.set(0, 0, 0)
// Use SAPBroadphase for better collision detection with many objects
world.broadphase = new CANNON.SAPBroadphase(world)
// Enable collision detection and response
world.allowSleep = false // Prevent objects from sleeping
// Configure contact material for better collisions
const defaultMaterial = new CANNON.Material('default')
const defaultContactMaterial = new CANNON.ContactMaterial(defaultMaterial, defaultMaterial, {
friction: 0.4,
restitution: 0.3,
contactEquationStiffness: 1e8,
contactEquationRelaxation: 3,
frictionEquationStiffness: 1e8,
frictionEquationRelaxation: 3
})
world.addContactMaterial(defaultContactMaterial)
world.defaultMaterial = defaultMaterial
return world
}
static updatePhysics(
world: CANNON.World,
physicsObjects: Array<{ mesh: THREE.Object3D; body: CANNON.Body }>,
attractionPoint: THREE.Vector3,
mouseWorldPosition: THREE.Vector3,
deltaTime: number
): void {
// Use a smaller, more stable timestep for better collision detection
const fixedTimeStep = 1/60 // 60 Hz - more stable than 120 Hz
const maxSubSteps = 5 // Increased substeps for better collision accuracy
world.step(fixedTimeStep, deltaTime, maxSubSteps)
// Apply forces to objects
physicsObjects.forEach((obj) => {
// Attraction to center point
const attractionForce = new CANNON.Vec3()
attractionForce.x = attractionPoint.x - obj.body.position.x
attractionForce.y = attractionPoint.y - obj.body.position.y
attractionForce.z = attractionPoint.z - obj.body.position.z
const distance = Math.sqrt(
attractionForce.x * attractionForce.x +
attractionForce.y * attractionForce.y +
attractionForce.z * attractionForce.z
)
if (distance > 0) {
const strength = 16.0 / (distance * distance + 1) // Reduced from 8.0 to prevent objects moving too fast
attractionForce.scale(strength, attractionForce)
obj.body.force.set(
obj.body.force.x + attractionForce.x,
obj.body.force.y + attractionForce.y,
obj.body.force.z + attractionForce.z
)
}
// Mouse repulsion
const repulsionForce = new CANNON.Vec3()
repulsionForce.x = obj.body.position.x - mouseWorldPosition.x
repulsionForce.y = obj.body.position.y - mouseWorldPosition.y
repulsionForce.z = obj.body.position.z - mouseWorldPosition.z
const mouseDistance = Math.sqrt(
repulsionForce.x * repulsionForce.x +
repulsionForce.y * repulsionForce.y +
repulsionForce.z * repulsionForce.z
)
if (mouseDistance < 2.5 && mouseDistance > 0) { // Decreased range from 5 to 2.5
const repulsionStrength = 25.0 / (mouseDistance * mouseDistance + 0.1) // Increased strength from 15.0 to 25.0
repulsionForce.scale(repulsionStrength, repulsionForce)
obj.body.force.set(
obj.body.force.x + repulsionForce.x,
obj.body.force.y + repulsionForce.y,
obj.body.force.z + repulsionForce.z
)
// Add rotational torque from mouse interaction
const torque = new CANNON.Vec3(
(Math.random() - 0.5) * repulsionStrength * 0.1,
(Math.random() - 0.5) * repulsionStrength * 0.1,
(Math.random() - 0.5) * repulsionStrength * 0.1
)
obj.body.torque.set(
obj.body.torque.x + torque.x,
obj.body.torque.y + torque.y,
obj.body.torque.z + torque.z
)
}
// Apply damping
obj.body.velocity.scale(0.99, obj.body.velocity)
// Update mesh position to match physics body
obj.mesh.position.copy(obj.body.position as any)
obj.mesh.quaternion.copy(obj.body.quaternion as any)
})
}
}