Feature: Add path segment rendering and point outlines

This commit is contained in:
2026-05-12 22:27:26 +02:00
parent 143b77fee4
commit 05108b6091

View File

@@ -7454,6 +7454,104 @@ export class ViewportHost {
return new BufferGeometry().setFromPoints(points); return new BufferGeometry().setFromPoints(points);
} }
private createPathPointVectors(path: ScenePath): Vector3[] {
const points = path.points.map(
(point) =>
new Vector3(point.position.x, point.position.y, point.position.z)
);
if (path.loop && points.length > 1) {
points.push(points[0].clone());
}
return points;
}
private createPathSegmentMesh(
start: Vector3,
end: Vector3,
radius: number,
material: MeshBasicMaterial,
pathId: string
): Mesh<CylinderGeometry, MeshBasicMaterial> | null {
const delta = end.clone().sub(start);
const length = delta.length();
if (length < 0.0001) {
return null;
}
const mesh = new Mesh(
new CylinderGeometry(radius, radius, length, 10, 1),
material
);
const direction = delta.normalize();
mesh.position.copy(start).add(end).multiplyScalar(0.5);
mesh.quaternion.setFromUnitVectors(new Vector3(0, 1, 0), direction);
mesh.renderOrder = PATH_RENDER_ORDER;
mesh.userData.pathId = pathId;
return mesh;
}
private createPathSegmentMeshes(path: ScenePath): PathRenderObjects["segments"] {
const points = this.createPathPointVectors(path);
const segments: PathRenderObjects["segments"] = [];
for (let index = 0; index < points.length - 1; index += 1) {
const outlineMesh = this.createPathSegmentMesh(
points[index],
points[index + 1],
PATH_SEGMENT_OUTLINE_RADIUS,
new MeshBasicMaterial({
color: PATH_OUTLINE_COLOR,
depthTest: false,
depthWrite: false
}),
path.id
);
const mesh = this.createPathSegmentMesh(
points[index],
points[index + 1],
PATH_SEGMENT_RADIUS,
new MeshBasicMaterial({
color: PATH_COLOR,
depthTest: false,
depthWrite: false
}),
path.id
);
if (outlineMesh !== null && mesh !== null) {
segments.push({
outlineMesh,
mesh
});
}
}
return segments;
}
private addPathSegmentMeshes(segments: PathRenderObjects["segments"]) {
for (const segment of segments) {
this.pathGroup.add(segment.outlineMesh);
this.pathGroup.add(segment.mesh);
}
}
private disposePathSegmentMeshes(segments: PathRenderObjects["segments"]) {
for (const segment of segments) {
this.pathGroup.remove(segment.outlineMesh);
this.pathGroup.remove(segment.mesh);
segment.outlineMesh.geometry.dispose();
segment.outlineMesh.material.dispose();
segment.mesh.geometry.dispose();
segment.mesh.material.dispose();
}
}
private rebuildPaths(document: SceneDocument, selection: EditorSelection) { private rebuildPaths(document: SceneDocument, selection: EditorSelection) {
this.clearPaths(); this.clearPaths();
@@ -7467,7 +7565,9 @@ export class ViewportHost {
const renderObjects = this.createPathRenderObjects(path, selection); const renderObjects = this.createPathRenderObjects(path, selection);
this.pathGroup.add(renderObjects.line); this.pathGroup.add(renderObjects.line);
this.addPathSegmentMeshes(renderObjects.segments);
for (const pointMesh of renderObjects.pointMeshes) { for (const pointMesh of renderObjects.pointMeshes) {
this.pathGroup.add(pointMesh.outlineMesh);
this.pathGroup.add(pointMesh.mesh); this.pathGroup.add(pointMesh.mesh);
} }
this.pathRenderObjects.set(path.id, renderObjects); this.pathRenderObjects.set(path.id, renderObjects);
@@ -7486,33 +7586,59 @@ export class ViewportHost {
new LineBasicMaterial({ new LineBasicMaterial({
color: isPathSelected(selection, path.id) color: isPathSelected(selection, path.id)
? PATH_SELECTED_COLOR ? PATH_SELECTED_COLOR
: PATH_COLOR : PATH_COLOR,
depthTest: false,
depthWrite: false,
linewidth: PATH_LINE_WIDTH
}) })
); );
line.renderOrder = PATH_RENDER_ORDER + 1;
line.userData.pathId = path.id; line.userData.pathId = path.id;
const segments = this.createPathSegmentMeshes(path);
const pointMeshes = path.points.map((point) => { const pointMeshes = path.points.map((point) => {
const outlineMesh = new Mesh(
new SphereGeometry(PATH_POINT_OUTLINE_RADIUS, 16, 12),
new MeshBasicMaterial({
color: PATH_OUTLINE_COLOR,
depthTest: false,
depthWrite: false
})
);
const mesh = new Mesh( const mesh = new Mesh(
new SphereGeometry(PATH_POINT_RADIUS, 12, 12), new SphereGeometry(PATH_POINT_RADIUS, 16, 12),
new MeshBasicMaterial({ new MeshBasicMaterial({
color: isPathSelected(selection, path.id) color: isPathSelected(selection, path.id)
? PATH_POINT_SELECTED_COLOR ? PATH_POINT_SELECTED_COLOR
: PATH_POINT_COLOR : PATH_POINT_COLOR,
depthTest: false,
depthWrite: false
}) })
); );
outlineMesh.position.set(
point.position.x,
point.position.y,
point.position.z
);
mesh.position.set(point.position.x, point.position.y, point.position.z); mesh.position.set(point.position.x, point.position.y, point.position.z);
outlineMesh.renderOrder = PATH_RENDER_ORDER + 2;
mesh.renderOrder = PATH_RENDER_ORDER + 3;
outlineMesh.userData.pathId = path.id;
outlineMesh.userData.pathPointId = point.id;
mesh.userData.pathId = path.id; mesh.userData.pathId = path.id;
mesh.userData.pathPointId = point.id; mesh.userData.pathPointId = point.id;
return { return {
pointId: point.id, pointId: point.id,
outlineMesh,
mesh mesh
}; };
}); });
return { return {
line, line,
segments,
pointMeshes pointMeshes
}; };
} }
@@ -7521,9 +7647,13 @@ export class ViewportHost {
this.pathGroup.remove(renderObjects.line); this.pathGroup.remove(renderObjects.line);
renderObjects.line.geometry.dispose(); renderObjects.line.geometry.dispose();
renderObjects.line.material.dispose(); renderObjects.line.material.dispose();
this.disposePathSegmentMeshes(renderObjects.segments);
for (const pointMesh of renderObjects.pointMeshes) { for (const pointMesh of renderObjects.pointMeshes) {
this.pathGroup.remove(pointMesh.outlineMesh);
this.pathGroup.remove(pointMesh.mesh); this.pathGroup.remove(pointMesh.mesh);
pointMesh.outlineMesh.geometry.dispose();
pointMesh.outlineMesh.material.dispose();
pointMesh.mesh.geometry.dispose(); pointMesh.mesh.geometry.dispose();
pointMesh.mesh.material.dispose(); pointMesh.mesh.material.dispose();
} }
@@ -7555,7 +7685,9 @@ export class ViewportHost {
this.currentSelection this.currentSelection
); );
this.pathGroup.add(renderObjects.line); this.pathGroup.add(renderObjects.line);
this.addPathSegmentMeshes(renderObjects.segments);
for (const pointMesh of renderObjects.pointMeshes) { for (const pointMesh of renderObjects.pointMeshes) {
this.pathGroup.add(pointMesh.outlineMesh);
this.pathGroup.add(pointMesh.mesh); this.pathGroup.add(pointMesh.mesh);
} }
this.pathRenderObjects.set(pathId, renderObjects); this.pathRenderObjects.set(pathId, renderObjects);
@@ -7577,6 +7709,9 @@ export class ViewportHost {
renderObjects.line.geometry.dispose(); renderObjects.line.geometry.dispose();
renderObjects.line.geometry = this.createPathLineGeometry(path); renderObjects.line.geometry = this.createPathLineGeometry(path);
this.disposePathSegmentMeshes(renderObjects.segments);
renderObjects.segments = this.createPathSegmentMeshes(path);
this.addPathSegmentMeshes(renderObjects.segments);
for (const pointMesh of renderObjects.pointMeshes) { for (const pointMesh of renderObjects.pointMeshes) {
const point = path.points.find( const point = path.points.find(
@@ -7587,6 +7722,11 @@ export class ViewportHost {
continue; continue;
} }
pointMesh.outlineMesh.position.set(
point.position.x,
point.position.y,
point.position.z
);
pointMesh.mesh.position.set( pointMesh.mesh.position.set(
point.position.x, point.position.x,
point.position.y, point.position.y,