ui/scene: replace 2d labels

This commit is contained in:
Johannes Kirschbauer
2025-08-29 19:33:50 +02:00
parent ba027c2239
commit c7e3bf624e
4 changed files with 53 additions and 8 deletions

View File

@@ -27,6 +27,7 @@ export class MachineManager {
machinesQueryResult: MachinesQueryResult, machinesQueryResult: MachinesQueryResult,
selectedIds: Accessor<Set<string>>, selectedIds: Accessor<Set<string>>,
setMachinePos: (id: string, position: [number, number] | null) => void, setMachinePos: (id: string, position: [number, number] | null) => void,
camera: THREE.Camera,
) { ) {
this.machinePositionsSignal = machinePositionsSignal; this.machinePositionsSignal = machinePositionsSignal;
@@ -82,6 +83,7 @@ export class MachineManager {
id, id,
selectedIds, selectedIds,
highlightGroups, highlightGroups,
camera,
); );
this.machines.set(id, repr); this.machines.set(id, repr);
scene.add(repr.group); scene.add(repr.group);

View File

@@ -3,6 +3,9 @@ import { ObjectRegistry } from "./ObjectRegistry";
import { CSS2DObject } from "three/examples/jsm/renderers/CSS2DRenderer.js"; import { CSS2DObject } from "three/examples/jsm/renderers/CSS2DRenderer.js";
import { Accessor, createEffect, createRoot, on } from "solid-js"; import { Accessor, createEffect, createRoot, on } from "solid-js";
import { renderLoop } from "./RenderLoop"; import { renderLoop } from "./RenderLoop";
import { TextGeometry } from "three/examples/jsm/geometries/TextGeometry.js";
import { FontLoader, FontData } from "three/examples/jsm/Addons";
import font from "../../.fonts/CommitMono_Regular.json";
// Constants // Constants
const BASE_SIZE = 0.9; const BASE_SIZE = 0.9;
@@ -28,6 +31,7 @@ export class MachineRepr {
private baseMesh: THREE.Mesh; private baseMesh: THREE.Mesh;
private geometry: THREE.BoxGeometry; private geometry: THREE.BoxGeometry;
private material: THREE.MeshPhongMaterial; private material: THREE.MeshPhongMaterial;
private camera: THREE.Camera;
private disposeRoot: () => void; private disposeRoot: () => void;
@@ -38,8 +42,10 @@ export class MachineRepr {
id: string, id: string,
selectedSignal: Accessor<Set<string>>, selectedSignal: Accessor<Set<string>>,
highlightGroups: Record<string, Set<string>>, // Reactive store highlightGroups: Record<string, Set<string>>, // Reactive store
camera: THREE.Camera,
) { ) {
this.id = id; this.id = id;
this.camera = camera;
this.geometry = new THREE.BoxGeometry(CUBE_SIZE, CUBE_SIZE, CUBE_SIZE); this.geometry = new THREE.BoxGeometry(CUBE_SIZE, CUBE_SIZE, CUBE_SIZE);
this.material = new THREE.MeshPhongMaterial({ this.material = new THREE.MeshPhongMaterial({
color: CUBE_COLOR, color: CUBE_COLOR,
@@ -62,7 +68,7 @@ export class MachineRepr {
this.baseMesh.name = "base"; this.baseMesh.name = "base";
const label = this.createLabel(id); const label = this.createLabel(id);
this.cubeMesh.add(label); // this.cubeMesh.add(label);
const shadowPlaneMaterial = new THREE.MeshStandardMaterial({ const shadowPlaneMaterial = new THREE.MeshStandardMaterial({
color: BASE_COLOR, // any color you like color: BASE_COLOR, // any color you like
@@ -82,6 +88,7 @@ export class MachineRepr {
shadowPlane.position.set(0, BASE_HEIGHT + 0.0001, 0); shadowPlane.position.set(0, BASE_HEIGHT + 0.0001, 0);
this.group = new THREE.Group(); this.group = new THREE.Group();
this.group.add(label);
this.group.add(this.cubeMesh); this.group.add(this.cubeMesh);
this.group.add(this.baseMesh); this.group.add(this.baseMesh);
this.group.add(shadowPlane); this.group.add(shadowPlane);
@@ -161,12 +168,40 @@ export class MachineRepr {
} }
private createLabel(id: string) { private createLabel(id: string) {
const div = document.createElement("div"); // const div = document.createElement("div");
div.className = "machine-label"; // div.className = "machine-label";
div.textContent = id; // div.textContent = id;
const label = new CSS2DObject(div); // const label = new CSS2DObject(div);
label.position.set(0, CUBE_SIZE + 0.1, 0); // label.position.set(0, CUBE_SIZE + 0.1, 0);
return label; // return label;
const loader = new FontLoader();
const final = loader.parse(font as unknown as FontData);
const geometry = new TextGeometry(id, {
font: final,
size: 0.1,
depth: 0.01,
curveSegments: 12,
bevelEnabled: false,
bevelThickness: 0.01,
bevelSize: 0.01,
bevelOffset: 0,
bevelSegments: 5,
});
const textMaterial = new THREE.MeshPhongMaterial({ color: 0x000000 });
const textMesh = new THREE.Mesh(geometry, textMaterial);
geometry.computeBoundingBox();
if (geometry.boundingBox) {
const xMid =
-0.5 * (geometry.boundingBox.max.x - geometry.boundingBox.min.x);
geometry.translate(xMid, 0, 0); // shift so it's centered on X
}
textMesh.position.set(0, CUBE_SIZE + 0.15, 0); // above the cube
textMesh.quaternion.copy(this.camera.quaternion);
textMesh.userData.isLabel = true;
return textMesh;
} }
dispose(scene: THREE.Scene) { dispose(scene: THREE.Scene) {

View File

@@ -1,7 +1,7 @@
import { Scene, Camera, WebGLRenderer } from "three"; import { Scene, Camera, WebGLRenderer } from "three";
import { MapControls } from "three/examples/jsm/controls/MapControls.js"; import { MapControls } from "three/examples/jsm/controls/MapControls.js";
import { CSS2DRenderer } from "three/examples/jsm/renderers/CSS2DRenderer.js"; import { CSS2DRenderer } from "three/examples/jsm/renderers/CSS2DRenderer.js";
import * as THREE from "three";
/** /**
* Private class to manage the render loop * Private class to manage the render loop
* @internal * @internal
@@ -93,6 +93,13 @@ class RenderLoop {
this.renderer.render(this.bgScene, this.bgCamera); this.renderer.render(this.bgScene, this.bgCamera);
this.renderer.render(this.scene, this.camera); this.renderer.render(this.scene, this.camera);
this.scene.traverse((obj) => {
if (obj.userData.isLabel) {
(obj as THREE.Mesh).quaternion.copy(this.camera.quaternion);
}
});
this.labelRenderer.render(this.scene, this.camera); this.labelRenderer.render(this.scene, this.camera);
} }

View File

@@ -433,6 +433,7 @@ export function CubeScene(props: {
props.cubesQuery, props.cubesQuery,
props.selectedIds, props.selectedIds,
props.setMachinePos, props.setMachinePos,
camera,
); );
// Click handler: // Click handler: