Skip to content

Instantly share code, notes, and snippets.

@sketchpunk
Created November 17, 2018 06:07
Show Gist options
  • Save sketchpunk/3568150a04b973430dfe8fd29bf470c8 to your computer and use it in GitHub Desktop.
Save sketchpunk/3568150a04b973430dfe8fd29bf470c8 to your computer and use it in GitHub Desktop.
Spring Physics - Oscillation and Critical Dampening on Quaternions
// Resources
// https://burakkanber.com/blog/physics-in-javascript-car-suspension-part-1-spring-mass-damper/
// https://gafferongames.com/post/spring_physics/
// https://gafferongames.com/post/physics_in_3d/
// http://digitalopus.ca/site/pd-controllers/
// .. Has things about Torque
class QuaterionSpring{
constructor( damping=5, stiffness=30 ){
this.velocity = new Float32Array(4);
this.stiffness = stiffness;
this.damping = damping;;
}
_velLenSqr(){
return this.velocity[0] ** 2 + this.velocity[1] ** 2 +
this.velocity[2] ** 2 + this.velocity[3] ** 2;
}
// Harmonic oscillation
// https://stackoverflow.com/questions/44688112/spring-physics-applied-to-quaternions-using-python
oscillationStep(cq, target, dt){
// Check when the spring is done.
let dot = Quat.dot(cq, target);
if( dot >= 0.9999 && this._velLenSqr() < 0.000001 ){
cq.copy( target );
return;
}
//.........................................
let tq = new Quat();
if( dot < 0 ){ // Use the closest rotation
tq[0] = -target[0];
tq[1] = -target[1];
tq[2] = -target[2];
tq[3] = -target[3];
}else tq.copy( target );
//.........................................
// displacement = current - target;
// spring_force = (stiffness * displacement - damper * velocity ) / mass
// velocity += spring_force * deltaTime; // Acceleration
// current += velocity * deltaTime
//tried with MASS, took it out cause no need for it at the moment.
//this.velocity[0] += ((k * ( cq[0] - tq[0] ) - d * this.velocity[0]) / m) * dt;
//this.velocity[1] += ((k * ( cq[1] - tq[1] ) - d * this.velocity[1]) / m) * dt;
//this.velocity[2] += ((k * ( cq[2] - tq[2] ) - d * this.velocity[2]) / m) * dt;
//this.velocity[3] += ((k * ( cq[3] - tq[3] ) - d * this.velocity[3]) / m) * dt;
this.velocity[0] += (-this.stiffness * ( cq[0] - tq[0] ) - this.damping * this.velocity[0]) * dt;
this.velocity[1] += (-this.stiffness * ( cq[1] - tq[1] ) - this.damping * this.velocity[1]) * dt;
this.velocity[2] += (-this.stiffness * ( cq[2] - tq[2] ) - this.damping * this.velocity[2]) * dt;
this.velocity[3] += (-this.stiffness * ( cq[3] - tq[3] ) - this.damping * this.velocity[3]) * dt;
//.........................................
cq[0] += this.velocity[0] * dt;
cq[1] += this.velocity[1] * dt;
cq[2] += this.velocity[2] * dt;
cq[3] += this.velocity[3] * dt;
//console.log(cq);
cq.normalize();
}
// Critically Damped Spring
criticallyStep(cq, target, dt){
// Check when the spring is done.
let dot = Quat.dot(cq, target);
if( dot >= 0.9999 && this._velLenSqr() < 0.000001 ){
cq.copy( target );
return;
}
//.........................................
let tq = new Quat();
if( dot < 0 ){ // Use the closest rotation
tq[0] = -target[0];
tq[1] = -target[1];
tq[2] = -target[2];
tq[3] = -target[3];
}else tq.copy( target );
//.........................................
// n1 = velocity - ( currentRot - targerRot ) * ( omega * omega * dt );
// n2 = 1 + omega * dt;
// newVelocity = n1 / ( n2 * n2 );
// currentRot += newVelocity * dt;
let dSqrDt = this.damping * this.damping * dt,
n2 = 1 + this.damping * dt,
n2Sqr = n2 * n2;
this.velocity[0] = ( this.velocity[0] - ( cq[0] - tq[0] ) * dSqrDt ) / n2Sqr;
this.velocity[1] = ( this.velocity[1] - ( cq[1] - tq[1] ) * dSqrDt ) / n2Sqr;
this.velocity[2] = ( this.velocity[2] - ( cq[2] - tq[2] ) * dSqrDt ) / n2Sqr;
this.velocity[3] = ( this.velocity[3] - ( cq[3] - tq[3] ) * dSqrDt ) / n2Sqr;
//.........................................
cq[0] += this.velocity[0] * dt;
cq[1] += this.velocity[1] * dt;
cq[2] += this.velocity[2] * dt;
cq[3] += this.velocity[3] * dt;
cq.normalize();
return cq;
}
}
@alex-shortt
Copy link

you are a legend <3

@kafkiacode
Copy link

Just wanted to say that I used this as a replacement for @react-spring/three in a personal project and it just worked, thanks so much for sharing!

@Cloves-Filho
Copy link

Used this for a script on lens studio, had to make some changes but it worked, thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment