The loader module provides classes for loading 3D models, textures, and other assets.
import io.materia.loader.*
Abstract base class for all loaders.
| Property | Type | Description |
|---|---|---|
manager |
LoadingManager |
Loading manager |
crossOrigin |
String |
CORS setting |
withCredentials |
Boolean |
Include credentials |
path |
String |
Base path for assets |
resourcePath |
String |
Resource path |
requestHeader |
Map<String, String> |
HTTP headers |
// Set base path
fun setPath(path: String): Loader
// Set resource path
fun setResourcePath(path: String): Loader
// Set cross-origin
fun setCrossOrigin(crossOrigin: String): Loader
// Set request headers
fun setRequestHeader(headers: Map<String, String>): Loader
Loads glTF 2.0 JSON files and GLB 2.0 binary containers into Materia scene objects.
class GLTFLoader(
resolver: AssetResolver = AssetResolver.default(),
json: Json = Json { ignoreUnknownKeys = true; isLenient = true },
cache: GLTFAssetCache? = GLTFAssetCache.shared,
cacheScope: String? = resolver.cacheKeyScope
)
By default, loaders using the platform default AssetResolver share GLTFAssetCache.shared. Repeated loads for the same normalized URL reuse completed and in-flight source assets, then return an independent instance to the caller. Custom resolvers are isolated by default unless a shared cacheScope is supplied.
suspend fun load(
url: String,
progress: ((LoadingProgress) -> Unit)? = null
): GLTFAsset
suspend fun load(
url: String,
onLoad: (GLTFAsset) -> Unit,
onProgress: ((LoadingProgress) -> Unit)? = null,
onError: ((Throwable) -> Unit)? = null
)
data class GLTFAsset(
val scene: Scene,
val scenes: List<Scene>,
val nodes: List<Object3D>,
val materials: List<Material>,
val animations: List<AnimationClip>
) {
fun instantiate(): GLTFAsset
}
instantiate() creates a new scene graph with independent object transforms and parent/child relationships. Geometry, materials, textures, and animations are shared intentionally so renderers can reuse CPU and GPU resources for repeated model instances.
class GLTFAssetCache {
suspend fun getOrLoad(scope: String, url: String, load: suspend () -> GLTFAsset): GLTFAsset
suspend fun remove(scope: String, url: String)
suspend fun clear()
companion object {
val shared: GLTFAssetCache
}
}
Use a custom cache for bounded lifetimes such as level loads, tests, or editor previews:
val cache = GLTFAssetCache()
val loader = GLTFLoader(cache = cache, cacheScope = "warehouse-level")
val crateA = loader.load("models/crate.glb")
val crateB = loader.load("models/crate.glb")
scene.add(crateA.scene)
scene.add(crateB.scene)
// Safe: object transforms are not shared between instances.
crateA.scene.position.x = -2f
crateB.scene.position.x = 2f
// Optional cleanup when the scope is no longer useful.
cache.clear()
data class LoadingProgress(
val loaded: Long,
val total: Long
) {
val percentage: Float
}
Progress currently reports dependent buffer byte loading. Cached source hits may not replay the original progress sequence because no new network/decode work is performed.
.gltf JSON documents are supported with embedded data URIs and external buffers..glb binary containers are supported for glTF 2.0 JSON plus embedded binary buffer chunks..gltf and .glb loads share source asset work when cache scope and URL match.Loads Wavefront OBJ files.
class OBJLoader(manager: LoadingManager = DefaultLoadingManager)
// Load OBJ file
fun load(
url: String,
onLoad: (Group) -> Unit,
onProgress: ((ProgressEvent) -> Unit)? = null,
onError: ((Exception) -> Unit)? = null
)
// Set materials
fun setMaterials(materials: MTLLoader.MaterialCreator): OBJLoader
// Parse OBJ string
fun parse(text: String): Group
val mtlLoader = MTLLoader()
val objLoader = OBJLoader()
// Load with materials
mtlLoader.load("models/model.mtl") { materials ->
materials.preload()
objLoader.setMaterials(materials)
objLoader.load("models/model.obj") { obj ->
scene.add(obj)
}
}
Loads FBX files (ASCII format, static geometry only).
class FBXLoader(manager: LoadingManager = DefaultLoadingManager)
fun load(
url: String,
onLoad: (Group) -> Unit,
onProgress: ((ProgressEvent) -> Unit)? = null,
onError: ((Exception) -> Unit)? = null
)
fun parse(buffer: ArrayBuffer, path: String): Group
val loader = FBXLoader()
loader.load("models/character.fbx") { fbx ->
fbx.scale.setScalar(0.01f) // FBX often uses cm
scene.add(fbx)
// Animations
if (fbx.animations.isNotEmpty()) {
val mixer = AnimationMixer(fbx)
mixer.clipAction(fbx.animations[0]).play()
}
}
Loads COLLADA (.dae) files.
class ColladaLoader(manager: LoadingManager = DefaultLoadingManager)
fun load(
url: String,
onLoad: (Collada) -> Unit,
onProgress: ((ProgressEvent) -> Unit)? = null,
onError: ((Exception) -> Unit)? = null
)
data class Collada(
val scene: Group,
val animations: List<AnimationClip>,
val kinematics: Any?,
val library: ColladaLibrary
)
Loads PLY (Polygon File Format) files.
class PLYLoader(manager: LoadingManager = DefaultLoadingManager)
fun load(
url: String,
onLoad: (BufferGeometry) -> Unit,
onProgress: ((ProgressEvent) -> Unit)? = null,
onError: ((Exception) -> Unit)? = null
)
fun parse(data: ArrayBuffer): BufferGeometry
val loader = PLYLoader()
loader.load("models/scan.ply") { geometry ->
geometry.computeVertexNormals()
val material = MeshStandardMaterial().apply {
vertexColors = true
}
val mesh = Mesh(geometry, material)
scene.add(mesh)
}
Loads STL (Stereolithography) files.
class STLLoader(manager: LoadingManager = DefaultLoadingManager)
fun load(
url: String,
onLoad: (BufferGeometry) -> Unit,
onProgress: ((ProgressEvent) -> Unit)? = null,
onError: ((Exception) -> Unit)? = null
)
fun parse(data: ArrayBuffer): BufferGeometry
val loader = STLLoader()
loader.load("models/part.stl") { geometry ->
val material = MeshPhongMaterial().apply {
color = Color(0x888888)
specular = Color(0x111111)
shininess = 200f
}
val mesh = Mesh(geometry, material)
mesh.rotation.x = -PI / 2 // STL often Z-up
scene.add(mesh)
}
Loads Draco-compressed geometry.
class DRACOLoader(manager: LoadingManager = DefaultLoadingManager)
// Set decoder path
fun setDecoderPath(path: String): DRACOLoader
// Set decoder config
fun setDecoderConfig(config: DracoDecoderConfig): DRACOLoader
// Enable/disable worker
fun setWorkerLimit(limit: Int): DRACOLoader
// Preload decoder
fun preload(): DRACOLoader
// Load compressed file
fun load(
url: String,
onLoad: (BufferGeometry) -> Unit,
onProgress: ((ProgressEvent) -> Unit)? = null,
onError: ((Exception) -> Unit)? = null
)
// Dispose workers
fun dispose()
val dracoLoader = DRACOLoader()
dracoLoader.setDecoderPath("libs/draco/")
dracoLoader.setWorkerLimit(4)
dracoLoader.preload()
// Use with GLTFLoader
val gltfLoader = GLTFLoader()
gltfLoader.setDRACOLoader(dracoLoader)
Loads image textures.
class TextureLoader(manager: LoadingManager = DefaultLoadingManager)
fun load(
url: String,
onLoad: ((Texture) -> Unit)? = null,
onProgress: ((ProgressEvent) -> Unit)? = null,
onError: ((Exception) -> Unit)? = null
): Texture
suspend fun loadAsync(url: String): Texture
val loader = TextureLoader()
// Sync (texture updates when loaded)
val texture = loader.load("textures/wood.jpg")
material.map = texture
// With callback
loader.load("textures/wood.jpg") { texture ->
texture.wrapS = TextureWrapping.REPEAT
texture.wrapT = TextureWrapping.REPEAT
texture.repeat.set(4f, 4f)
material.map = texture
material.needsUpdate = true
}
// Load multiple
val textures = listOf(
"albedo.jpg",
"normal.jpg",
"roughness.jpg"
).map { loader.load("textures/pbr/$it") }
Loads cube map textures (skyboxes, environment maps).
class CubeTextureLoader(manager: LoadingManager = DefaultLoadingManager)
fun load(
urls: Array<String>, // [px, nx, py, ny, pz, nz]
onLoad: ((CubeTexture) -> Unit)? = null,
onProgress: ((ProgressEvent) -> Unit)? = null,
onError: ((Exception) -> Unit)? = null
): CubeTexture
val loader = CubeTextureLoader()
// Load skybox
val cubeTexture = loader.load(arrayOf(
"textures/sky/px.jpg",
"textures/sky/nx.jpg",
"textures/sky/py.jpg",
"textures/sky/ny.jpg",
"textures/sky/pz.jpg",
"textures/sky/nz.jpg"
)) { texture ->
scene.background = texture
scene.environment = texture // For reflections
}
Low-level image loader that returns raw pixel data.
class ImageLoader(
resolver: AssetResolver = AssetResolver.default(),
manager: LoadingManager? = null
)
// Load image data
suspend fun load(path: String): ImageData
suspend fun load(path: String, onProgress: ((LoadingProgress) -> Unit)?): ImageData
// Cache management
fun clearCache()
fun uncache(path: String)
data class ImageData(
val width: Int,
val height: Int,
val data: ByteArray // RGBA pixel data
)
val loader = ImageLoader()
val image = loader.load("textures/sprite.png")
println("Loaded ${image.width}x${image.height} image")
// Use for custom processing
val pixels = image.data
Loads HDR cube textures for environment mapping.
class HDRCubeTextureLoader(
resolver: AssetResolver = AssetResolver.default(),
manager: LoadingManager? = null
)
// Load from 6 face images
suspend fun load(
paths: Array<String>, // [+X, -X, +Y, -Y, +Z, -Z]
onProgress: ((Int, Int) -> Unit)? = null
): CubeTexture
// Load from equirectangular HDR
suspend fun loadEquirectangular(
path: String,
faceSize: Int = 512
): CubeTexture
val loader = HDRCubeTextureLoader()
// Load from 6 HDR faces
val envMap = loader.load(arrayOf(
"px.hdr", "nx.hdr",
"py.hdr", "ny.hdr",
"pz.hdr", "nz.hdr"
))
scene.environment = envMap
// Or from equirectangular panorama
val envMap2 = loader.loadEquirectangular("studio.hdr")
Loads KTX2 compressed textures.
class KTX2Loader(manager: LoadingManager = DefaultLoadingManager)
fun setTranscoderPath(path: String): KTX2Loader
fun setWorkerLimit(limit: Int): KTX2Loader
fun detectSupport(renderer: Renderer): KTX2Loader
fun load(
url: String,
onLoad: ((CompressedTexture) -> Unit),
onProgress: ((ProgressEvent) -> Unit)? = null,
onError: ((Exception) -> Unit)? = null
)
fun dispose()
val ktx2Loader = KTX2Loader()
ktx2Loader.setTranscoderPath("libs/basis/")
ktx2Loader.detectSupport(renderer)
ktx2Loader.load("textures/compressed.ktx2") { texture ->
material.map = texture
}
Loads OpenEXR HDR images.
class EXRLoader(manager: LoadingManager = DefaultLoadingManager)
fun setDataType(type: TextureDataType): EXRLoader
fun load(
url: String,
onLoad: ((DataTexture) -> Unit),
onProgress: ((ProgressEvent) -> Unit)? = null,
onError: ((Exception) -> Unit)? = null
)
val loader = EXRLoader()
loader.setDataType(TextureDataType.HALF_FLOAT)
loader.load("textures/environment.exr") { texture ->
texture.mapping = TextureMapping.EQUIRECTANGULAR_REFLECTION
scene.environment = texture
scene.background = texture
}
Loads Radiance HDR (.hdr) images.
class RGBELoader(manager: LoadingManager = DefaultLoadingManager)
fun setDataType(type: TextureDataType): RGBELoader
fun load(
url: String,
onLoad: ((DataTexture) -> Unit),
onProgress: ((ProgressEvent) -> Unit)? = null,
onError: ((Exception) -> Unit)? = null
)
val loader = RGBELoader()
loader.load("textures/environment.hdr") { texture ->
texture.mapping = TextureMapping.EQUIRECTANGULAR_REFLECTION
scene.environment = texture
// Convert to cube map for better performance
val pmremGenerator = PMREMGenerator(renderer)
val envMap = pmremGenerator.fromEquirectangular(texture).texture
scene.environment = envMap
texture.dispose()
pmremGenerator.dispose()
}
Loads JSON font files for TextGeometry.
class FontLoader(manager: LoadingManager = DefaultLoadingManager)
fun load(
url: String,
onLoad: (Font) -> Unit,
onProgress: ((ProgressEvent) -> Unit)? = null,
onError: ((Exception) -> Unit)? = null
)
fun parse(json: JsonObject): Font
val loader = FontLoader()
loader.load("fonts/helvetiker_regular.typeface.json") { font ->
val geometry = TextGeometry("Hello", TextGeometryOptions(
font = font,
size = 1f,
height = 0.2f
))
val mesh = Mesh(geometry, material)
scene.add(mesh)
}
Manages loading progress across multiple loaders.
class LoadingManager(
onLoad: (() -> Unit)? = null,
onProgress: ((url: String, loaded: Int, total: Int) -> Unit)? = null,
onError: ((url: String) -> Unit)? = null
)
| Property | Type | Description |
|---|---|---|
onStart |
((url: String, loaded: Int, total: Int) -> Unit)? |
Start callback |
onLoad |
(() -> Unit)? |
All loaded callback |
onProgress |
((url: String, loaded: Int, total: Int) -> Unit)? |
Progress callback |
onError |
((url: String) -> Unit)? |
Error callback |
// Get/set URL modifier
fun setURLModifier(callback: ((url: String) -> String)?): LoadingManager
// Add/remove handlers
fun addHandler(regex: Regex, loader: Loader): LoadingManager
fun removeHandler(regex: Regex): LoadingManager
// Get handler for URL
fun getHandler(file: String): Loader?
// Resolve URL
fun resolveURL(url: String): String
// Item tracking
fun itemStart(url: String)
fun itemEnd(url: String)
fun itemError(url: String)
// Create manager with callbacks
val manager = LoadingManager(
onLoad = {
println("All assets loaded!")
hideLoadingScreen()
},
onProgress = { url, loaded, total ->
val progress = loaded.toFloat() / total * 100
updateLoadingBar(progress)
},
onError = { url ->
println("Error loading: $url")
}
)
// Use with loaders
val gltfLoader = GLTFLoader(manager)
val textureLoader = TextureLoader(manager)
// Load assets
gltfLoader.load("models/scene.glb") { /* ... */ }
textureLoader.load("textures/diffuse.jpg") { /* ... */ }
textureLoader.load("textures/normal.jpg") { /* ... */ }
// onLoad fires when all three complete
Low-level file loading.
class FileLoader(manager: LoadingManager = DefaultLoadingManager)
fun load(
url: String,
onLoad: (String) -> Unit,
onProgress: ((ProgressEvent) -> Unit)? = null,
onError: ((Exception) -> Unit)? = null
)
fun setResponseType(type: String): FileLoader // "text", "arraybuffer", "blob", "json"
fun setMimeType(mimeType: String): FileLoader
