top of page
Search
cedarcantab

Soft Bunny


This is me trying to understand the code from this video


// ------------------------------------------------------------------

class SoftBody {

constructor(tetMesh, scene, edgeCompliance = 100.0, volCompliance = 0.0)

{

// physics

this.numParticles = tetMesh.verts.length / 3;

this.numTets = tetMesh.tetIds.length / 4;

this.pos = new Float32Array(tetMesh.verts);

this.prevPos = tetMesh.verts.slice();

this.vel = new Float32Array(3 * this.numParticles);

this.tetIds = tetMesh.tetIds;

this.edgeIds = tetMesh.tetEdgeIds;

this.restVol = new Float32Array(this.numTets);

this.edgeLengths = new Float32Array(this.edgeIds.length / 2);

this.invMass = new Float32Array(this.numParticles);

this.edgeCompliance = edgeCompliance;

this.volCompliance = volCompliance;

this.temp = new Float32Array(4 * 3);

this.grads = new Float32Array(4 * 3);

this.grabId = -1;

this.grabInvMass = 0.0;

this.initPhysics();

// surface tri mesh

var geometry = new THREE.BufferGeometry();

geometry.setAttribute('position', new THREE.BufferAttribute(this.pos, 3));

geometry.setIndex(tetMesh.tetSurfaceTriIds);

var material = new THREE.MeshPhongMaterial({color: 0xF02000});

material.flatShading = true;

this.surfaceMesh = new THREE.Mesh(geometry, material);

this.surfaceMesh.geometry.computeVertexNormals();

this.surfaceMesh.userData = this;

this.surfaceMesh.layers.enable(1);

scene.add(this.surfaceMesh);

this.volIdOrder = [[1,3,2], [0,2,3], [0,3,1], [0,1,2]];

// console.log(JSON.stringify(tetMesh.verts));

}

translate(x, y, z)

{

for (var i = 0; i < this.numParticles; i++) {

vecAdd(this.pos,i, [x,y,z],0);

vecAdd(this.prevPos,i, [x,y,z],0);

}

}

updateMeshes()

{

this.surfaceMesh.geometry.computeVertexNormals();

this.surfaceMesh.geometry.attributes.position.needsUpdate = true;

this.surfaceMesh.geometry.computeBoundingSphere();

}

getTetVolume(nr)

{

var id0 = this.tetIds[4 * nr];

var id1 = this.tetIds[4 * nr + 1];

var id2 = this.tetIds[4 * nr + 2];

var id3 = this.tetIds[4 * nr + 3];

vecSetDiff(this.temp,0, this.pos,id1, this.pos,id0);

vecSetDiff(this.temp,1, this.pos,id2, this.pos,id0);

vecSetDiff(this.temp,2, this.pos,id3, this.pos,id0);

vecSetCross(this.temp,3, this.temp,0, this.temp,1);

return vecDot(this.temp,3, this.temp,2) / 6.0;

}

initPhysics()

{

this.invMass.fill(0.0);

this.restVol.fill(0.0);

for (var i = 0; i < this.numTets; i++) {

var vol =this.getTetVolume(i);

this.restVol[i] = vol;

var pInvMass = vol > 0.0 ? 1.0 / (vol / 4.0) : 0.0;

this.invMass[this.tetIds[4 * i]] += pInvMass;

this.invMass[this.tetIds[4 * i + 1]] += pInvMass;

this.invMass[this.tetIds[4 * i + 2]] += pInvMass;

this.invMass[this.tetIds[4 * i + 3]] += pInvMass;

}

for (var i = 0; i < this.edgeLengths.length; i++) {

var id0 = this.edgeIds[2 * i];

var id1 = this.edgeIds[2 * i + 1];

this.edgeLengths[i] = Math.sqrt(vecDistSquared(this.pos,id0, this.pos,id1));

}

}

preSolve(dt, gravity)

{

for (var i = 0; i < this.numParticles; i++) {

if (this.invMass[i] == 0.0)

continue;

vecAdd(this.vel,i, gravity,0, dt);

vecCopy(this.prevPos,i, this.pos,i);

vecAdd(this.pos,i, this.vel,i, dt);

var y = this.pos[3 * i + 1];

if (y < 0.0) {

vecCopy(this.pos,i, this.prevPos,i);

this.pos[3 * i + 1] = 0.0;

}

}

}

solve(dt)

{

this.solveEdges(this.edgeCompliance, dt);

this.solveVolumes(this.volCompliance, dt);

}

postSolve(dt)

{

for (var i = 0; i < this.numParticles; i++) {

if (this.invMass[i] == 0.0)

continue;

vecSetDiff(this.vel,i, this.pos,i, this.prevPos,i, 1.0 / dt);

}

this.updateMeshes();

}

solveEdges(compliance, dt) {

var alpha = compliance / dt /dt;

for (var i = 0; i < this.edgeLengths.length; i++) {

var id0 = this.edgeIds[2 * i];

var id1 = this.edgeIds[2 * i + 1];

var w0 = this.invMass[id0];

var w1 = this.invMass[id1];

var w = w0 + w1;

if (w == 0.0)

continue;

vecSetDiff(this.grads,0, this.pos,id0, this.pos,id1);

var len = Math.sqrt(vecLengthSquared(this.grads,0));

if (len == 0.0)

continue;

vecScale(this.grads,0, 1.0 / len);

var restLen = this.edgeLengths[i];

var C = len - restLen;

var s = -C / (w + alpha);

vecAdd(this.pos,id0, this.grads,0, s * w0);

vecAdd(this.pos,id1, this.grads,0, -s * w1);

}

}

solveVolumes(compliance, dt) {

var alpha = compliance / dt /dt;

for (var i = 0; i < this.numTets; i++) {

var w = 0.0;

for (var j = 0; j < 4; j++) {

var id0 = this.tetIds[4 * i + this.volIdOrder[j][0]];

var id1 = this.tetIds[4 * i + this.volIdOrder[j][1]];

var id2 = this.tetIds[4 * i + this.volIdOrder[j][2]];

vecSetDiff(this.temp,0, this.pos,id1, this.pos,id0);

vecSetDiff(this.temp,1, this.pos,id2, this.pos,id0);

vecSetCross(this.grads,j, this.temp,0, this.temp,1);

vecScale(this.grads,j, 1.0/6.0);

w += this.invMass[this.tetIds[4 i + j]] vecLengthSquared(this.grads,j);

}

if (w == 0.0)

continue;

var vol = this.getTetVolume(i);

var restVol = this.restVol[i];

var C = vol - restVol;

var s = -C / (w + alpha);

for (var j = 0; j < 4; j++) {

var id = this.tetIds[4 * i + j];

vecAdd(this.pos,id, this.grads,j, s * this.invMass[id])

}

}

}

squash() {

for (var i = 0; i < this.numParticles; i++) {

this.pos[3 * i + 1] = 0.5;

}

this.updateMeshes();

}

startGrab(pos)

{

var p = [pos.x, pos.y, pos.z];

var minD2 = Number.MAX_VALUE;

this.grabId = -1;

for (let i = 0; i < this.numParticles; i++) {

var d2 = vecDistSquared(p,0, this.pos,i);

if (d2 < minD2) {

minD2 = d2;

this.grabId = i;

}

}

if (this.grabId >= 0) {

this.grabInvMass = this.invMass[this.grabId];

this.invMass[this.grabId] = 0.0;

vecCopy(this.pos,this.grabId, p,0);

}

}

moveGrabbed(pos, vel)

{

if (this.grabId >= 0) {

var p = [pos.x, pos.y, pos.z];

vecCopy(this.pos,this.grabId, p,0);

}

}

endGrab(pos, vel)

{

if (this.grabId >= 0) {

this.invMass[this.grabId] = this.grabInvMass;

var v = [vel.x, vel.y, vel.z];

vecCopy(this.vel,this.grabId, v,0);

}

this.grabId = -1;

}

}




