Skip to content

Commit

Permalink
sample computer soft body simulation
Browse files Browse the repository at this point in the history
add sample computer soft body simulation
  • Loading branch information
seven1031 committed Nov 15, 2023
1 parent 5baeaf4 commit 9ce9f5a
Show file tree
Hide file tree
Showing 15 changed files with 1,880 additions and 0 deletions.
95 changes: 95 additions & 0 deletions samples/computer/Sample_Softbody.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import { GUIHelp } from '@orillusion/debug/GUIHelp';
import { AtmosphericComponent, BoxGeometry, CameraUtil, DirectLight, Engine3D, ForwardRenderJob, HoverCameraController, LitMaterial, MeshRenderer, Object3D, Scene3D, View3D, webGPUContext } from '@orillusion/core';
import { BunnySimulator } from "./softbody/BunnySimulator";

export class Demo_Softbody {
constructor() {}

async run() {

Engine3D.setting.shadow.autoUpdate = true;
Engine3D.setting.shadow.updateFrameRate = 1;
Engine3D.setting.shadow.shadowBound = 8;
//Engine3D.setting.shadow.shadowBias = 0.000001;

await Engine3D.init({});

GUIHelp.init();

let scene = new Scene3D();
let sky = scene.addComponent(AtmosphericComponent);
await this.initScene(scene);

let camera = CameraUtil.createCamera3DObject(scene);

camera.perspective(60, webGPUContext.aspect, 1 , 5000.0);
let ctl = camera.object3D.addComponent(HoverCameraController);
ctl.setCamera(30, -28, 15);

let view = new View3D();
view.scene = scene;
view.camera = camera;

Engine3D.startRenderView(view);
}

async initScene(scene: Scene3D) {
let mat = new LitMaterial();
mat.baseMap = Engine3D.res.grayTexture;
mat.roughness = 0.8;
mat.metallic = 0.1;

let box = new Object3D();
box.transform.y = 0.0;
box.transform.x = 0.0;
box.transform.z = 0.0;
let mr = box.addComponent(MeshRenderer);
mr.geometry = new BoxGeometry(3.0, 3.0, 3.0);
let boxMat = new LitMaterial();
boxMat.roughness = 0.8;
boxMat.metallic = 0.1
boxMat.cullMode = `front`
//boxMat.depthCompare = `greater`

mr.material = boxMat;
// mr.material.doubleSide = true;
mr.castShadow = true;
scene.addChild(box);

let bunny = new Object3D();
let simulator = bunny.addComponent(BunnySimulator);
simulator.castShadow = true;
simulator.SetInteractionBox(box);
scene.addChild(bunny);

// {
// let mat = new HDRLitMaterial();
// mat.baseMap = defaultTexture.createTexture(32, 32, 72, 126, 2, 255);
// mat.roughness = 0.8;
// let plane = new Object3D();
// plane.transform.y = -1;
// let planeMesh = plane.addComponent(MeshRenderer);
// planeMesh.geometry = new PlaneGeometry(100, 100);
// planeMesh.material = mat;
// planeMesh.receiveShadow = true;
// scene.addChild(plane);
// }

{
var lightObj = new Object3D();
lightObj.x = 0;
lightObj.y = 0;
lightObj.z = 0;
lightObj.rotationX = 45;
lightObj.rotationY = 0;
lightObj.rotationZ = 0;
let lc = lightObj.addComponent(DirectLight);
lc.castShadow = true;
lc.intensity = 20;
scene.addChild(lightObj);
}

}

// async initComputeBuffer() {}
}
172 changes: 172 additions & 0 deletions samples/computer/softbody/BunnyGeometry.ts

Large diffs are not rendered by default.

122 changes: 122 additions & 0 deletions samples/computer/softbody/BunnySimulator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import { Engine3D, LitMaterial, KeyCode, KeyEvent, MeshRenderer, Object3D, Time, Vector3, VertexAttributeName, View3D } from '@orillusion/core';
import { BunnySimulatorConfig } from "./BunnySimulatorConfig";
import { BunnySimulatorPipeline } from "./BunnySimulatorPipeline";
import bunnyMesh from "./bunnyMesh"
import { BunnyGeometry } from "./BunnyGeometry";

