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

View File

@@ -3,6 +3,9 @@ import { ObjectRegistry } from "./ObjectRegistry";
import { CSS2DObject } from "three/examples/jsm/renderers/CSS2DRenderer.js";
import { Accessor, createEffect, createRoot, on } from "solid-js";
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
const BASE_SIZE = 0.9;
@@ -28,6 +31,7 @@ export class MachineRepr {
private baseMesh: THREE.Mesh;
private geometry: THREE.BoxGeometry;
private material: THREE.MeshPhongMaterial;
private camera: THREE.Camera;
private disposeRoot: () => void;
@@ -38,8 +42,10 @@ export class MachineRepr {
id: string,
selectedSignal: Accessor<Set<string>>,
highlightGroups: Record<string, Set<string>>, // Reactive store
camera: THREE.Camera,
) {
this.id = id;
this.camera = camera;
this.geometry = new THREE.BoxGeometry(CUBE_SIZE, CUBE_SIZE, CUBE_SIZE);
this.material = new THREE.MeshPhongMaterial({
color: CUBE_COLOR,
@@ -62,7 +68,7 @@ export class MachineRepr {
this.baseMesh.name = "base";
const label = this.createLabel(id);
this.cubeMesh.add(label);
// this.cubeMesh.add(label);
const shadowPlaneMaterial = new THREE.MeshStandardMaterial({
color: BASE_COLOR, // any color you like
@@ -82,6 +88,7 @@ export class MachineRepr {
shadowPlane.position.set(0, BASE_HEIGHT + 0.0001, 0);
this.group = new THREE.Group();
this.group.add(label);
this.group.add(this.cubeMesh);
this.group.add(this.baseMesh);
this.group.add(shadowPlane);
@@ -161,12 +168,40 @@ export class MachineRepr {
}
private createLabel(id: string) {
const div = document.createElement("div");
div.className = "machine-label";
div.textContent = id;
const label = new CSS2DObject(div);
label.position.set(0, CUBE_SIZE + 0.1, 0);
return label;
// const div = document.createElement("div");
// div.className = "machine-label";
// div.textContent = id;
// const label = new CSS2DObject(div);
// label.position.set(0, CUBE_SIZE + 0.1, 0);
// 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) {

View File

@@ -1,7 +1,7 @@
import { Scene, Camera, WebGLRenderer } from "three";
import { MapControls } from "three/examples/jsm/controls/MapControls.js";
import { CSS2DRenderer } from "three/examples/jsm/renderers/CSS2DRenderer.js";
import * as THREE from "three";
/**
* Private class to manage the render loop
* @internal
@@ -93,6 +93,13 @@ class RenderLoop {
this.renderer.render(this.bgScene, this.bgCamera);
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);
}

View File

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