// ------------------------------------------------------------------

function simulate()

{

if (gPhysicsScene.paused)

return;

var sdt = gPhysicsScene.dt / gPhysicsScene.numSubsteps;

for (var step = 0; step < gPhysicsScene.numSubsteps; step++) {

for (var i = 0; i < gPhysicsScene.objects.length; i++)

gPhysicsScene.objects[i].preSolve(sdt, gPhysicsScene.gravity);

for (var i = 0; i < gPhysicsScene.objects.length; i++)

gPhysicsScene.objects[i].solve(sdt);

for (var i = 0; i < gPhysicsScene.objects.length; i++)

gPhysicsScene.objects[i].postSolve(sdt);

}

gGrabber.increaseTime(gPhysicsScene.dt);

}




function initThreeScene()

{

gThreeScene = new THREE.Scene();

// Lights

gThreeScene.add( new THREE.AmbientLight( 0x505050 ) );

gThreeScene.fog = new THREE.Fog( 0x000000, 0, 15 );

var spotLight = new THREE.SpotLight( 0xffffff );

spotLight.angle = Math.PI / 5;

spotLight.penumbra = 0.2;

spotLight.position.set( 2, 3, 3 );

spotLight.castShadow = true;

spotLight.shadow.mapSize.width = 1024;

spotLight.shadow.mapSize.height = 1024;

gThreeScene.add( spotLight );

var dirLight = new THREE.DirectionalLight( 0x55505a, 1 );

dirLight.position.set( 0, 3, 0 );

dirLight.castShadow = true;

dirLight.shadow.camera.bottom = - 1;

dirLight.shadow.mapSize.width = 1024;

dirLight.shadow.mapSize.height = 1024;

gThreeScene.add( dirLight );

// Geometry

var ground = new THREE.Mesh(

new THREE.PlaneBufferGeometry( 20, 20, 1, 1 ),

new THREE.MeshPhongMaterial( { color: 0xa0adaf, shininess: 150 } )

);

ground.rotation.x = - Math.PI / 2; // rotates X/Y to X/Z

ground.receiveShadow = true;

gThreeScene.add( ground );

var helper = new THREE.GridHelper( 20, 20 );

helper.material.opacity = 1.0;

helper.material.transparent = true;

helper.position.set(0, 0.002, 0);

gThreeScene.add( helper );

// Renderer

gRenderer = new THREE.WebGLRenderer();

gRenderer.shadowMap.enabled = true;

gRenderer.setPixelRatio( window.devicePixelRatio );

gRenderer.setSize( 0.8 window.innerWidth, 0.8 window.innerHeight );

window.addEventListener( 'resize', onWindowResize, false );

container.appendChild( gRenderer.domElement );

// Camera

gCamera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 0.01, 100);

gCamera.position.set(0, 1, 2);

gCamera.updateMatrixWorld();

gThreeScene.add(gCamera);

gCameraControl = new THREE.OrbitControls(gCamera, gRenderer.domElement);

gCameraControl.zoomSpeed = 2.0;

gCameraControl.panSpeed = 0.4;

// grabber

gGrabber = new Grabber();

container.addEventListener( 'pointerdown', onPointer, false );

container.addEventListener( 'pointermove', onPointer, false );

container.addEventListener( 'pointerup', onPointer, false );

}









Useful References



3 views0 comments

Recent Posts

See All

p2 naive broadphase

var Broadphase = require('../collision/Broadphase'); module.exports = NaiveBroadphase; /** * Naive broadphase implementation. Does N^2...

sopiro motor constranit

import { Matrix2, Vector2 } from "./math.js"; import { RigidBody } from "./rigidbody.js"; import { Settings } from "./settings.js";...

Comments


記事: Blog2_Post
bottom of page