From 629ef65ce52ef2bc9df2a4d9d40d71fa7f87914f Mon Sep 17 00:00:00 2001 From: Johannes Kirschbauer Date: Wed, 3 Sep 2025 21:34:26 +0200 Subject: [PATCH] ui/3d-fonts: replace troika with 3d rendered default font --- pkgs/clan-app/ui/src/scene/MachineRepr.ts | 61 +++++++++++++++-------- 1 file changed, 41 insertions(+), 20 deletions(-) diff --git a/pkgs/clan-app/ui/src/scene/MachineRepr.ts b/pkgs/clan-app/ui/src/scene/MachineRepr.ts index deac95eb7..8b673d63e 100644 --- a/pkgs/clan-app/ui/src/scene/MachineRepr.ts +++ b/pkgs/clan-app/ui/src/scene/MachineRepr.ts @@ -2,9 +2,9 @@ import * as THREE from "three"; import { ObjectRegistry } from "./ObjectRegistry"; import { Accessor, createEffect, createRoot, on } from "solid-js"; import { renderLoop } from "./RenderLoop"; -// @ts-expect-error: No types for troika-three-text -import { Text } from "troika-three-text"; -import ttf from "../../.fonts/ArchivoSemiCondensed-Medium.ttf"; +import { TextGeometry } from "three/examples/jsm/geometries/TextGeometry.js"; +import { FontLoader } from "three/examples/jsm/Addons"; +import jsonfont from "three/examples/fonts/helvetiker_regular.typeface.json"; // Constants const BASE_SIZE = 0.9; @@ -201,26 +201,49 @@ export class MachineRepr { private createLabel(id: string) { const group = new THREE.Group(); // 0x162324 - const text = new Text(); - text.text = id; - text.font = ttf; - text.fontSize = 0.1; - text.color = 0xffffff; - text.anchorX = "center"; - text.anchorY = "middle"; - text.position.set(0, 0, 0.01); - text.outlineWidth = 0.005; - text.outlineColor = 0x162324; + // const text = new Text(); + // text.text = id; + // text.font = ttf; + // text.fontSize = 0.1; + // text.color = 0xffffff; + // text.anchorX = "center"; + // text.anchorY = "middle"; + // text.position.set(0, 0, 0.01); + // text.outlineWidth = 0.005; + // text.outlineColor = 0x162324; + // text.sync(() => { + // renderLoop.requestRender(); + // }); - // Re-render on text changes - text.sync(() => { - renderLoop.requestRender(); + const textMaterial = new THREE.MeshPhongMaterial({ + color: 0xffffff, }); + const textGeo = new TextGeometry(id, { + font: new FontLoader().parse(jsonfont), + size: 0.09, + depth: 0.001, + curveSegments: 12, + bevelEnabled: false, + }); + + const text = new THREE.Mesh(textGeo, textMaterial); + textGeo.computeBoundingBox(); + + const bbox = textGeo.boundingBox; + if (bbox) { + const xMid = -0.5 * (bbox.max.x - bbox.min.x); + // const yMid = -0.5 * (bbox.max.y - bbox.min.y); + // const zMid = -0.5 * (bbox.max.z - bbox.min.z); + + // Translate geometry so center is at origin / baseline aligned with y=0 + textGeo.translate(xMid, -0.035, 0); + } // --- Background (rounded rect) --- const padding = 0.04; - // TODO: compute from text.bounds after sync - const bgWidth = text.text.length * 0.07 + padding; + const textWidth = bbox ? bbox.max.x - bbox.min.x : 1; + const bgWidth = textWidth + 10 * padding; + // const bgWidth = text.text.length * 0.07 + padding; const bgHeight = 0.1 + 2 * padding; const radius = 0.02; @@ -230,8 +253,6 @@ export class MachineRepr { const bg = new THREE.Mesh(bgGeom, bgMat); bg.position.set(0, 0, -0.01); - // bg.position.set(0, 0, -0.01); // slightly behind text - // --- Arrow (triangle pointing down) --- const arrowShape = new THREE.Shape(); arrowShape.moveTo(-0.05, 0);