GPU 3D Scenes
Load a model, instantiate its scene and display it in an interactive OpenGL viewport. Scene objects, mathematics and model codecs are implemented in Klyn.
A Robotic Face
Run klyn samples/graphics3d/RobotFace.kn. The original cobalt, pearl-white and crimson armored helmet,
robot-face.glb, lives beside the sample. Its head and neck are closed solids,
with complete side and rear armor. The earlier design is saved as
robot-face-original.glb beside it. Hold the primary mouse button and drag
to orbit; use the wheel to zoom. This is the complete example:
import klyn.graphics3d
import klyn.graphics3d.assets
import klyn.gui.windows
import klyn.gui.windows.graphics3d
import klyn.gui.windows.layouts
import klyn.io
# The model lives beside this sample, independently of the launch directory.
model = FilePath(FolderPath(Application.properties["klyn.sourceDirectory"]), "robot-face.glb")
asset = ModelLoader().load(model)
scene = Scene()
scene.ambient = 0.20f
scene.root.add(asset.instantiate())
window = MainWindow("Aster — robotic face")
window.size = (1000, 800)
layout = VBoxLayout(spacing=8)
window.centralWidget.layout = layout
layout.add(Label("Drag with the left mouse button to orbit · Wheel to zoom"))
view = SceneView(scene)
layout.add(view)
view.frameScene()
OrbitController(view.camera).rotate(0.35, 0.0)
try:
window.run()
finally:
view.close()
python3 tools/graphics3d/generate_robot_face.py.
The generator validates outward winding and closed manifold edges.
tools/graphics3d/generate_robot_face_original.py reproduces the preserved earlier model.Packages and Conventions
klyn.math.Quaternion: Hamilton quaternions with scalar component first.klyn.math.geometry: immutableRotation3dand column-majorMatrix4, using column vectors and right-handed rotations in radians.A * Bapplies B first.klyn.graphics3d: immutable Mesh, Material and LinearColor; mutable Scene, SceneNode, PerspectiveCamera and OrbitController; Renderer.klyn.graphics3d.assets: CPU-only loading and export.klyn.gui.windows.graphics3d.SceneView: viewport integration and mouse controls.
The scene API has no GUI dependency. Mesh vertices contain eight Float values each: position XYZ, normal XYZ and UV. UInt indices describe counter-clockwise triangles. Materials use linear RGB, opaque metallic/roughness shading, emission and an optional double-sided flag.
The current renderer uses OpenGL 3.3 or newer on Windows and Linux/X11.
GPU device selection belongs to the OS driver. Offscreen Renderer use also requires a display
with this desktop backend. WebGL, Vulkan and CUDA/OpenGL resource interoperability are not implemented.
The internal klyn.internal.gpu buffer contract is shared with numeric runtime transfers;
it is not a public application API.
Load, Instantiate and Export
import klyn.graphics3d.assets
import klyn.io
asset = ModelLoader().load(FilePath("robot.glb"))
instance = asset.instantiate()
report = ModelExporter().save(asset, FilePath("copy.glb"))
report = ModelExporter().save(asset, FilePath("copy.obj"), allowLoss=true)
for loss in report.losses:
print(loss)
Every load returns a ModelAsset with mesh/material catalogs and scene templates. Instantiation copies nodes while sharing immutable geometry and materials. An explicit scene index is required if several scenes exist without a declared default. File extensions select formats; both synchronous and asynchronous loads accept an explicit ModelFormat.
Export rejects unaccepted semantic losses before opening the destination. Flat formats require exactly one scene and report baked transforms, omitted unused catalog entries and lost material, naming or vertex attributes. OBJ also writes an adjacent MTL sidecar.
| Format | Current supported contract |
|---|---|
| glTF 2.0 / GLB 2.0 | Static indexed or non-indexed triangles; Float32 positions, normals and UV, including interleaved accessors; u8/u16/u32 indices; matrix or TRS nodes; multiple scenes; opaque untextured metallic/roughness materials. Missing normals are generated. Local relative buffers and padded Base64 data URIs are supported. |
| OBJ + MTL | Positions, normals, UV, signed indices, objects/groups, smoothing and planar polygons triangulated by ear clipping. MTL supports Kd, Ke, Pm, Pr and opaque illum 2. Other material directives and textures are explicitly rejected. |
| PLY 1.0 | ASCII geometry, optional normals and UV (u/v or s/t), planar polygon faces. Binary PLY, colors and additional elements are rejected. |
| STL | Binary and ASCII triangle geometry on load; binary export. Normals are recomputed from geometry. Binary attributes and vendor color conventions are rejected. |
glTF animations, skins, morph targets, sparse accessors, textures, cameras and extensions are explicitly rejected rather than silently discarded. Each binary resource is limited to 512 MiB; codecs also enforce index, vertex and hierarchy limits. These are static format subsets, not general-purpose support for every feature in each standard.
Background Loading
operation = ModelLoader().loadAsync(FilePath("robot.glb"))
# Poll operation.isDone, operation.progress and operation.phase from a GUI Timer.
# Once isDone is true:
asset = operation.awaitResult()
operation.close()
A worker performs CPU parsing only. Polling reads the latest progress, with no accumulating queue.
Attach the completed asset to a scene on the GUI thread, then call view.frameScene().
cancel() requests cooperative cancellation; close() cancels and joins.
awaitResult() blocks until completion and rethrows any loading failure.
GPU Lifetime and Performance
Mesh takes an immutable CPU snapshot. Its first render uploads geometry once into persistent vertex/index buffers. Subsequent frames submit matrices and material records in one native batch, with one draw call per instance. Camera movement never transforms all vertices on the CPU.
mesh.copyVertices(), mesh.copyIndices() and flat export explicitly copy
CPU data. renderer.capture(path) synchronously reads the framebuffer into a BMP.
Normal GUI frames present GPU pixels directly; GUI overlays, nonrectangular clipping and window
capture require an explicit compositor readback. renderer.statistics exposes frame,
upload, byte, readback, draw and resident-mesh counters without GPU synchronization.
Call view.repaint() after scene changes. After replacing a large scene,
view.renderer.clearMeshCache() releases unused cached geometry; the next frame
uploads the current meshes. Close SceneView or Renderer explicitly on the owning thread.
Window teardown also releases its native child surfaces.