From ba82df769407adb4e53796a7001d44f0491a70ca Mon Sep 17 00:00:00 2001 From: lobo Date: Fri, 30 Oct 2026 14:12:22 +0100 Subject: [PATCH] fix(cloth-icm): reuse contour mesh so contours draw Contour lines were disposed and rebuilt when their shape changed, so the new shader never finished compiling before removal. Keep one mesh and set its geometry each frame. Hide it when there are no contours. Draw contours green and culled BVH boxes cyan for contrast. --- .../cloth-icm/cloth-icm-renderer.ts | 41 ++++++++----------- 1 file changed, 17 insertions(+), 24 deletions(-) diff --git a/src/app/pages/algorithms/cloth-icm/cloth-icm-renderer.ts b/src/app/pages/algorithms/cloth-icm/cloth-icm-renderer.ts index caf03e9..61ccb6a 100644 --- a/src/app/pages/algorithms/cloth-icm/cloth-icm-renderer.ts +++ b/src/app/pages/algorithms/cloth-icm/cloth-icm-renderer.ts @@ -1,5 +1,5 @@ import { - ArcRotateCamera, Color3, Color4, LinesMesh, Mesh, MeshBuilder, Scene, ShaderMaterial, StandardMaterial, Vector3, VertexBuffer, VertexData + ArcRotateCamera, Color3, Color4, CreateLineSystemVertexData, LinesMesh, Mesh, MeshBuilder, Scene, ShaderMaterial, StandardMaterial, Vector3, VertexBuffer, VertexData } from '@babylonjs/core'; import {CLOTH_FRAGMENT_SHADER_GLSL, CLOTH_VERTEX_SHADER_GLSL} from '../cloth/cloth-glsl.shader'; import {ClothIcmScene} from './cloth-icm-scenes'; @@ -10,8 +10,8 @@ import {DOP_SIZE} from './collision/dop18'; import {Contour} from './collision/intersection-contours'; import {IntersectionSet} from './collision/intersection-set'; -const CONTOUR_COLOR = new Color3(1, 0.1, 0.1); -const CULLED_COLOR = new Color4(0.2, 0.9, 0.3, 1); +const CONTOUR_COLOR = new Color3(0.1, 1, 0.2); +const CULLED_COLOR = new Color4(0.2, 0.8, 1, 1); const TESTED_COLOR = new Color4(0.6, 0.6, 0.6, 1); const OBSTACLE_COLOR = new Color3(0.35, 0.55, 0.9); const GROUND_SIZE = 8; @@ -35,7 +35,6 @@ export class ClothIcmRenderer { private bvhLines: LinesMesh | null = null; private vertexBuffer = new Float32Array(0); private culledRegion = new Uint8Array(0); - private contourShape: number[] = []; private bvhBoxes: BvhBox[] = []; private bvhPaths: Vector3[][] = []; private bvhPathColors: Color4[][] = []; @@ -67,7 +66,6 @@ export class ClothIcmRenderer { this.obstacles.forEach(mesh => mesh.dispose()); this.obstacleMaterial?.dispose(); this.contourLines = null; - this.contourShape = []; this.bvhLines = null; this.bvhBoxes = []; this.bvhPaths = []; @@ -153,31 +151,26 @@ export class ClothIcmRenderer { /** * The hit points come from the detection, which runs on the predicted positions before the solver moves the cloth, - * so the lines can be a little off the drawn cloth (up to about 0.05 m). Babylon can only update a line system in - * place while every line keeps its point count; otherwise the mesh is rebuilt. + * so the lines can be a little off the drawn cloth (up to about 0.05 m). + * The mesh is created once and gets new geometry every frame. Disposing and creating it again would drop its shader + * (reference counted) and recompile it; the line shader compiles asynchronously, so a mesh whose point layout changes + * every frame (the drop scene) was never ready to draw. */ private updateContourLines(scene: Scene, set: IntersectionSet, contours: Contour[]): void { if (contours.length === 0) { - this.contourLines?.dispose(); - this.contourLines = null; - this.contourShape = []; + this.contourLines?.setEnabled(false); return; } - const lines = contours.map(contour => this.contourPath(set, contour)); - const shape = lines.map(line => line.length); - const existing = this.contourLines; - const sameShape = shape.length === this.contourShape.length && shape.every((length, index) => length === this.contourShape[index]); - if (existing && sameShape) { - MeshBuilder.CreateLineSystem('clothIcmContours', {lines, instance: existing}); - return; + const vertexData = CreateLineSystemVertexData({lines: contours.map(contour => this.contourPath(set, contour))}); + if (!this.contourLines) { + const mesh = new LinesMesh('clothIcmContours', scene); + mesh.color = CONTOUR_COLOR; + mesh.renderingGroupId = OVERLAY_RENDERING_GROUP; + mesh.alwaysSelectAsActiveMesh = true; + this.contourLines = mesh; } - this.contourLines?.dispose(); - const mesh = MeshBuilder.CreateLineSystem('clothIcmContours', {lines, updatable: true}, scene); - mesh.color = CONTOUR_COLOR; - mesh.renderingGroupId = OVERLAY_RENDERING_GROUP; - mesh.alwaysSelectAsActiveMesh = true; - this.contourLines = mesh; - this.contourShape = shape; + vertexData.applyToMesh(this.contourLines, true); + this.contourLines.setEnabled(true); } private contourPath(set: IntersectionSet, contour: Contour): Vector3[] {