export class BunnySimulator extends MeshRenderer {
protected mConfig: BunnySimulatorConfig;
protected mBunnyGeometry: BunnyGeometry;
protected mInteractionBox: Object3D;
protected mBunnyComputePipeline: BunnySimulatorPipeline;
protected mKeyState: boolean[] = [false, false, false, false];

constructor() {
super();
this.mConfig = {
NUMPARTICLES: 0,
NUMTETS: 0,
NUMTEDGES: 0,
NUMTSURFACES: 0,
GRAVITY: -10,
DELTATIME: 1 / 60,
NUMSUBSTEPS: 10,
EDGECOMPLIANCE: 0.0,
VOLCOMPLIANCE: 0.0,
CUBECENTREX: 0.0,
CUBECENTREY: 0.0,
CUBECENTREZ: 0.0,
CUBEWIDTH: 3.0,
CUBEHEIGHT: 3.0,
CUBEDEPTH: 3.0,
bunnyVertex: null,
bunnyTetIds: null,
bunnyEdgeIds: null,
bunnySurfaceTriIds: null,
bunnyVertexBuffer: null,
};
this.mConfig.bunnyVertex = new Float32Array(bunnyMesh.verts);
this.mConfig.bunnyTetIds = new Uint16Array(bunnyMesh.tetIds);
this.mConfig.bunnyEdgeIds = new Uint16Array(bunnyMesh.tetEdgeIds);
this.mConfig.bunnySurfaceTriIds = new Uint16Array(bunnyMesh.tetSurfaceTriIds);
this.mConfig.NUMPARTICLES = this.mConfig.bunnyVertex.length / 3;
this.mConfig.NUMTETS = this.mConfig.bunnyTetIds.length / 4;
this.mConfig.NUMTEDGES = this.mConfig.bunnyEdgeIds.length / 2;
this.mConfig.NUMTSURFACES = this.mConfig.bunnySurfaceTriIds.length / 3;
this.mBunnyGeometry = new BunnyGeometry(0.0, 0.0, 0.0, this.mConfig.NUMPARTICLES);
// this.mConfig.bunnyVertex = this.mBunnyGeometry.getAttribute(VertexAttributeName.position).data;
// console.log(this.mConfig.NUMPARTICLES,this.mConfig.NUMTETS,this.mConfig.NUMTEDGES,this.mConfig.NUMTSURFACES)
}

protected updateKeyState(keyCode: number, state: boolean) {
switch (keyCode) {
case KeyCode.Key_W:
this.mKeyState[0] = state;
break;
case KeyCode.Key_S:
this.mKeyState[1] = state;
break;
case KeyCode.Key_A:
this.mKeyState[2] = state;
break;
case KeyCode.Key_D:
this.mKeyState[3] = state;
break;
}
}

public init(){
super.init();
this.alwaysRender = true;
this.geometry = this.mBunnyGeometry;
var mat = new LitMaterial();
mat.roughness = 0.8;
mat.baseMap = Engine3D.res.redTexture;
this.material = mat;
this.material.doubleSide = true;
}

public start() {
Engine3D.inputSystem.addEventListener(KeyEvent.KEY_DOWN, (e: KeyEvent) => this.updateKeyState(e.keyCode, true), this);
Engine3D.inputSystem.addEventListener(KeyEvent.KEY_UP, (e: KeyEvent) => this.updateKeyState(e.keyCode, false), this);
}

public SetInteractionBox(box: Object3D) {
this.mInteractionBox = box;
}

private _tickTime = 0;

public onCompute(view: View3D, command?: GPUCommandEncoder) {
if (!this.mBunnyComputePipeline) {
this.mConfig.bunnyVertexBuffer = this.mBunnyGeometry.vertexBuffer.vertexGPUBuffer;
this.mBunnyComputePipeline = new BunnySimulatorPipeline(this.mConfig);
}

var pos = new Vector3();
if (this.mInteractionBox) {
var transform = this.mInteractionBox.transform;
let dt = Time.delta / 1000.0;
let speed = 5 * dt;
// W S
if (this.mKeyState[0]) {
transform.y += speed
} else if (this.mKeyState[1]) {
transform.y -= speed
}
// A D
if (this.mKeyState[2]) {
transform.x -= speed
} else if (this.mKeyState[3]) {
transform.x += speed
}
pos.copyFrom(this.mInteractionBox.transform.worldPosition);
}

this._tickTime += Time.delta / 1000.0;
if (this._tickTime >= this.mConfig.DELTATIME) {
this._tickTime -= this.mConfig.DELTATIME;
this.mBunnyComputePipeline.compute(command, pos);
}
}
}
172 changes: 172 additions & 0 deletions samples/computer/softbody/BunnySimulatorBuffer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
import { ComputeGPUBuffer, Vector3, webGPUContext } from '@orillusion/core';
import { BunnySimulatorConfig } from "./BunnySimulatorConfig";

