Refactored the cloth class for better reading
This commit is contained in:
@@ -7,7 +7,7 @@ import { Component } from '@angular/core';
|
|||||||
import { MatCard, MatCardContent, MatCardHeader, MatCardTitle } from '@angular/material/card';
|
import { MatCard, MatCardContent, MatCardHeader, MatCardTitle } from '@angular/material/card';
|
||||||
import { TranslatePipe } from '@ngx-translate/core';
|
import { TranslatePipe } from '@ngx-translate/core';
|
||||||
import { BabylonCanvas, RenderConfig, SceneEventData } from '../../../shared/components/render-canvas/babylon-canvas.component';
|
import { BabylonCanvas, RenderConfig, SceneEventData } from '../../../shared/components/render-canvas/babylon-canvas.component';
|
||||||
import {ComputeShader, StorageBuffer, MeshBuilder, ShaderMaterial, ShaderLanguage, ArcRotateCamera, GroundMesh} from '@babylonjs/core';
|
import {ComputeShader, StorageBuffer, MeshBuilder, ShaderMaterial, ShaderLanguage, ArcRotateCamera, GroundMesh, WebGPUEngine, Scene} from '@babylonjs/core';
|
||||||
import {
|
import {
|
||||||
CLOTH_FRAGMENT_SHADER_WGSL,
|
CLOTH_FRAGMENT_SHADER_WGSL,
|
||||||
CLOTH_INTEGRATE_COMPUTE_WGSL,
|
CLOTH_INTEGRATE_COMPUTE_WGSL,
|
||||||
@@ -16,6 +16,7 @@ import {
|
|||||||
CLOTH_VERTEX_SHADER_WGSL
|
CLOTH_VERTEX_SHADER_WGSL
|
||||||
} from './cloth.shader';
|
} from './cloth.shader';
|
||||||
import {MatButton} from '@angular/material/button';
|
import {MatButton} from '@angular/material/button';
|
||||||
|
import {ClothBuffers, ClothConfig, ClothData, ClothPipelines} from './cloth.model';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-cloth',
|
selector: 'app-cloth',
|
||||||
@@ -61,45 +62,75 @@ export class ClothComponent {
|
|||||||
* Initializes and starts the cloth simulation.
|
* Initializes and starts the cloth simulation.
|
||||||
*/
|
*/
|
||||||
private createSimulation(): void {
|
private createSimulation(): void {
|
||||||
if (!this.currentSceneData) {
|
if (!this.currentSceneData) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { engine, scene } = this.currentSceneData;
|
const { engine, scene } = this.currentSceneData;
|
||||||
|
|
||||||
// --- 1. CONFIGURE CLOTH GRID ---
|
// 1. Define physics parameters
|
||||||
|
const config = this.getClothConfig();
|
||||||
|
|
||||||
|
// 2. Generate initial CPU data (positions, constraints)
|
||||||
|
const clothData = this.generateClothData(config);
|
||||||
|
|
||||||
|
// 3. Upload to GPU
|
||||||
|
const buffers = this.createStorageBuffers(engine, clothData);
|
||||||
|
|
||||||
|
// 4. Create Compute Shaders
|
||||||
|
const pipelines = this.setupComputePipelines(engine, buffers);
|
||||||
|
|
||||||
|
// 5. Setup Rendering (Mesh, Material, Camera)
|
||||||
|
this.setupRenderMesh(scene, config, buffers.positions);
|
||||||
|
|
||||||
|
// 6. Start the physics loop
|
||||||
|
this.startRenderLoop(engine, scene, config, buffers, pipelines);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================================================================
|
||||||
|
// 1. CONFIGURATION
|
||||||
|
// ========================================================================
|
||||||
|
private getClothConfig(): ClothConfig {
|
||||||
const gridWidth = 100;
|
const gridWidth = 100;
|
||||||
const gridHeight = 100;
|
const gridHeight = 100;
|
||||||
const spacing = 0.05;
|
const spacing = 0.05;
|
||||||
const numVertices = gridWidth * gridHeight;
|
|
||||||
const density = 1.0;
|
const density = 1.0;
|
||||||
const particleArea = spacing * spacing;
|
const particleArea = spacing * spacing;
|
||||||
const particleMass = density * particleArea;
|
const particleMass = density * particleArea;
|
||||||
const particleInvMass = 1.0 / particleMass;
|
|
||||||
|
|
||||||
const positionsData = new Float32Array(numVertices * 4);
|
return {
|
||||||
const prevPositionsData = new Float32Array(numVertices * 4);
|
gridWidth,
|
||||||
const velocitiesData = new Float32Array(numVertices * 4);
|
gridHeight,
|
||||||
|
spacing,
|
||||||
|
density,
|
||||||
|
numVertices: gridWidth * gridHeight,
|
||||||
|
particleInvMass: 1.0 / particleMass
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================================================================
|
||||||
|
// 2. DATA GENERATION (CPU)
|
||||||
|
// ========================================================================
|
||||||
|
private generateClothData(config: ClothConfig): ClothData {
|
||||||
|
const positionsData = new Float32Array(config.numVertices * 4);
|
||||||
|
const prevPositionsData = new Float32Array(config.numVertices * 4);
|
||||||
|
const velocitiesData = new Float32Array(config.numVertices * 4);
|
||||||
|
|
||||||
// Arrays for our 4 phases (dynamic size as we push)
|
|
||||||
const constraintsP0: number[] = [];
|
const constraintsP0: number[] = [];
|
||||||
const constraintsP1: number[] = [];
|
const constraintsP1: number[] = [];
|
||||||
const constraintsP2: number[] = [];
|
const constraintsP2: number[] = [];
|
||||||
const constraintsP3: number[] = [];
|
const constraintsP3: number[] = [];
|
||||||
|
|
||||||
// Helper function for clean adding (vec4 structure)
|
|
||||||
const addConstraint = (arr: number[], a: number, b: number): void => {
|
const addConstraint = (arr: number[], a: number, b: number): void => {
|
||||||
arr.push(a, b, spacing, 1.0);
|
arr.push(a, b, config.spacing, 1.0);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Fill positions and pin the top edge
|
// Fill positions (Pin top row)
|
||||||
for (let y = 0; y < gridHeight; y++) {
|
for (let y = 0; y < config.gridHeight; y++) {
|
||||||
for (let x = 0; x < gridWidth; x++) {
|
for (let x = 0; x < config.gridWidth; x++) {
|
||||||
const idx = (y * gridWidth + x) * 4;
|
const idx = (y * config.gridWidth + x) * 4;
|
||||||
positionsData[idx + 0] = (x - gridWidth / 2) * spacing;
|
positionsData[idx + 0] = (x - config.gridWidth / 2) * config.spacing;
|
||||||
positionsData[idx + 1] = 5.0 - (y * spacing);
|
positionsData[idx + 1] = 5.0 - (y * config.spacing);
|
||||||
positionsData[idx + 2] = 0.0;
|
positionsData[idx + 2] = 0.0;
|
||||||
positionsData[idx + 3] = (y === 0) ? 0.0 : particleInvMass;
|
positionsData[idx + 3] = (y === 0) ? 0.0 : config.particleInvMass;
|
||||||
|
|
||||||
prevPositionsData[idx + 0] = positionsData[idx + 0];
|
prevPositionsData[idx + 0] = positionsData[idx + 0];
|
||||||
prevPositionsData[idx + 1] = positionsData[idx + 1];
|
prevPositionsData[idx + 1] = positionsData[idx + 1];
|
||||||
@@ -108,72 +139,70 @@ export class ClothComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- GRAPH COLORING: Fill constraints in 4 phases ---
|
// Graph Coloring (4 Phases)
|
||||||
// Phase 0: Horizontal Even
|
for (let y = 0; y < config.gridHeight; y++) {
|
||||||
for (let y = 0; y < gridHeight; y++) {
|
for (let x = 0; x < config.gridWidth - 1; x += 2) addConstraint(constraintsP0, y * config.gridWidth + x, y * config.gridWidth + x + 1);
|
||||||
for (let x = 0; x < gridWidth - 1; x += 2) {
|
for (let x = 1; x < config.gridWidth - 1; x += 2) addConstraint(constraintsP1, y * config.gridWidth + x, y * config.gridWidth + x + 1);
|
||||||
addConstraint(constraintsP0, y * gridWidth + x, y * gridWidth + x + 1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// Phase 1: Horizontal Odd
|
for (let y = 0; y < config.gridHeight - 1; y += 2) {
|
||||||
for (let y = 0; y < gridHeight; y++) {
|
for (let x = 0; x < config.gridWidth; x++) addConstraint(constraintsP2, y * config.gridWidth + x, (y + 1) * config.gridWidth + x);
|
||||||
for (let x = 1; x < gridWidth - 1; x += 2) {
|
|
||||||
addConstraint(constraintsP1, y * gridWidth + x, y * gridWidth + x + 1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// Phase 2: Vertical Even
|
for (let y = 1; y < config.gridHeight - 1; y += 2) {
|
||||||
for (let y = 0; y < gridHeight - 1; y += 2) {
|
for (let x = 0; x < config.gridWidth; x++) addConstraint(constraintsP3, y * config.gridWidth + x, (y + 1) * config.gridWidth + x);
|
||||||
for (let x = 0; x < gridWidth; x++) {
|
|
||||||
addConstraint(constraintsP2, y * gridWidth + x, (y + 1) * gridWidth + x);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Phase 3: Vertical Odd
|
|
||||||
for (let y = 1; y < gridHeight - 1; y += 2) {
|
|
||||||
for (let x = 0; x < gridWidth; x++) {
|
|
||||||
addConstraint(constraintsP3, y * gridWidth + x, (y + 1) * gridWidth + x);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const paramsData = new Float32Array(8);
|
return {
|
||||||
|
positions: positionsData,
|
||||||
|
prevPositions: prevPositionsData,
|
||||||
|
velocities: velocitiesData,
|
||||||
|
constraints: [constraintsP0, constraintsP1, constraintsP2, constraintsP3],
|
||||||
|
params: new Float32Array(8)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// --- 2. CREATE GPU STORAGE BUFFERS ---
|
// ========================================================================
|
||||||
const positionsBuffer = new StorageBuffer(engine, positionsData.byteLength);
|
// 3. BUFFER CREATION (GPU)
|
||||||
positionsBuffer.update(positionsData);
|
// ========================================================================
|
||||||
|
private createStorageBuffers(engine: WebGPUEngine, data: ClothData): ClothBuffers {
|
||||||
const prevPositionsBuffer = new StorageBuffer(engine, prevPositionsData.byteLength);
|
const createBuffer = (arrayData: Float32Array | number[]): StorageBuffer => {
|
||||||
prevPositionsBuffer.update(prevPositionsData);
|
const buffer = new StorageBuffer(engine, arrayData.length * 4);
|
||||||
|
buffer.update(arrayData instanceof Float32Array ? arrayData : new Float32Array(arrayData));
|
||||||
const velocitiesBuffer = new StorageBuffer(engine, velocitiesData.byteLength);
|
|
||||||
const paramsBuffer = new StorageBuffer(engine, paramsData.byteLength);
|
|
||||||
|
|
||||||
// Create 4 separate buffers for the 4 phases
|
|
||||||
const createAndPopulateBuffer = (data: number[]): StorageBuffer => {
|
|
||||||
const buffer = new StorageBuffer(engine, data.length * 4);
|
|
||||||
buffer.update(new Float32Array(data));
|
|
||||||
return buffer;
|
return buffer;
|
||||||
};
|
};
|
||||||
|
|
||||||
const cBuffer0 = createAndPopulateBuffer(constraintsP0);
|
return {
|
||||||
const cBuffer1 = createAndPopulateBuffer(constraintsP1);
|
positions: createBuffer(data.positions),
|
||||||
const cBuffer2 = createAndPopulateBuffer(constraintsP2);
|
prevPositions: createBuffer(data.prevPositions),
|
||||||
const cBuffer3 = createAndPopulateBuffer(constraintsP3);
|
velocities: createBuffer(data.velocities),
|
||||||
|
params: createBuffer(data.params),
|
||||||
|
constraints: data.constraints.map(cData => createBuffer(cData))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// --- 3. SETUP COMPUTE SHADERS ---
|
// ========================================================================
|
||||||
const csIntegrate = new ComputeShader("integrate", engine, { computeSource: CLOTH_INTEGRATE_COMPUTE_WGSL }, {
|
// 4. COMPUTE SHADERS
|
||||||
bindingsMapping: {
|
// ========================================================================
|
||||||
"p": { group: 0, binding: 0 },
|
private setupComputePipelines(engine: WebGPUEngine, buffers: ClothBuffers): ClothPipelines {
|
||||||
"positions": { group: 0, binding: 1 },
|
|
||||||
"prev_positions": { group: 0, binding: 2 },
|
|
||||||
"velocities": { group: 0, binding: 3 }
|
|
||||||
}
|
|
||||||
});
|
|
||||||
csIntegrate.setStorageBuffer("p", paramsBuffer);
|
|
||||||
csIntegrate.setStorageBuffer("positions", positionsBuffer);
|
|
||||||
csIntegrate.setStorageBuffer("prev_positions", prevPositionsBuffer);
|
|
||||||
csIntegrate.setStorageBuffer("velocities", velocitiesBuffer);
|
|
||||||
|
|
||||||
// Helper function to create the 4 solve shaders
|
// Helper for integrating & velocity
|
||||||
const createSolver = (name: string, cBuffer: StorageBuffer): ComputeShader => {
|
const createBasicShader = (name: string, source: string) => {
|
||||||
|
const cs = new ComputeShader(name, engine, { computeSource: source }, {
|
||||||
|
bindingsMapping: {
|
||||||
|
"p": { group: 0, binding: 0 },
|
||||||
|
"positions": { group: 0, binding: 1 },
|
||||||
|
"prev_positions": { group: 0, binding: 2 },
|
||||||
|
"velocities": { group: 0, binding: 3 }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
cs.setStorageBuffer("p", buffers.params);
|
||||||
|
cs.setStorageBuffer("positions", buffers.positions);
|
||||||
|
cs.setStorageBuffer("prev_positions", buffers.prevPositions);
|
||||||
|
cs.setStorageBuffer("velocities", buffers.velocities);
|
||||||
|
return cs;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Helper for solvers
|
||||||
|
const createSolverShader = (name: string, constraintBuffer: StorageBuffer) => {
|
||||||
const cs = new ComputeShader(name, engine, { computeSource: CLOTH_SOLVE_COMPUTE_WGSL }, {
|
const cs = new ComputeShader(name, engine, { computeSource: CLOTH_SOLVE_COMPUTE_WGSL }, {
|
||||||
bindingsMapping: {
|
bindingsMapping: {
|
||||||
"p": { group: 0, binding: 0 },
|
"p": { group: 0, binding: 0 },
|
||||||
@@ -181,36 +210,28 @@ export class ClothComponent {
|
|||||||
"constraints": { group: 0, binding: 2 }
|
"constraints": { group: 0, binding: 2 }
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
cs.setStorageBuffer("p", paramsBuffer);
|
cs.setStorageBuffer("p", buffers.params);
|
||||||
cs.setStorageBuffer("positions", positionsBuffer);
|
cs.setStorageBuffer("positions", buffers.positions);
|
||||||
cs.setStorageBuffer("constraints", cBuffer);
|
cs.setStorageBuffer("constraints", constraintBuffer);
|
||||||
return cs;
|
return cs;
|
||||||
};
|
};
|
||||||
|
|
||||||
const csSolve0 = createSolver("solve0", cBuffer0);
|
return {
|
||||||
const csSolve1 = createSolver("solve1", cBuffer1);
|
integrate: createBasicShader("integrate", CLOTH_INTEGRATE_COMPUTE_WGSL),
|
||||||
const csSolve2 = createSolver("solve2", cBuffer2);
|
solvers: buffers.constraints.map((cBuffer, i) => createSolverShader(`solve${i}`, cBuffer)),
|
||||||
const csSolve3 = createSolver("solve3", cBuffer3);
|
velocity: createBasicShader("velocity", CLOTH_VELOCITY_COMPUTE_WGSL)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const csVelocity = new ComputeShader("velocity", engine, { computeSource: CLOTH_VELOCITY_COMPUTE_WGSL }, {
|
// ========================================================================
|
||||||
bindingsMapping: {
|
// 5. RENDERING SETUP
|
||||||
"p": { group: 0, binding: 0 },
|
// ========================================================================
|
||||||
"positions": { group: 0, binding: 1 },
|
private setupRenderMesh(scene: Scene, config: ClothConfig, positionsBuffer: StorageBuffer): void {
|
||||||
"prev_positions": { group: 0, binding: 2 },
|
if (this.clothMesh) {
|
||||||
"velocities": { group: 0, binding: 3 }
|
|
||||||
}
|
|
||||||
});
|
|
||||||
csVelocity.setStorageBuffer("p", paramsBuffer);
|
|
||||||
csVelocity.setStorageBuffer("positions", positionsBuffer);
|
|
||||||
csVelocity.setStorageBuffer("prev_positions", prevPositionsBuffer);
|
|
||||||
csVelocity.setStorageBuffer("velocities", velocitiesBuffer);
|
|
||||||
|
|
||||||
// --- 4. SETUP RENDER MESH ---
|
|
||||||
if (this.clothMesh)
|
|
||||||
{
|
|
||||||
scene.removeMesh(this.clothMesh);
|
scene.removeMesh(this.clothMesh);
|
||||||
}
|
}
|
||||||
this.clothMesh = MeshBuilder.CreateGround("cloth", { width: 10, height: 10, subdivisions: gridWidth - 1 }, scene);
|
|
||||||
|
this.clothMesh = MeshBuilder.CreateGround("cloth", { width: 10, height: 10, subdivisions: config.gridWidth - 1 }, scene);
|
||||||
|
|
||||||
const clothMaterial = new ShaderMaterial("clothMat", scene, {
|
const clothMaterial = new ShaderMaterial("clothMat", scene, {
|
||||||
vertexSource: CLOTH_VERTEX_SHADER_WGSL,
|
vertexSource: CLOTH_VERTEX_SHADER_WGSL,
|
||||||
@@ -232,46 +253,54 @@ export class ClothComponent {
|
|||||||
camera.beta = Math.PI / 2.5;
|
camera.beta = Math.PI / 2.5;
|
||||||
camera.radius = 15;
|
camera.radius = 15;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================================================================
|
||||||
|
// 6. RENDER LOOP
|
||||||
|
// ========================================================================
|
||||||
|
private startRenderLoop(engine: WebGPUEngine, scene: Scene, config: ClothConfig, buffers: ClothBuffers, pipelines: ClothPipelines): void {
|
||||||
|
const paramsData = new Float32Array(8);
|
||||||
|
|
||||||
|
// Pre-calculate constraint dispatch sizes for the 4 phases
|
||||||
|
const constraintsLength = buffers.constraints.map(b => (b as any)._buffer.capacity / 4 / 4); // Elements / vec4 length
|
||||||
|
const dispatchXConstraints = constraintsLength.map(len => Math.ceil(len / 64));
|
||||||
|
const dispatchXVertices = Math.ceil(config.numVertices / 64);
|
||||||
|
const substeps = 15;
|
||||||
|
|
||||||
// --- 5. RENDER LOOP ---
|
|
||||||
scene.onBeforeRenderObservable.clear();
|
scene.onBeforeRenderObservable.clear();
|
||||||
scene.onBeforeRenderObservable.add(() => {
|
scene.onBeforeRenderObservable.add(() => {
|
||||||
this.simulationTime += engine.getDeltaTime() / 1000.0;
|
this.simulationTime += engine.getDeltaTime() / 1000.0;
|
||||||
|
|
||||||
|
// Update Physics Parameters
|
||||||
const windX = this.isWindActive ? 5.0 : 0.0;
|
const windX = this.isWindActive ? 5.0 : 0.0;
|
||||||
const windY = 0.0;
|
const windY = 0.0;
|
||||||
const windZ = this.isWindActive ? 15.0 : 0.0;
|
const windZ = this.isWindActive ? 15.0 : 0.0;
|
||||||
|
const scaledCompliance = 0.00001 * config.particleInvMass * config.spacing;
|
||||||
|
|
||||||
const baseCompliance = 0.00001;
|
paramsData[0] = 0.016; // dt
|
||||||
const scaledCompliance = baseCompliance * particleInvMass * spacing;
|
paramsData[1] = -9.81; // gravity
|
||||||
|
paramsData[2] = scaledCompliance;
|
||||||
paramsData[0] = 0.016;
|
paramsData[3] = config.numVertices;
|
||||||
paramsData[1] = -9.81;
|
|
||||||
paramsData[2] = scaledCompliance; //scaled stiffness
|
|
||||||
paramsData[3] = numVertices;
|
|
||||||
paramsData[4] = windX;
|
paramsData[4] = windX;
|
||||||
paramsData[5] = windY;
|
paramsData[5] = windY;
|
||||||
paramsData[6] = windZ;
|
paramsData[6] = windZ;
|
||||||
paramsData[7] = this.simulationTime;
|
paramsData[7] = this.simulationTime;
|
||||||
|
|
||||||
paramsBuffer.update(paramsData);
|
buffers.params.update(paramsData);
|
||||||
|
|
||||||
const dispatchXVertices = Math.ceil(numVertices / 64);
|
|
||||||
|
|
||||||
// 1. Predict positions
|
// 1. Predict positions
|
||||||
csIntegrate.dispatch(dispatchXVertices, 1, 1);
|
pipelines.integrate.dispatch(dispatchXVertices, 1, 1);
|
||||||
|
|
||||||
// 2. XPBD Solver (Substeps) - Solve each color individually
|
// 2. XPBD Solver (Substeps) - Graph Coloring Phase
|
||||||
const substeps = 15;
|
|
||||||
for (let i = 0; i < substeps; i++) {
|
for (let i = 0; i < substeps; i++) {
|
||||||
csSolve0.dispatch(Math.ceil((constraintsP0.length / 4) / 64), 1, 1);
|
pipelines.solvers[0].dispatch(dispatchXConstraints[0], 1, 1);
|
||||||
csSolve1.dispatch(Math.ceil((constraintsP1.length / 4) / 64), 1, 1);
|
pipelines.solvers[1].dispatch(dispatchXConstraints[1], 1, 1);
|
||||||
csSolve2.dispatch(Math.ceil((constraintsP2.length / 4) / 64), 1, 1);
|
pipelines.solvers[2].dispatch(dispatchXConstraints[2], 1, 1);
|
||||||
csSolve3.dispatch(Math.ceil((constraintsP3.length / 4) / 64), 1, 1);
|
pipelines.solvers[3].dispatch(dispatchXConstraints[3], 1, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Update velocities
|
// 3. Update velocities
|
||||||
csVelocity.dispatch(dispatchXVertices, 1, 1);
|
pipelines.velocity.dispatch(dispatchXVertices, 1, 1);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
36
src/app/pages/algorithms/cloth/cloth.model.ts
Normal file
36
src/app/pages/algorithms/cloth/cloth.model.ts
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
// --- SIMULATION CONFIGURATION ---
|
||||||
|
import {ComputeShader, StorageBuffer} from '@babylonjs/core';
|
||||||
|
|
||||||
|
export interface ClothConfig {
|
||||||
|
gridWidth: number;
|
||||||
|
gridHeight: number;
|
||||||
|
spacing: number;
|
||||||
|
density: number;
|
||||||
|
numVertices: number;
|
||||||
|
particleInvMass: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- RAW CPU DATA ---
|
||||||
|
export interface ClothData {
|
||||||
|
positions: Float32Array;
|
||||||
|
prevPositions: Float32Array;
|
||||||
|
velocities: Float32Array;
|
||||||
|
constraints: number[][]; // Array containing the 4 phases
|
||||||
|
params: Float32Array;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- WEBGPU BUFFERS ---
|
||||||
|
export interface ClothBuffers {
|
||||||
|
positions: StorageBuffer;
|
||||||
|
prevPositions: StorageBuffer;
|
||||||
|
velocities: StorageBuffer;
|
||||||
|
params: StorageBuffer;
|
||||||
|
constraints: StorageBuffer[]; // 4 phase buffers
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- COMPUTE PIPELINES ---
|
||||||
|
export interface ClothPipelines {
|
||||||
|
integrate: ComputeShader;
|
||||||
|
solvers: ComputeShader[]; // 4 solve shaders
|
||||||
|
velocity: ComputeShader;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user