Source code for simvx.core.nodes_3d.text
"""Text3D -- depth-tested MSDF text billboard at a 3D world position."""
from ..descriptors import Property
from ..math.types import Vec2
from ..properties import Colour
from .node3d import Node3D
[docs]
class Text3D(Node3D):
"""Text that floats at a 3D world position as a depth-tested billboard.
Rendered by the graphics backend's ``Billboard2DPass`` (design §5.2) as a
camera-facing run of MSDF glyph quads, depth-tested against the 3D scene so
the label is correctly occluded by (and occludes) geometry -- unlike a plain
screen-space overlay. Symmetric with ``Text2D`` / ``Sprite3D``.
``font_scale`` is the text-size multiplier (the canonical ``font_scale * 16``
logical-pixel unit shared with the whole text family); ``pixel_size`` maps
those logical pixels to world units, so the on-screen size scales with both
the node's distance from the camera and ``pixel_size``.
Example::
label = self.add_child(Text3D(text="Boss", position=(0, 2, -5)))
label.font_scale = 2.0
"""
text = Property("", hint="Text to display")
font_scale = Property(1.0, range=(0.1, 100.0), hint="Text size multiplier (font_scale * 16 logical px)")
pixel_size = Property(0.01, range=(0.0001, 1.0), hint="World units per logical pixel")
colour = Colour((1.0, 1.0, 1.0, 1.0))
alignment = Property("centre", enum=["left", "centre", "right"], hint="Horizontal alignment about the anchor")
billboard = Property(True, hint="Always face the camera")
offset = Property(None, hint="Logical-pixel offset from position (Vec2)")
[docs]
@property
def pixel_offset(self) -> Vec2:
"""Offset from the anchor in logical pixels (``(0, 0)`` when unset)."""
if self.offset is None:
return Vec2(0.0, 0.0)
return Vec2(float(self.offset[0]), float(self.offset[1]))
[docs]
def on_draw(self, renderer) -> None:
"""Emit a depth-tested MSDF text billboard for the renderer (design §5.2)."""
if not self.text or not self.visible:
return
draw = getattr(renderer, "draw_text_3d", None)
if draw is None: # backend without the billboard pass
return
po = self.pixel_offset
draw(
text=self.text,
position=self.world_position,
font_scale=float(self.font_scale),
pixel_size=float(self.pixel_size),
colour=tuple(self.colour),
alignment=str(self.alignment),
offset=(float(po.x), float(po.y)),
)