Advanced guide: Lines, paths, and text labels¶
Time: ~15 minutes Level: Intermediate What you'll build: 3D line drawings and floating text labels in world space
Overview¶
SceneView 3.2.0 adds three geometry primitives for annotation and visualization:
LineNode— a single line segment between two pointsPathNode— a polyline through multiple points (open or closed)TextNode— a camera-facing text label rendered as a billboard
Lines and paths¶
Single line¶
SceneView(engine = engine, modelLoader = modelLoader) {
val line = remember(engine) {
LineNode(
engine = engine,
start = Position(x = -1f, y = 0f, z = 0f),
end = Position(x = 1f, y = 1f, z = 0f)
)
}
Node(node = line)
}
Path (polyline)¶
val points = remember {
(0..100).map { i ->
val t = i / 100f * Math.PI.toFloat() * 4
Position(
x = t * 0.1f - 2f,
y = sin(t) * 0.5f,
z = cos(t) * 0.5f
)
}
}
SceneView(engine = engine, modelLoader = modelLoader) {
val path = remember(engine, points) {
PathNode(engine = engine, points = points)
}
Node(node = path)
}
Closed path (polygon)¶
val triangle = listOf(
Position(0f, 1f, 0f),
Position(-1f, -0.5f, 0f),
Position(1f, -0.5f, 0f)
)
val closedPath = remember(engine) {
PathNode(engine = engine, points = triangle, closed = true)
}
Text labels¶
TextNode renders text to a bitmap and displays it on a quad that turns toward the camera
when it is given a cameraPositionProvider.
val cameraNode = rememberCameraNode(engine)
SceneView(
engine = engine,
modelLoader = modelLoader,
cameraNode = cameraNode
) {
TextNode(
text = "Hello 3D!",
fontSize = 48f,
textColor = android.graphics.Color.WHITE,
backgroundColor = 0xCC000000.toInt(),
typeface = Typeface.DEFAULT_BOLD,
widthMeters = 0.6f,
heightMeters = 0.2f,
// Read by the node on each frame — no Compose state, no recomposition.
cameraPositionProvider = { cameraNode.worldPosition }
)
}
Parameters¶
| Parameter | Effect |
|---|---|
text |
The string to display |
fontSize |
Font size in pixels for the bitmap texture |
textColor |
ARGB text colour |
backgroundColor |
ARGB background fill |
typeface |
android.graphics.Typeface of the text (default Typeface.DEFAULT_BOLD) |
widthMeters / heightMeters |
Size of the quad in world space (read once, at creation) |
position / scale |
Local transform of the label |
cameraPositionProvider |
Lambda returning camera position — label faces the camera. Without it the label does not turn |
The backing bitmap is 512 × 128 px. bitmapWidth / bitmapHeight are not parameters of the
composable: they belong to the constructor of the node class, io.github.sceneview.node.TextNode.
Positioning labels¶
Set the label's position like any node:
TextNode(
text = "Earth",
position = Position(x = 0f, y = 2f, z = 0f),
cameraPositionProvider = { cameraNode.worldPosition }
)
Combining lines and labels¶
A common pattern is annotating a 3D scene with measurement lines and labels:
SceneView(engine = engine, modelLoader = modelLoader, cameraNode = cameraNode) {
// Measurement line
val measureLine = remember(engine) {
LineNode(engine, start = Position(-1f, 0f, 0f), end = Position(1f, 0f, 0f))
}
Node(node = measureLine)
// Label at midpoint
TextNode(
text = "2.0 m",
fontSize = 36f,
widthMeters = 0.4f,
heightMeters = 0.15f,
position = Position(0f, 0.2f, 0f),
cameraPositionProvider = { cameraNode.worldPosition }
)
}
What's next¶
- See the
line-pathsample for animated sine/Lissajous curves with parameter sliders - See the
text-labelssample for an interactive solar system with label cycling