Materia is designed as a modular Kotlin Multiplatform library that provides Three.js-equivalent 3D graphics capabilities across JVM, Web, Android, iOS, and Native platforms.
┌─────────────────────────────────────────────────────────────┐
│ Application Layer │
├─────────────────────────────────────────────────────────────┤
│ Scene Graph & Object Hierarchy │
│ Scene → Group → Mesh/Light/Camera → Object3D │
├─────────────────────────────────────────────────────────────┤
│ Core Systems │
│ ┌─────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Animation │ │ Physics │ │ XR │ │
│ └─────────────┘ └──────────────┘ └──────────────┘ │
├─────────────────────────────────────────────────────────────┤
│ Rendering Pipeline │
│ ┌──────┐ ┌──────────┐ ┌────────┐ ┌──────────────┐ │
│ │Culling│→│ Sorting │→│Batching│→│Post-Processing│ │
│ └──────┘ └──────────┘ └────────┘ └──────────────┘ │
├─────────────────────────────────────────────────────────────┤
│ Platform Abstraction (expect/actual) │
│ ┌─────────┐ ┌─────────┐ ┌──────────┐ ┌──────────┐ │
│ │ JVM │ │ Web │ │ Android │ │ iOS │ │
│ │ Vulkan │ │ WebGPU │ │ Vulkan │ │MoltenVK │ │
│ └─────────┘ └─────────┘ └──────────┘ └──────────┘ │
└─────────────────────────────────────────────────────────────┘
materia-core
materia-renderer
materia-scene
materia-geometry
materia-material
materia-animation
materia-physics
materia-xr
materia-loader
.dae loader (triangle meshes)materia-postprocessing
materia-controls
materia-validation
tools/editor
tools/profiler
Materia uses Kotlin's expect/actual mechanism for platform-specific code:
// Common code (expect declaration)
expect class Renderer {
fun render(scene: Scene, camera: Camera)
fun setSize(width: Int, height: Int)
}
// JVM implementation (actual)
actual class Renderer {
private val vkRenderer = VulkanRenderer()
actual fun render(scene: Scene, camera: Camera) {
vkRenderer.render(scene, camera)
}
actual fun setSize(width: Int, height: Int) {
vkRenderer.setSize(width, height)
}
}
// Web implementation (actual)
actual class Renderer {
private val webgpuRenderer = WebGPURenderer()
actual fun render(scene: Scene, camera: Camera) {
webgpuRenderer.render(scene, camera)
}
actual fun setSize(width: Int, height: Int) {
webgpuRenderer.setSize(width, height)
}
}
JVM (Vulkan)
Web (WebGPU)
Android (Vulkan)
iOS (MoltenVK)
1. Scene Update
├─ Update transformations (Object3D.updateMatrixWorld)
├─ Update animations (AnimationMixer.update)
├─ Update physics (PhysicsWorld.step)
└─ Update XR (XRSession.update)
2. Frustum Culling
├─ Extract camera frustum
├─ Test bounding volumes
└─ Mark visible objects
3. Render List Construction
├─ Collect visible meshes
├─ Sort by material/depth
└─ Batch draw calls
4. Shadow Pass (if enabled)
├─ Render from light perspective
├─ Generate shadow maps
└─ Store depth textures
5. Main Render Pass
├─ Clear framebuffer
├─ Render opaque objects (front-to-back)
├─ Render transparent objects (back-to-front)
└─ Render overlays
6. Post-Processing (if enabled)
├─ Render to framebuffer
├─ Apply effects (bloom, SSAO, etc.)
└─ Tone mapping and color grading
7. Present
├─ Resolve multisampling
├─ Swap buffers
└─ Submit to display
Materia uses WGSL (WebGPU Shading Language) as the source shader language:
WGSL Source → Shader Compiler → Platform Shader
├─ SPIR-V (Vulkan)
├─ WGSL (WebGPU)
└─ MSL (Metal/iOS)
Shader compilation happens at runtime with caching:
// Creation
val geometry = BoxGeometry(1f, 1f, 1f) // CPU data
val material = MeshStandardMaterial() // CPU data
val mesh = Mesh(geometry, material) // Scene graph node
// GPU Upload (lazy)
renderer.render(scene, camera) // Uploads on first render
// Modification
mesh.geometry.attributes.position.needsUpdate = true
// Disposal
mesh.geometry.dispose() // Free GPU memory
mesh.material.dispose() // Free GPU textures/buffers
// Vector pool for temporary calculations
private val vectorPool = ObjectPool { Vector3() }
fun computeNormal(a: Vector3, b: Vector3, c: Vector3): Vector3 {
val v1 = vectorPool.acquire()
val v2 = vectorPool.acquire()
try {
v1.subVectors(b, a)
v2.subVectors(c, a)
return v1.cross(v2).normalize()
} finally {
vectorPool.release(v1)
vectorPool.release(v2)
}
}
class Object3D {
private var matrixNeedsUpdate = true
private var worldMatrixVersion = 0
fun updateMatrixWorld(force: Boolean = false) {
// Skip update if nothing changed
if (!force && !matrixNeedsUpdate) return
// Update only when needed
if (matrixNeedsUpdate) {
updateMatrix()
matrixNeedsUpdate = false
worldMatrixVersion++
}
// Propagate to children only if changed
if (worldMatrixVersion != lastWorldMatrixVersion) {
children.forEach { it.updateMatrixWorld() }
}
}
}
fun render(scene: Scene, camera: Camera) {
// Extract frustum from camera
val frustum = Frustum.fromProjectionMatrix(camera.projectionMatrix)
// Test objects against frustum
scene.traverse { obj ->
if (obj is Mesh) {
val bounds = obj.geometry.boundingBox
if (frustum.intersectsBox(bounds)) {
renderList.add(obj)
}
}
}
}
// Group objects by material
val batches = renderList.groupBy { it.material }
// Render in batches
batches.forEach { (material, meshes) ->
bindMaterial(material)
meshes.forEach { mesh ->
setTransform(mesh.matrixWorld)
draw(mesh.geometry)
}
}
GPU-Driven Rendering
Ray Tracing Support
Streaming System
Multi-Threading