diff --git a/src/app/pages/algorithms/cloth/cloth.component.html b/src/app/pages/algorithms/cloth/cloth.component.html index f98a204..ed559ae 100644 --- a/src/app/pages/algorithms/cloth/cloth.component.html +++ b/src/app/pages/algorithms/cloth/cloth.component.html @@ -3,6 +3,13 @@ {{ 'CLOTH.TITLE' | translate }} +
+
+ +
+
{ + this.simulationTime += engine.getDeltaTime() / 1000.0; + + const windX = this.isWindActive ? 5.0 : 0.0; + const windY = 0.0; + const windZ = this.isWindActive ? 15.0 : 0.0; + + const baseCompliance = 0.00001; + const scaledCompliance = baseCompliance * particleInvMass * spacing; + paramsData[0] = 0.016; paramsData[1] = -9.81; - paramsData[2] = 0.0001; // Compliance (very small = stiff fabric) + paramsData[2] = scaledCompliance; //scaled stiffness paramsData[3] = numVertices; + paramsData[4] = windX; + paramsData[5] = windY; + paramsData[6] = windZ; + paramsData[7] = this.simulationTime; + paramsBuffer.update(paramsData); const dispatchXVertices = Math.ceil(numVertices / 64); @@ -230,7 +262,8 @@ export class ClothComponent { csIntegrate.dispatch(dispatchXVertices, 1, 1); // 2. XPBD Solver (Substeps) - Solve each color individually - for (let i = 0; i < 5; i++) { + const substeps = 15; + for (let i = 0; i < substeps; i++) { csSolve0.dispatch(Math.ceil((constraintsP0.length / 4) / 64), 1, 1); csSolve1.dispatch(Math.ceil((constraintsP1.length / 4) / 64), 1, 1); csSolve2.dispatch(Math.ceil((constraintsP2.length / 4) / 64), 1, 1); diff --git a/src/app/pages/algorithms/cloth/cloth.shader.ts b/src/app/pages/algorithms/cloth/cloth.shader.ts index f8ea79c..08d055b 100644 --- a/src/app/pages/algorithms/cloth/cloth.shader.ts +++ b/src/app/pages/algorithms/cloth/cloth.shader.ts @@ -6,14 +6,14 @@ // --- SHARED DATA STRUCTURES --- export const CLOTH_SHARED_STRUCTS = ` struct Params { - dt: f32, // Time step per substep - gravity_y: f32, // Gravity (e.g. -9.81) - compliance: f32, // Inverse stiffness (0.0 = completely rigid) - numVertices: f32, // Total number of vertices - numConstraints: f32, // Total number of springs - pad1: f32, // Padding - pad2: f32, // Padding - pad3: f32 // Padding (8 * f32 = 32 bytes) + dt: f32, + gravity_y: f32, + compliance: f32, + numVertices: f32, + wind_x: f32, + wind_y: f32, + wind_z: f32, + time: f32 }; `; @@ -76,17 +76,25 @@ export const CLOTH_INTEGRATE_COMPUTE_WGSL = CLOTH_SHARED_STRUCTS + ` var pos = positions[idx]; var vel = velocities[idx]; - let invMass = pos.w; // w stores inverse mass (0.0 = pinned/static) + let invMass = pos.w; - // Only move if it is not pinned if (invMass > 0.0) { - // 1. Apply Gravity: v = v + g * dt vel.y = vel.y + (p.gravity_y * p.dt); - // 2. Save current position for later velocity calculation + let flutter = sin(pos.x * 2.0 + p.time * 5.0) * cos(pos.y * 2.0 + p.time * 3.0); + + let windForce = vec3( + p.wind_x + (flutter * p.wind_x * 0.8), + p.wind_y + (flutter * 2.0), // Leichter Auftrieb durchs Flattern + p.wind_z + (flutter * p.wind_z * 0.8) + ); + + vel.x = vel.x + (windForce.x * p.dt); + vel.y = vel.y + (windForce.y * p.dt); + vel.z = vel.z + (windForce.z * p.dt); + prev_positions[idx] = pos; - // 3. Predict new position: p = p + v * dt pos.x = pos.x + vel.x * p.dt; pos.y = pos.y + vel.y * p.dt; pos.z = pos.z + vel.z * p.dt; diff --git a/src/assets/i18n/de.json b/src/assets/i18n/de.json index dcd275b..014c568 100644 --- a/src/assets/i18n/de.json +++ b/src/assets/i18n/de.json @@ -472,7 +472,9 @@ } }, "CLOTH": { - "TITLE": "Stoff-Simulation" + "TITLE": "Stoff-Simulation", + "WIND_ON": "Wind Einschalten", + "WIND_OFF": "Wind Ausschalten" }, "ALGORITHM": { "TITLE": "Algorithmen", diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index 186685b..4681f5e 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -471,7 +471,9 @@ } }, "CLOTH": { - "TITLE": "Cloth simulation" + "TITLE": "Cloth simulation", + "WIND_ON": "Wind On", + "WIND_OFF": "Wind Off" }, "ALGORITHM": { "TITLE": "Algorithms",