threepipe
Version:
A modern 3D viewer framework built on top of three.js, written in TypeScript, designed to make creating high-quality, modular, and extensible 3D experiences on the web simple and enjoyable.
106 lines (85 loc) • 3.29 kB
text/typescript
import {Object3DComponent} from './Object3DComponent'
import {Vector3} from 'three'
import {IAnimationLoopEvent} from '../../../core'
import {uiButton} from 'uiconfig.js'
import {timeout} from 'ts-browser-helpers'
/**
* Sample component that simulates a basic rigid body with forces, impulses, and velocity
*/
export class SampleBodyComponent extends Object3DComponent {
static StateProperties = ['running', 'mass', 'damping']
static ComponentType = 'SampleBodyComponent'
running = true
mass = 1
damping = 0.98 // small damping to prevent infinite motion
velocity = new Vector3()
acceleration = new Vector3()
update({deltaTime}: IAnimationLoopEvent) {
if (!this.object || !this.running) return
const dt = (deltaTime ?? 16) / 1000 // ~60fps fallback
// Integrate acceleration -> velocity (acceleration is sum of forces / mass for this frame)
this.velocity.x += this.acceleration.x * dt
this.velocity.y += this.acceleration.y * dt
this.velocity.z += this.acceleration.z * dt
// Apply damping
this.velocity.multiplyScalar(this.damping)
// Reset acceleration so forces must be re-applied next frame
this.acceleration.set(0, 0, 0)
// Check if velocity is effectively non-zero using sum of absolute values
const speedSum = Math.abs(this.velocity.x) + Math.abs(this.velocity.y) + Math.abs(this.velocity.z)
if (speedSum <= 1e-6) return false
// Integrate velocity -> position
this.object.position.x += this.velocity.x * dt
this.object.position.y += this.velocity.y * dt
this.object.position.z += this.velocity.z * dt
return true
}
addForce(force: {x?: number; y?: number; z?: number}) {
// F = m * a => a = F / m
if (force.x !== undefined) this.acceleration.x += force.x / this.mass
if (force.y !== undefined) this.acceleration.y += force.y / this.mass
if (force.z !== undefined) this.acceleration.z += force.z / this.mass
}
applyImpulse(impulse: {x?: number, y?: number, z?: number}) {
// Impulse = Δv * m → Δv = Impulse / m
if (impulse.x !== undefined) this.velocity.x += impulse.x / this.mass
if (impulse.y !== undefined) this.velocity.y += impulse.y / this.mass
if (impulse.z !== undefined) this.velocity.z += impulse.z / this.mass
}
setVelocity(velocity: {x: number; y: number; z: number}) {
this.velocity.copy(velocity)
}
stop() {
this.reset()
}
start() {
this.reset()
}
reset() {
this.velocity.set(0, 0, 0)
this.acceleration.set(0, 0, 0)
}
// Sample functions
MoveLeft = () => {
this.applyImpulse({x: -5})
}
MoveRight = () => {
this.applyImpulse({x: 5})
}
PushForward = async() => {
for (let i = 0; i < 200; i++) {
this.addForce({z: 10})
await timeout(32)
}
}
PushBackward = async() => {
for (let i = 0; i < 200; i++) {
this.addForce({z: -10})
await timeout(32)
}
}
}