113 lines
4.0 KiB
TypeScript
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)
|
|
})
|
|
}
|
|
}
|