export class BunnySimulatorBuffer {
protected mPositionBuffer: ComputeGPUBuffer;
protected mNormalBuffer: ComputeGPUBuffer;
protected mVertexPositionData: Float32Array;
protected mVertexPositionBuffer: ComputeGPUBuffer;
protected mNewPositionBuffer: ComputeGPUBuffer;
protected mAtomicPositionBuffer: ComputeGPUBuffer;
protected mAtomicNormalBuffer: ComputeGPUBuffer;
protected mVelocityBuffer: ComputeGPUBuffer;
protected mEdgeInfosBuffer: ComputeGPUBuffer;
protected mTetIdsBuffer: ComputeGPUBuffer;
protected mRestVolBuffer: ComputeGPUBuffer;
protected mSurfaceInfosBuffer: ComputeGPUBuffer;
// protected mInputData: Float32Array;
protected mInputBuffer: ComputeGPUBuffer;
protected mOutput0Buffer: ComputeGPUBuffer;

constructor(config: BunnySimulatorConfig) {
this.initGPUBuffer(config);
}

protected initGPUBuffer(config: BunnySimulatorConfig) {
const { NUMPARTICLES, bunnyVertex } = config;

let vertex = new Float32Array(NUMPARTICLES * 4)
for (let i = 0; i < NUMPARTICLES; i++) {
vertex[i * 4] = bunnyVertex[i * 3]
vertex[i * 4 + 1] = bunnyVertex[i * 3 + 1]
vertex[i * 4 + 2] = bunnyVertex[i * 3 + 2]
vertex[i * 4 + 3] = 1
}
this.mVertexPositionData = vertex;
this.mVertexPositionBuffer = new ComputeGPUBuffer(this.mVertexPositionData.length);
this.mVertexPositionBuffer.setFloat32Array("", this.mVertexPositionData);
this.mVertexPositionBuffer.apply();

this.mNewPositionBuffer = new ComputeGPUBuffer(this.mVertexPositionData.length);
this.mNewPositionBuffer.setFloat32Array("", this.mVertexPositionData);
this.mNewPositionBuffer.apply();

this.mAtomicPositionBuffer = new ComputeGPUBuffer(this.mVertexPositionData.length);

this.mAtomicNormalBuffer = new ComputeGPUBuffer(NUMPARTICLES * 4);
this.mNormalBuffer = new ComputeGPUBuffer(NUMPARTICLES * 4);

const { NUMTETS, bunnyTetIds } = config;

const invMass = new Float32Array(NUMPARTICLES)
const restVol = new Float32Array(NUMTETS)
const tetIds = new Int32Array(NUMTETS * 4)
for (let i = 0; i < NUMTETS; i++) {
var vol = this.getTetVolume(bunnyVertex, bunnyTetIds, i);
restVol[i] = vol;
var pInvMass = vol > 0.0 ? 1.0 / (vol / 4.0) : 0.0;
invMass[bunnyTetIds[4 * i]] += pInvMass;
invMass[bunnyTetIds[4 * i + 1]] += pInvMass;
invMass[bunnyTetIds[4 * i + 2]] += pInvMass;
invMass[bunnyTetIds[4 * i + 3]] += pInvMass;
tetIds[4 * i] = bunnyTetIds[4 * i];
tetIds[4 * i + 1] = bunnyTetIds[4 * i + 1];
tetIds[4 * i + 2] = bunnyTetIds[4 * i + 2];
tetIds[4 * i + 3] = bunnyTetIds[4 * i + 3];
}
this.mTetIdsBuffer = new ComputeGPUBuffer(tetIds.length);
webGPUContext.device.queue.writeBuffer(this.mTetIdsBuffer.buffer, 0, tetIds);
// this.mTetIdsBuffer.setInt32Array("", tetIds);
// this.mTetIdsBuffer.apply();

this.mRestVolBuffer = new ComputeGPUBuffer(restVol.length);
this.mRestVolBuffer.setFloat32Array("", restVol);
this.mRestVolBuffer.apply();

const velocity = new Float32Array(4 * NUMPARTICLES)
for (let i = 0; i < NUMPARTICLES; ++i) {
velocity[i * 4 + 3] = invMass[i]
}
this.mVelocityBuffer = new ComputeGPUBuffer(velocity.length);
this.mVelocityBuffer.setFloat32Array("", velocity);
this.mVelocityBuffer.apply();

const { NUMTEDGES, bunnyEdgeIds } = config;

const edgeInfos = new Float32Array(NUMTEDGES * 4)
for (var i = 0; i < NUMTEDGES; i++) {
edgeInfos[i * 4 + 0] = bunnyEdgeIds[2 * i];
edgeInfos[i * 4 + 1] = bunnyEdgeIds[2 * i + 1];
edgeInfos[i * 4 + 2] = Math.sqrt(this.distance(bunnyVertex, edgeInfos[i * 4], bunnyVertex, edgeInfos[i * 4 + 1]));
}
this.mEdgeInfosBuffer = new ComputeGPUBuffer(edgeInfos.length);
this.mEdgeInfosBuffer.setFloat32Array("", edgeInfos);
this.mEdgeInfosBuffer.apply();

const {NUMTSURFACES, bunnySurfaceTriIds} = config;
const surfaceInfos = new Float32Array(NUMTSURFACES * 4)
for (let i = 0; i < NUMTSURFACES; i++) {
surfaceInfos[i * 4] = bunnySurfaceTriIds[i * 3]
surfaceInfos[i * 4 + 1] = bunnySurfaceTriIds[i * 3 + 1]
surfaceInfos[i * 4 + 2] = bunnySurfaceTriIds[i * 3 + 2]
surfaceInfos[i * 4 + 3] = 1
}
this.mSurfaceInfosBuffer = new ComputeGPUBuffer(surfaceInfos.length);
this.mSurfaceInfosBuffer.setFloat32Array("", surfaceInfos);
this.mSurfaceInfosBuffer.apply();

const { GRAVITY, DELTATIME, NUMSUBSTEPS, EDGECOMPLIANCE, VOLCOMPLIANCE, CUBECENTREX, CUBECENTREY, CUBECENTREZ, CUBEWIDTH, CUBEHEIGHT, CUBEDEPTH } = config;
this.mInputBuffer = new ComputeGPUBuffer(16);
this.mInputBuffer.setFloat("NUMPARTICLES", NUMPARTICLES);
this.mInputBuffer.setFloat("NUMTETS", NUMTETS);
this.mInputBuffer.setFloat("NUMTEDGES", NUMTEDGES);
this.mInputBuffer.setFloat("NUMTSURFACES", NUMTSURFACES);
this.mInputBuffer.setFloat("GRAVITY", GRAVITY);
this.mInputBuffer.setFloat("DELTATIME", DELTATIME / NUMSUBSTEPS);
this.mInputBuffer.setFloat("EDGECOMPLIANCE", EDGECOMPLIANCE);
this.mInputBuffer.setFloat("VOLCOMPLIANCE", VOLCOMPLIANCE);
this.mInputBuffer.setFloat("CUBECENTREX", CUBECENTREX);
this.mInputBuffer.setFloat("CUBECENTREY", CUBECENTREY);
this.mInputBuffer.setFloat("CUBECENTREZ", CUBECENTREZ);
this.mInputBuffer.setFloat("CUBEWIDTH", CUBEWIDTH);
this.mInputBuffer.setFloat("CUBEHEIGHT", CUBEHEIGHT);
this.mInputBuffer.setFloat("CUBEDEPTH", CUBEDEPTH);
this.mInputBuffer.apply();

this.mOutput0Buffer = new ComputeGPUBuffer(NUMTETS * 4);
// this.mOutput0Buffer.debug();
}

public updateInputData(pos: Vector3) {
this.mInputBuffer.setFloat("CUBECENTREX", pos.x);
this.mInputBuffer.setFloat("CUBECENTREY", pos.y);
this.mInputBuffer.setFloat("CUBECENTREZ", pos.z);
this.mInputBuffer.apply();
}

protected cross(a: number[], indexa: number, b: number[], indexb: number, c: number[], indexc: number) {
a[indexa * 3] = b[indexb * 3 + 1] * c[indexc * 3 + 2] - b[indexb * 3 + 2] * c[indexc * 3 + 1];
a[indexa * 3 + 1] = b[indexb * 3 + 2] * c[indexc * 3 + 0] - b[indexb * 3 + 0] * c[indexc * 3 + 2];
a[indexa * 3 + 2] = b[indexb * 3 + 0] * c[indexc * 3 + 1] - b[indexb * 3 + 1] * c[indexc * 3 + 0];
}

protected dot(a: number[], indexa: number, b: number[], indexb: number) {
return a[indexa * 3] * b[indexb * 3] + a[indexa * 3 + 1] * b[indexb * 3 + 1] + a[indexa * 3 + 2] * b[indexb * 3 + 2];
}

protected getTetVolume(position: Float32Array | Uint16Array | Uint32Array, tetIds: Float32Array | Uint16Array | Uint32Array, i: number) {
let id0 = tetIds[4 * i];
let id1 = tetIds[4 * i + 1];
let id2 = tetIds[4 * i + 2];
let id3 = tetIds[4 * i + 3];

let temp = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];
temp[0] = position[id1 * 3] - position[id0 * 3];
temp[1] = position[id1 * 3 + 1] - position[id0 * 3 + 1];
temp[2] = position[id1 * 3 + 2] - position[id0 * 3 + 2];
temp[3] = position[id2 * 3] - position[id0 * 3];
temp[4] = position[id2 * 3 + 1] - position[id0 * 3 + 1];
temp[5] = position[id2 * 3 + 2] - position[id0 * 3 + 2];
temp[6] = position[id3 * 3] - position[id0 * 3];
temp[7] = position[id3 * 3 + 1] - position[id0 * 3 + 1];
temp[8] = position[id3 * 3 + 2] - position[id0 * 3 + 2];

this.cross(temp, 3, temp, 0, temp, 1);
return this.dot(temp, 3, temp, 2) / 6.0;
}

protected distance(a: Float32Array | Uint16Array | Uint32Array, indexa: number, b: Float32Array | Uint16Array | Uint32Array, indexb: number) {
let a0 = a[indexa * 3] - b[indexb * 3], a1 = a[indexa * 3 + 1] - b[indexb * 3 + 1], a2 = a[indexa * 3 + 2] - b[indexb * 3 + 2];
return a0 * a0 + a1 * a1 + a2 * a2;
}
}
Loading

0 comments on commit 9ce9f5a

Please sign in to comment.