@@ -43,8 +43,9 @@ Feel free to join the [Discord Server](https://discord.gg/GvsJj2Pk3K)!
4343- [ Creative Coding] ( https://fabmax.github.io/kool/kool-js/?demo=creative-coding ) : A few relatively simple demos
4444 showcasing different techniques of generating procedural geometry.
4545- [ Procedural Geometry] ( https://fabmax.github.io/kool/kool-js/?demo=procedural ) : Small test-case for
46- procedural geometry; all geometry is generated in code (even the roses! Textures are regular images though). Also, some glass
47- shading (shaft of the wine glass, the wine itself looks quite odd when shaded with refractions and is therefore opaque).
46+ procedural geometry; all geometry is generated in code (even the roses! Textures are regular images though). Also,
47+ some glass shading (shaft of the wine glass, the wine itself looks quite odd when shaded with refractions and is
48+ therefore opaque).
4849- [ glTF Models] ( https://fabmax.github.io/kool/kool-js/?demo=gltf ) : Various demo models loaded from glTF / glb format
4950 - Flight Helmet from [ glTF sample models] ( https://github.com/KhronosGroup/glTF-Sample-Models/tree/master/2.0/FlightHelmet )
5051 - Polly from [ Blender] ( https://github.com/KhronosGroup/glTF-Blender-Exporter/tree/master/polly )
@@ -172,13 +173,13 @@ fun main() = KoolApplication { ctx ->
172173 }
173174
174175 // Load a glTF 2.0 model
175- Assets .launch {
176+ launchOnMainThread {
176177 val materialCfg = GltfFile .ModelMaterialConfig (
177178 shadowMaps = listOf (shadowMap),
178179 scrSpcAmbientOcclusionMap = aoPipeline.aoMap
179180 )
180181 val modelCfg = GltfFile .ModelGenerateConfig (materialConfig = materialCfg)
181- val model = loadGltfModel(" path/to/model.glb" , modelCfg)
182+ val model = Assets . loadGltfModel(" path/to/model.glb" , modelCfg)
182183
183184 model.transform.translate(0f , 0.5f , 0f )
184185 if (model.animations.isNotEmpty()) {
@@ -188,10 +189,8 @@ fun main() = KoolApplication { ctx ->
188189 }
189190 }
190191
191- // Add model to scene, use RenderLoop coroutine context to make sure insertion happens at a safe time
192- withContext(Dispatchers .RenderLoop ) {
193- addNode(model)
194- }
192+ // Add loaded model to scene
193+ addNode(model)
195194 }
196195 }
197196}
@@ -212,17 +211,18 @@ use the ambient occlusion and shadow maps we created before. Moreover, the shade
212211attribute, but a simple pre-defined color (white in this case).
213212
214213Finally, we want to load a glTF 2.0 model. Resources are loaded via the ` Assets ` object. Since resource loading is a
215- potentially long-running operation we do that from within a coroutine launched with the asset manager:
216- ` Assets.launch { ... } ` . By default, the built-in glTF parser creates shaders for all models it loads. The
214+ potentially long-running operation we do that from within a coroutine launched with ` launchOnMainThread { ... } ` .
215+ By default, the built-in glTF parser creates shaders for all models it loads. The
217216created shaders can be customized via a provided material configuration, which we use to pass the shadow and
218217ambient occlusion maps we created during light setup. After we created the custom model / material configuration
219- we can load the model with ` loadGltfModel("path/to/model.glb", modelCfg) ` . This ( suspending) function returns the
218+ we can load the model with ` Assets. loadGltfModel("path/to/model.glb", modelCfg)` . This suspending function returns the
220219loaded model, which can then be customized and inserted into the scene. Here we move the model 0.5 units along the
221220y-axis (up). If the model contains any animations, these can be easily activated. This example checks whether there
222221are any animations and if so activates the first one. The ` model.onUpdate { } ` block is executed on every frame and
223- updates the enabled animation. The model is inserted into the scene with ` addNode(model) ` , however we do that from
224- the ` Dispatchers.RenderLoop ` context to avoid threading issues (on JVM the asset loader coroutines run on a different
225- thread than the render-loop).
222+ updates the enabled animation. The model is inserted into the scene with ` addNode(model) ` . Calling ` addNode(model) `
223+ from within the coroutine is fine, since the coroutine is launched via ` launchOnMainThread { ... } ` and therefor
224+ is executed by the main render thread. If a different coroutine context / thread were used, we had to be careful to
225+ not modify the scene content while it is rendered.
226226
227227The resulting scene looks like [ this] ( https://fabmax.github.io/kool/kool-js/?demo=helloGltf ) . Here, the
228228[ Animated Box] ( https://github.com/KhronosGroup/glTF-Sample-Models/tree/master/2.0/BoxAnimated ) from the glTF sample
@@ -264,8 +264,6 @@ controlled by their `modifier`s.
264264
265265Whenever the button is clicked we increment a ` clickCount ` which is then displayed by the text field. This works
266266because the ` Panel ` -block is executed each time any ` remember ` ed state (or ` mutableStateOf() ` ) within the block changes.
267- The mechanics behind that are somewhat similar to how Jetpack-Compose works, although my implementation is much less
268- sophisticated. On the plus-side we don't need a dedicated compiler-plugin and there is a bit less magic involved.
269267
270268The resulting scene looks like [ this] ( https://fabmax.github.io/kool/kool-js/?demo=hello-ui ) .
271269
@@ -274,15 +272,57 @@ More complex layouts can be created by nesting `Row { }` and `Column { }` object
274272
275273## Kool Shader Language
276274
277- I'm currently working on my own shader language (called ksl), which is implemented as a
275+ Kool comes with its own shader language (called ksl), which is implemented as a
278276[ Kotlin Type-safe builder / DSL] ( https://kotlinlang.org/docs/type-safe-builders.html ) . The ksl shader code you write is
279277used to generate the actual GLSL shader code. The benefit with this approach is that there is no hard-coded GLSL
280278code in common code, and it should be relatively easy to add different generators which generate shader code for
281279different backends in the future (e.g. WGSL, or metal).
282280
283- This is still work in progress. However, in case you are curious, you can take a look at
284- [ KslLitShader] ( kool-core/src/commonMain/kotlin/de/fabmax/kool/modules/ksl/KslLitShader.kt ) , which
285- uses the new ksl approach (the interesting stuff happens in the ` LitShaderModel ` inner class).
281+ Writing shaders in ksl is very similar to GLSL, here's how a hello-world style shader looks like:
282+
283+ ``` kotlin
284+ fun main () = KoolApplication { ctx ->
285+ ctx.scenes + = scene {
286+ defaultOrbitCamera()
287+
288+ addColorMesh {
289+ generate {
290+ cube {
291+ colored()
292+ }
293+ }
294+ shader = KslShader (" Hello world shader" ) {
295+ val interStageColor = interStageFloat4()
296+ vertexStage {
297+ main {
298+ val mvp = mvpMatrix()
299+ val localPosition = float3Var(vertexAttribFloat3(Attribute .POSITIONS ))
300+ outPosition set mvp.matrix * float4Value(localPosition, 1f .const)
301+ interStageColor.input set vertexAttribFloat4(Attribute .COLORS )
302+ }
303+ }
304+ fragmentStage {
305+ main {
306+ colorOutput(interStageColor.output)
307+ }
308+ }
309+ }
310+ }
311+ }
312+ }
313+ ```
314+ The interesting part starts at ` shader = KslShader() = { ... } ` . Here a new shader is created and assigned to the mesh
315+ created before. If you ever wrote a shader before the structure should be familiar: The shader consists of a vertex
316+ stage (responsible for projecting the individual mesh vertices onto the screen) and a fragment stage (responsible
317+ for computing the output-color for each pixel covered by the mesh). This example shader is almost as simple as a valid
318+ shader can be: It uses a pre-multiplied MVP matrix to project the vertex position attribute to the screen. Moreover,
319+ the color attribute is taken from the vertex input and forwarded to the fragment shader via ` interStageColor ` . The
320+ fragment stage then simply takes the color from ` interStageColor ` and writes it to the screen.
321+
322+ A little more complex example is available in [ HelloKsl] ( kool-demo/src/commonMain/kotlin/de/fabmax/kool/demo/helloworld/HelloKsl.kt ) ,
323+ which looks like [ this] ( https://fabmax.github.io/kool/kool-js/?demo=helloksl ) .
324+ Of course, shaders can get more complex than that, you can dig further into the code. All shaders currently used in kool
325+ are written in ksl.
286326
287327## Physics Simulation
288328
0 commit comments