Coder Social home page Coder Social logo

fabmax / kool Goto Github PK

View Code? Open in Web Editor NEW
253.0 14.0 15.0 40.79 MB

An OpenGL / WebGPU engine for Desktop JVM, Android and Javascript written in Kotlin

Home Page: https://fabmax.github.io/kool/kool-js

License: Apache License 2.0

Kotlin 99.86% HTML 0.03% JavaScript 0.01% CMake 0.04% C++ 0.06%
kotlin kotlin-multiplatform vulkan webgl2 opengl pbr-shading deferred-shading 3d game-development physics webgpu android

kool's Introduction

kool - An OpenGL / WebGPU graphics engine written in Kotlin

License Maven Central Build

A multi-platform OpenGL / WebGPU / Vulkan game engine that works on Desktop Java, Android and browsers.

This is very much a code-only game engine. I recommend taking a look at the demos listed below in case you are curious (all demo source code is available in the kool-demo subproject). The library is also published on maven central, see below.

I also started working on a graphical scene editor. The editor is still in an early state and not very useful yet, but you can try the Web Demo in case you are curious. You can also take a look at the kool-editor-template in the kool-templates in case you want to play around with it a bit more.

Get In Touch

Feel free to join the Discord Server!

Demos

  • Editor: Work in progress graphical editor for this engine. Fully implemented within the engine itself.
  • Island: Height-map based island incl. some wind-affected vegetation + a basic controllable character.
  • Physics - Ragdoll: Ragdoll physics demo.
  • Physics - Vehicle: A drivable vehicle (W, A, S, D / cursor keys, R to reset) based on the Nvidia PhysX vehicles SDK.
  • Physics - Joints: Physics demo consisting of a chain running over two gears. Uses a lot of multi shapes and revolute joints.
  • Physics - Collision: The obligatory collision physics demo with various different shapes.
  • Embedded UI: Integrated UI framework implemented completely within the engine. Fast, highly customizable and easy-to-use.
  • Particles: Two teams of bees fighting against each other. Simulation can be toggled between CPU and compute-shader (if available, i.e. on WebGPU).
  • Fluffy Bunny: Shell-shading based rendering of animated fur (based on this video).
  • Creative Coding: A few relatively simple demos showcasing different techniques of generating procedural geometry.
  • Procedural Geometry: Small test-case for procedural geometry; all geometry is generated in code (even the roses! Textures are regular images though). Also, some glass shading (shaft of the wine glass, the wine itself looks quite odd when shaded with refractions and is therefore opaque).
  • glTF Models: Various demo models loaded from glTF / glb format
  • Deferred Shading: Thousands of dynamic light sources, bloom and ambient occlusion.
  • Screen-space Ambient Occlusion: Roughly based on this article by John Chapman with slightly optimized sampling (also shamelessly recreated his demo scene).
  • Screen-space Reflections: A simple PBR shaded model with screen-space reflections and up to four spot-lights with dynamic shadows.
  • Physical Based Rendering: Interactive PBR demo with image based lighting for various materials and environments (underlying PBR theory from this awesome article series).
  • Instanced / LOD Drawing: Instanced rendering demo of the Stanford Bunny. Uses six levels of detail to render up to 8000 instances.
  • Mesh Simplification: Interactive mesh simplification demo (based on traditional error-quadrics)

Code for all demos is available in kool-demo sub-project.

By default, the web demos use the WebGPU backend and fall back to WebGL if WebGPU is not supported by your browser. The used backend is printed in the extended info-panel in the lower right corner (click on the little +), apart from that there shouldn't be much visible difference in the WebGL and WebGPU backends. You can also force a certain backend by appending &backend=webgpu or &backend=webgl to the URL.

I also made an actual game with this: Blocks and Belts. Give it a try (it's free)!

Platform Support

Platform Backend Implementation Status
Desktop JVM OpenGL ✅ Fully working
Desktop JVM Vulkan ❌ Not working (under construction)
Browser WebGL 2 ✅ Fully working
Browser WebGPU ✅ Fully working
Android OpenGL ES 3 ✳️ kool-core fully working (but no physics yet)

Android Support

The Android target is disabled by default (to avoid having the Android SDK as a build requirement). You can enable the Android target by running the gradle task enableAndroidPlatform.

Moreover, Android support is only available for kool-core for now. Therefore, the demos don't work on Android yet (because they also require kool-physics). However, there's a basic kool-android-template project with a minimal kool Android app.

Usage

If you are adventurous, you can use kool as a library in your own (multiplatform-) projects. The library is published on maven central, and there is a separate repo containing a minimal template project to get you started:

https://github.com/fabmax/kool-templates

The demos mentioned above and examples shown below should give you a rough idea on how to do stuff (documentation is still a bit of a weak spot).

Running the Demos on JVM

You can launch the desktop demo app directly from a terminal via gradle with ./gradlew :kool-demo:run.

Running the main() method from within IntelliJ requires that the native libraries are located in a local folder and added as file dependencies (seems to be some kind of dependency resolution bug in IntelliJ when importing multiplatform projects with JVM runtimeOnly libraries).

The required libs are copied automatically on build. So, in order to launch the demos from within IntelliJ you need to build the project first (or manually run the cacheRuntimeLibs task) and then re-sync the gradle project, so that the libs are resolved and added to the IntelliJ module classpath.

Engine Features / Noticeable Stuff:

  • js: Interchangeable WebGL and WebGPU backends
  • Basic Compute shader support
  • Reversed-depth rendering for vastly improved depth precision and range (more or less infinite)
  • Physics simulation (based on Nvidia PhysX 5.3, using physx-jni on Java and physx-js-webidl on javascript)
  • Kotlin DSL based shader language (translates into GLSL and WGSL)
  • Neat little integrated GUI framework. The API is heavily inspired by Jetpack Compose but the implementation is different, as it needs to run within the OpenGL context.
  • MSDF Font support for text rendering in arbitrary font sizes
  • Experimental Vulkan rendering backend (on JVM)
  • Support for physical based rendering (with metallic workflow) and image-based lighting
  • (Almost) complete support for glTF 2.0 model format (including animations, morph targets and skins)
  • Skin / armature mesh animation (vertex shader based)
  • Deferred shading
  • Screen-space ambient occlusion
  • Normal, roughness, metallic, ambient occlusion and displacement mapping
  • Lighting with multiple point, spot and directional lights
  • Shadow mapping for multiple light sources (only spot and directional lights for now)
  • Basic audio support

A Hello World Example

Getting a basic scene on the screen is quite simple:

fun main() = KoolApplication { ctx ->
    ctx.scenes += scene {
        defaultOrbitCamera()

        addColorMesh {
            generate {
                cube {
                    colored()
                }
            }
            shader = KslPbrShader {
                color { vertexColor() }
                metallic(0f)
                roughness(0.25f)
            }
            onUpdate {
                transform.rotate(45f.deg * Time.deltaT, Vec3f.X_AXIS)
            }
        }

        lighting.singleDirectionalLight {
            setup(Vec3f(-1f, -1f, -1f))
            setColor(Color.WHITE, 5f)
        }
    }
}

The above example creates an application with a single scene and sets up a mouse-controlled camera (with defaultOrbitCamera()). As you might have guessed the addColorMesh { ... } block creates a colored cube and adds it to the scene. In order to draw the mesh on the screen it needs a shader, which is assigned with shader = KslPbrShader { ... }. This creates a simple PBR shader for a dielectric material with a rather smooth surface. Color information is taken from the corresponding vertex attribute. The onUpdate-block is called on each frame and modifies the cube transform to rotate it 45° per second around its X-axis. Finally, we set up a single directional scene light (of white color and an intensity of 5), so that our cube can shine in its full glory. The resulting scene looks like this.

Model Loading and Advanced Lighting

Model loading, animation and more advanced lighting with shadow mapping and ambient occlusion requires only a few more lines of code:

fun main() = KoolApplication { ctx ->
    ctx.scenes += scene {
        defaultOrbitCamera()

        // Light setup
        lighting.singleSpotLight {
            setup(Vec3f(5f, 6.25f, 7.5f), Vec3f(-1f, -1.25f, -1.5f), 45f.deg)
            setColor(Color.WHITE, 300f)
        }
        val shadowMap = SimpleShadowMap(this, lighting.lights[0])
        val aoPipeline = AoPipeline.createForward(this)

        // Add a ground plane
        addColorMesh {
            generate {
                grid { }
            }
            shader = KslPbrShader {
                color { constColor(Color.WHITE) }
                shadow { addShadowMap(shadowMap) }
                enableSsao(aoPipeline.aoMap)
            }
        }

        // Load a glTF 2.0 model
        launchOnMainThread {
            val materialCfg = GltfFile.ModelMaterialConfig(
                shadowMaps = listOf(shadowMap),
                scrSpcAmbientOcclusionMap = aoPipeline.aoMap
            )
            val modelCfg = GltfFile.ModelGenerateConfig(materialConfig = materialCfg)
            val model = Assets.loadGltfModel("path/to/model.glb", modelCfg)

            model.transform.translate(0f, 0.5f, 0f)
            if (model.animations.isNotEmpty()) {
                model.enableAnimation(0)
                model.onUpdate {
                    model.applyAnimation(Time.deltaT)
                }
            }
            
            // Add loaded model to scene
            addNode(model)
        }
    }
}

First we set up the lighting. This is very similar to the previous example but this time we use a spot-light, which requires a position, direction and opening angle. Other than directional lights, point and spot-lights have a distinct (point-) position and objects are affected less by them, the farther they are away. This usually results in a much higher required light intensity: Here we use an intensity of 300.

Next we create a SimpleShadowMap which computes the shadows cast by the light source we defined before. Moreover, the created AoPipeline computes an ambient occlusion map, which is later used by the shaders to further improve the visual appearance of the scene.

After light setup we can add objects to our scene. First we generate a grid mesh as ground plane. Default size and position of the generated grid are fine, therefore grid { } does not need any more configuration. Similar to the color cube from the previous example, the ground plane uses a PBR shader. However, this time we tell the shader to use the ambient occlusion and shadow maps we created before. Moreover, the shader should not use the vertex color attribute, but a simple pre-defined color (white in this case).

Finally, we want to load a glTF 2.0 model. Resources are loaded via the Assets object. Since resource loading is a potentially long-running operation we do that from within a coroutine launched with launchOnMainThread { ... }. By default, the built-in glTF parser creates shaders for all models it loads. The created shaders can be customized via a provided material configuration, which we use to pass the shadow and ambient occlusion maps we created during light setup. After we created the custom model / material configuration we can load the model with Assets.loadGltfModel("path/to/model.glb", modelCfg). This suspending function returns the loaded model, which can then be customized and inserted into the scene. Here we move the model 0.5 units along the y-axis (up). If the model contains any animations, these can be easily activated. This example checks whether there are any animations and if so activates the first one. The model.onUpdate { } block is executed on every frame and updates the enabled animation. The model is inserted into the scene with addNode(model). Calling addNode(model) from within the coroutine is fine, since the coroutine is launched via launchOnMainThread { ... } and therefor is executed by the main render thread. If a different coroutine context / thread were used, we had to be careful to not modify the scene content while it is rendered.

The resulting scene looks like this. Here, the Animated Box from the glTF sample repository is loaded.

Kool UI

Kool comes with an embedded UI framework, which is heavily inspired by Jetpack Compose but was implemented from scratch. Here is a small example:

fun main() = KoolApplication { ctx ->
    ctx.scenes += UiScene(clearScreen = true) {
        Panel(colors = Colors.singleColorLight(MdColor.LIGHT_GREEN)) {
            modifier
                .size(400.dp, 300.dp)
                .align(AlignmentX.Center, AlignmentY.Center)
                .background(RoundRectBackground(colors.background, 16.dp))

            var clickCount by remember(0)
            Button("Click me!") {
                modifier
                    .alignX(AlignmentX.Center)
                    .margin(sizes.largeGap * 4f)
                    .padding(horizontal = sizes.largeGap, vertical = sizes.gap)
                    .font(sizes.largeText)
                    .onClick { clickCount++ }
            }
            Text("Button clicked $clickCount times") {
                modifier
                    .alignX(AlignmentX.Center)
            }
        }
    }
}

Here, we create a new UiScene and add a Panel to it, which serves as top-level container for our UI content. Within the Panel-block, we add a button and a text field. All appearance and layout-properties of the UI elements are controlled by their modifiers.

Whenever the button is clicked we increment a clickCount which is then displayed by the text field. This works because the Panel-block is executed each time any remembered state (or mutableStateOf()) within the block changes.

The resulting scene looks like this.

More complex layouts can be created by nesting Row { } and Column { } objects. The full UI demo should give you an impression on what's possible.

Kool Shader Language

Kool comes with its own shader language (called ksl), which is implemented as a Kotlin Type-safe builder / DSL. The ksl shader code you write is used to generate the actual GLSL / WGSL shader code. The benefit with this approach is that there is no hard-coded platform-specific shader code in common code and all shaders work on OpenGL / GLSL as well as WebGPU / WGSL. Moreover, it is relatively easy to add different generators which generate shader code for different backends in the future (e.g. metal).

Writing shaders in ksl is quite similar to GLSL, here's how a hello-world style shader looks like:

fun main() = KoolApplication { ctx ->
    ctx.scenes += scene {
        defaultOrbitCamera()

        addColorMesh {
            generate {
                cube {
                    colored()
                }
            }
            shader = KslShader("Hello world shader") {
                val interStageColor = interStageFloat4()
                vertexStage {
                    main {
                        val mvp = mvpMatrix()
                        val localPosition = float3Var(vertexAttribFloat3(Attribute.POSITIONS))
                        outPosition set mvp.matrix * float4Value(localPosition, 1f.const)
                        interStageColor.input set vertexAttribFloat4(Attribute.COLORS)
                    }
                }
                fragmentStage {
                    main {
                        colorOutput(interStageColor.output)
                    }
                }
            }
        }
    }
}

The interesting part starts at shader = KslShader() = { ... }. Here a new shader is created and assigned to the mesh created before. If you ever wrote a shader before the structure should be familiar: The shader consists of a vertex stage (responsible for projecting the individual mesh vertices onto the screen) and a fragment stage (responsible for computing the output-color for each pixel covered by the mesh). This example shader is almost as simple as a valid shader can be: It uses a pre-multiplied MVP matrix to project the vertex position attribute to the screen. Moreover, the color attribute is taken from the vertex input and forwarded to the fragment shader via interStageColor. The fragment stage then simply takes the color from interStageColor and writes it to the screen.

A little more complex example is available in HelloKsl, which looks like this. Of course, shaders can get more complex than that, you can dig further into the code. All shaders currently used in kool are written in ksl.

Physics Simulation

After playing around with various different engines on javascript and JVM I came to the conclusion that all of them had some kind of flaw. So I decided to write my own bindings for Nvidia PhysX: physx-jni for JVM, and physx-js-webidl for javascript.

This was quite a bit of work (and is an ongoing project), but I think it was worth it: By writing my own bindings I get the features I need, and, even better, I get the same features for javascript and JVM, which makes the multiplatform approach much easier.

kool's People

Contributors

andrey-zakharov avatar b4rtware avatar donaldr avatar fabmax avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

kool's Issues

Keyboard input

From what I see in kool/input/PlatformInput.kt it appears that kool captures all keypresses in the document. I'm planning on using kool in a non fullscreen canvas that has other components on the page. It would be better to only capture keyboard when the canvas is in focus (using tabindex) or perhaps provide an option to do so.

Font from bitmap

I have tileset (bitmap) of chars. I do not see the way to make a font from it.
The only what i want is to provide assetPath, and valid chars as char association map with tile index (or even more precise - exact region in bitmap for not-monospaced fonts) in it. Looks like Font currently draw this tileset for itself internally, and i want to provide this tileset by asset.

Feature Request (Optional ) - GLTF/GLB combining model animations

This can be optional feature, we know that we can have all animations in a single gltf/glb file. I know this is overkill but incase we want to merge animations from different file with same model without modifying it in Blender

I'm thinking of merging the models animations in preload of gltf models.

loadGltfModel("path/to/model-walk.glb", "path/to/model-idle.glb", "path/to/model-run.glb", modelCfg)

Then we can now enable animations like this

// walk = 0, idle =1, run = 2
model.enableAnimation("walk") // idle, run, atck

[Suggestion] Better physics character + heightmap world loading

Hello fabmax, If I may suggest a better system for physics character with an (onGround detection to prevent multiple jump and flying), and without bounces glitch with rigidbody.
I think it will be way more easy for you to do it than me.

Also, I saw the noise functions (perlin noise, simplex noise and others) it is really cool !
Do you think it is possible to make an heightmap image loader as a new world demo ?

Thank your for your work
Protoxy

Default font inside KoolConfig

Hi please add font de.fabmax.kool.util.MsdfFont as one of parameters de.fabmax.kool.KoolConfig
+remove fallows:

de.fabmax.kool.util.MsdfFont.Companion.DEFAULT_FONT_DATA
de.fabmax.kool.util.MsdfFont.Companion.DEFAULT_FONT

or transfer into getters:

val de.fabmax.kool.util.MsdfFont.Companion.DEFAULT_FONT get() = de.fabmax.kool.KoolSystem.config.font
val de.fabmax.kool.util.MsdfFont.Companion.DEFAULT_FONT_DATA get() = de.fabmax.kool.util.MsdfFont.Companion.DEFAULT_FONT.data

Primary reason:
On multiple places is configurations of font loaded from default (created in static initializer) but everywhere i use custom fonts ... central europe charset.

Secondary reason:
Without image resource of default font framework do not work.

And the last one:
Is no good practise bundling json inside source code as base64.
I understand why, but better option is, separate json+png as library with no other code, just base64.

I hope I have convinced.
Thank you in advance.

java.nio.BufferOverflowException or indeces ignoring

I've suddenly faced to

Exception in thread "main" java.nio.BufferOverflowException
	at java.base/java.nio.Buffer.nextPutIndex(Buffer.java:730)
	at java.base/java.nio.DirectByteBuffer.putFloat(DirectByteBuffer.java:916)
	at de.fabmax.kool.util.MixedBufferImpl.putFloat32(Buffer.kt:289)
	at de.fabmax.kool.util.MixedBuffer$DefaultImpls.putFloat32(Buffer.kt:169)
	at de.fabmax.kool.util.MixedBufferImpl.putFloat32(Buffer.kt:190)
	at de.fabmax.kool.pipeline.Uniform2fv.putToBuffer(Uniform.kt:106)
	at de.fabmax.kool.pipeline.BufferLayout.putToBuffer(BufferLayout.kt:22)
	at de.fabmax.kool.platform.gl.MappedUbo.setUniform(MappedUniform.kt:50)
	at de.fabmax.kool.platform.gl.CompiledShader$ShaderInstance.bindInstance(CompiledShader.kt:280)
	at de.fabmax.kool.platform.gl.CompiledShader.bindInstance(CompiledShader.kt:134)
	at de.fabmax.kool.platform.gl.ShaderManager.setupShader(ShaderManager.kt:23)
	at de.fabmax.kool.platform.gl.QueueRendererGl.renderQueue(QueueRendererGl.kt:64)
	at de.fabmax.kool.platform.gl.GlRenderBackend.drawFrame(GlRenderBackend.kt:116)
	at de.fabmax.kool.platform.Lwjgl3Context.run(Lwjgl3Context.kt:124)
	at App.<init>(App.kt:35)
	at MainKt.main(main.kt:5)
	at MainKt.main(main.kt)

and looks like here

uboLayouts[desc.name] = ExternalBufferLayout(desc.uniforms, offsets, blockSize)

    private fun setupUboLayout(desc: UniformBuffer, blockIndex: Int) {
        val blockSize = glGetActiveUniformBlocki(prog, blockIndex, GL_UNIFORM_BLOCK_DATA_SIZE)
        val indices = IntArray(desc.uniforms.size)
        val offsets = IntArray(desc.uniforms.size)

        MemoryStack.stackPush().use { stack ->
            val uniformNames = desc.uniforms.map {
                if (it.length > 1) { MemoryUtil.memASCII("${it.name}[0]") } else { MemoryUtil.memASCII(it.name) }
            }.toTypedArray()
            val namePointers = stack.pointers(*uniformNames)
            glGetUniformIndices(prog, namePointers, indices)
            glGetActiveUniformsiv(prog, indices, GL_UNIFORM_OFFSET, offsets)
        }
        glUniformBlockBinding(prog, blockIndex, desc.binding)
        uboLayouts[desc.name] = ExternalBufferLayout(desc.uniforms, offsets, blockSize)
    }

(and in WebGL version too) indiced only used by gl call, and offsets is still in indices order? For example, if we have

indices = {int[2]@3664} [1, 0]
 0 = 1
 1 = 0

and offsets like

offsets = {int[2]@3665} [0, 64]
 0 = 0
 1 = 64

and uniforms like

desc.uniforms = {ArrayList@4006}  size = 2
 0 = {UniformMat4f@4008} uMvpMat
  value = {Mat4f@4013} de.fabmax.kool.math.Mat4f@24b52d3e
  name = "uMvpMat"
  size = 64
  length = 1
  isArray {boolean} 
 1 = {Uniform2fv@4009} roots
  value = {MutableVec2f[3]@4012} 
  name = "roots"
  size = 48
  length = 3
  isArray {boolean} 

it would brake??? Or i'm missing something?
Please, help! 👍

Stereo support

At least, to be able handle stereo audio, Stereo should be wrapped from JS side, and Java's Mixer from JVM.

thinking about another one AudioOutput class?

Mac M1 arm64 support

Hello, I would like to setup a development environment with kool on my M1 Macbook.
I started to change the lwjgl version from 3.2.4 to 3.3.0 which is compatible with arm64 architectures.

In Dependencies.kt -> lwjglVersion = 3.3.0-SNAPSHOT
And I select the good natives in it natives-macos-arm64.
Note: Add this repository url (https://oss.sonatype.org/content/repositories/snapshots)

I am now looking for physx-jni, to be able to build arm natives, but there is lot of stuff on the cmake.
Is it possible for you to do add the compiling process for arm64 architectures on mac ?
If you don't have a macos arm64, I can build it for you on XCode or look for the Github Workflow.

Thanks in advance !
And great work here.

(help wanted) "Attach" a model to the CharacterTrackingCamRig

Hi, I have some difficulties to "attach" an object to the camera.
I've tried different matrix transformations, but I think I'm missing something

Here is my code:
image

gunModel is a Model (you could imagine that)

Here is a the result:
https://streamable.com/c9sq26

I also tested to translate to the real camera position by translating X by 0.8

I have read different tutorials about transformations, but I can't figure it out.
Thank you very much for your work

PS: I asked the last week for an arm64 compatibility, I succeeded to build the bindings + natives, but there was too much problems to solve with opengl / lwjgl 3.3.0. I'll try to continue later, I'll content to Windows for now

Lack of support for uniform[1-4]iu?v set of gl functions

void glUniform1iv(GLint location, GLsizei count, const GLint *value);
void glUniform2iv(GLint location, GLsizei count, const GLint *value);
void glUniform3iv(GLint location, GLsizei count, const GLint *value);
void glUniform4iv(GLint location, GLsizei count, const GLint *value);
void glUniform1uiv(GLint location, GLsizei count, const GLuint *value);
void glUniform2uiv(GLint location, GLsizei count, const GLuint *value);
void glUniform3uiv(GLint location, GLsizei count, const GLuint *value);
void glUniform4uiv(GLint location, GLsizei count, const GLuint *value);

does they exists in Vulkan? IDK.
workaround: use fv with converting.

Scenes models rendering jitters when using CharacterTrackingCamRig

Hi, on the Island demo and when I use CharacterTrackingCamRig in general, the objects jitters/glitch when moving the character position.

It is maybe due of the camera.trackedPose being set directly to playerController.controller.actor.transform which is handled by the physics engine and not interpolated with frames rendering (I'm not sure).

Thanks

GLFW.glfwInit() and -XstartOnFirstThread

Hello, I wanted to make a new test on my macbook arm64.
I built PhysX with jni natives and it went good.

But I can't run the demo, even with the argument/VM option "-XstartOnFirstThread" on Intellij, it crash at start:

I really carefully check if the VM options was valid, and it if it is interpreted at the launch.
Executing task ' run -XstartOnFirstThread'

It is really strange, does it launch for you on a macos ?

Exception in thread "main" java.lang.ExceptionInInitializerError
	at org.lwjgl.glfw.GLFW.glfwInit(GLFW.java:1046)
	at de.fabmax.kool.DesktopImpl.<clinit>(JvmPlatform.kt:62)
	at de.fabmax.kool.JvmPlatformKt.createContext(JvmPlatform.kt:28)
	at de.fabmax.kool.demo.MainKt.main(Main.kt:18)
	at de.fabmax.kool.demo.MainKt.main(Main.kt)
Caused by: java.lang.IllegalStateException: GLFW may only be used on the main thread and that thread must be the first thread in the process. Please run the JVM with -XstartOnFirstThread. This check may be disabled with Configuration.GLFW_CHECK_THREAD0.
	at org.lwjgl.glfw.EventLoop.<clinit>(EventLoop.java:30)
	... 5 more
Caused by: java.lang.IllegalStateException: GLFW may only be used on the main thread and that thread must be the first thread in the process. Please run the JVM with -XstartOnFirstThread. This check may be disabled with Configuration.GLFW_CHECK_THREAD0.

Thank you in advance, I really liked the demo update of the island =)

EDIT: SOLVED
I added jvmArgs("-XstartOnFirstThread") in the gradle run task

LazyList scroll by onDrag

Hi, i have little problem of scroll by remote controller ... no wheel
Here is piece off code whitch it will fix.
Will be great have it in inside library

fun UiScope.LazyList( ... ){

    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }

    val lastTouchDrag = remember {
        MutableVec2d(Vec2d.ZERO)
    }

    Box {
        modifier
            .width(width)
            .height(height)
            .backgroundColor(colors.backgroundVariant)
            .onWheelX {
                if (isScrollableHorizontal) {
                    state.scrollDpX(it.pointer.deltaScrollX.toFloat() * -20f)
                }
            }
            .onDragStart{
                lastTouchDrag.set(it.pointer.dragDeltaX,it.pointer.dragDeltaY)
            }
            .onDragEnd {
                lastTouchDrag.set(Vec2d.ZERO)
            }
            .onDrag {
                if (isScrollableHorizontal) {
                    if(lastTouchDrag.x != it.pointer.dragDeltaX){
                        state.scrollDpX((lastTouchDrag.x- it.pointer.dragDeltaX ).toFloat())
                    }
                }
                if (isScrollableVertical) {
                    if(lastTouchDrag.y != it.pointer.dragDeltaY){
                        state.scrollDpY((lastTouchDrag.y - it.pointer.dragDeltaY ).toFloat())
                    }
                }
                lastTouchDrag.set(it.pointer.dragDeltaX,it.pointer.dragDeltaY)
            }
            .onWheelY {
                if (isScrollableVertical) {
                    state.scrollDpY(it.pointer.deltaScrollY.toFloat() * -50f)
                }
            }
    ...

ClassCastException when creating default context in JS

I've made clean Kotlin/JS project, add dependency


kotlin {
    js(IR) {
        binaries.executable()
        browser {
            commonWebpackConfig {
                cssSupport.enabled = true
            }
        }
    }

    dependencies {
        implementation("de.fabmax.kool:kool-core-js:0.8.0")
    }
}

Build and run, got ClassCastException in console:

"ClassCastException
    at THROW_CCE (webpack-internal:///./kotlin/untitled.js:21774:11)
    at new JsContext (webpack-internal:///./kotlin/untitled.js:89775:68)
    at JsImpl.createContext_u6767_k$ (webpack-internal:///./kotlin/untitled.js:86481:20)
    at createContext (webpack-internal:///./kotlin/untitled.js:86443:33)
    at createDefaultContext (webpack-internal:///./kotlin/untitled.js:86440:12)
    at main (webpack-internal:///./kotlin/untitled.js:95226:15)
    at Object.eval (webpack-internal:///./kotlin/untitled.js:95613:3)
    at eval (webpack-internal:///./kotlin/untitled.js:5:35)
    at eval (webpack-internal:///./kotlin/untitled.js:8:2)
    at Object../kotlin/untitled.js (http://localhost:8080/untitled.js:505:1)"

my versions:
kotlin: 1.6.10
webpack: 5.57.1

UI Opacity

I'm trying to animate appeareance of UI stuff by smooth revealing from transparent state to full opacity.
I could not find out any easy way to make UiSurface semitransparent. I'd prefer to have some exposed shader's uniform as alphaMultiplier.
How could i make this?

Android Support

Hi @fabmax any updates on android support? looking forward to test this library for my MMO project

Advice: guidelines for using kool for a simulation tool

Hi,
I'm not sure this is the right place to ask this,
I'm thinking about using kool for a massive molecular dynamic visualization tool,

main headline is:

  • massive number of particles (~10^6)

can you give me some advice?
is kool the right fit?
anything i should review before starting this project?
Satellite_tobacco_mosaic_virus_rendering_produced_by_VMD_and_Tachyon

iOS support

Is there some technical nuance that makes iOS support undoable? Can I suggest my contribution if no?

Video recording tap-in

Hi Max,

I would love to use this engine, but I am not seeing the plug-in points I need.

I want to be able to retrieve each frame, and record it to a video file (mp4). Where can I find the finished image after each frame? Are there best practices around this that I should be aware of?

Thanks in advance,

Touchscreen do not work

Hi,
I not sure why but touchscreen do not work on JS target (notebook with touchscreen)

I rewrite fallows, it is NOT the best solution but works:

// de.fabmax.kool.input.PlatformInputJs

    private fun Event.asTouchEvent(): TouchEvent? {
        return if (this is TouchEvent) this
        else null
    }

 private fun installInputHandlers(canvas: HTMLCanvasElement) {
        var isTouchActive = false

        fun Event.buildTouchPosition(): Vec2d? {
            preventDefault()
            return with(canvas.getBoundingClientRect()) {
                asTouchEvent()?.let { touch ->
                    if (touch.changedTouches.length == 0) null
                    else {
                        val item = touch.changedTouches.item(0)
                        Vec2d(
                            item.elementX * window.devicePixelRatio - left,
                            item.elementY * window.devicePixelRatio - top
                        )
                    }
                }
            }
        }

        fun Event.invokeMoveTouchAsMouse() {
            buildTouchPosition()?.also {
                virtualPointerPos.x = it.x
                virtualPointerPos.y = it.y
                PointerInput.handleMouseMove(virtualPointerPos.x, virtualPointerPos.y)
            }
        }

        canvas.onmousemove = { ev ->
            if (!isTouchActive) {
                val bounds = canvas.getBoundingClientRect()
                if (PointerLockState.hasPointerLock) {
                    // on active pointer lock, mouse event position is constant and only deltas are reported
                    //  -> use deltas to compute a virtual unbounded pointer position
                    virtualPointerPos.x += pointerMovementX(ev)
                    virtualPointerPos.y += pointerMovementY(ev)
                } else {
                    virtualPointerPos.x = (ev.clientX * window.devicePixelRatio - bounds.left)
                    virtualPointerPos.y = (ev.clientY * window.devicePixelRatio - bounds.top)
                }
                PointerInput.handleMouseMove(virtualPointerPos.x, virtualPointerPos.y)
            }
        }

        canvas.addEventListener("touchstart", { ev ->
            ev.preventDefault()
            isTouchActive = true
            ev.invokeMoveTouchAsMouse()
            PointerInput.handleMouseButtonEvent(0, true)
        }, false)

        canvas.addEventListener("touchend", { ev ->
            ev.preventDefault()
            ev.invokeMoveTouchAsMouse()
            PointerInput.handleMouseButtonEvent(0, false)
            isTouchActive = false
        }, false)

        canvas.addEventListener("touchcancel", { ev ->
            ev.preventDefault()
            isTouchActive = false
            ev.invokeMoveTouchAsMouse()
            PointerInput.handleMouseExit()
            isTouchActive = false
        }, false)

        canvas.addEventListener("touchmove", { ev ->
            ev.preventDefault()
            ev.asTouchEvent()?.apply {
                if (changedTouches.length == 1) {
                    ev.invokeMoveTouchAsMouse()
                }
            }
        }, false)

...

These modifications break OrbitInputTransform at touch mode
But can be easily fixed by storing initial position at touch start
and for camera transform use difference between current and initial position

NoSuchElementException when adding Ui2 Node to the scene

Hi,
I would like to make a scene editor with a lists of models objects on the scene, and gizmo when selecting a model.

But when I try to render a simple panel on my 3D scene I'm having NoSuchElementException
Do you know a simple method to render an ui window in top of a 3D scene ?

Code:

    override fun Scene.setupMainScene(ctx: KoolContext) {
        camera.clipFar = 10000f
        defaultCamTransform()

        setupUiScene(clearScreen = false)

        +Panel(colors = Colors.singleColorLight(MdColor.LIGHT_GREEN)) {
            modifier
                .size(400.dp, 300.dp)
                .align(AlignmentX.Center, AlignmentY.Center)
                .background(RoundRectBackground(colors.background, 16.dp))

            val clickCount = weakRememberState(0)
            Button("Click me!") {
                modifier
                    .alignX(AlignmentX.Center)
                    .margin(sizes.largeGap * 4f)
                    .padding(horizontal = sizes.largeGap, vertical = sizes.gap)
                    .font(sizes.largeText)
                    .onClick { clickCount.set(clickCount.value + 1) }
            }
            Text("Button clicked ${clickCount.use()} times") {
                modifier
                    .alignX(AlignmentX.Center)
            }
        }
    }

Error:

Exception in thread "main" java.util.NoSuchElementException: Mesh does not include required vertex attribute: attrib_positions
	at de.fabmax.kool.modules.ksl.KslShader.setupAttributes(KslShader.kt:182)
	at de.fabmax.kool.modules.ksl.KslShader.onPipelineSetup(KslShader.kt:28)
	at de.fabmax.kool.pipeline.Shader.createPipeline(Shader.kt:14)
	at de.fabmax.kool.pipeline.DepthMapPass.getDepthPipeline(DepthMapPass.kt:40)
	at de.fabmax.kool.pipeline.DepthMapPass.setupDrawCommand(DepthMapPass.kt:31)
	at de.fabmax.kool.util.SimpleShadowMap.setupDrawCommand(ShadowMap.kt:73)
	at de.fabmax.kool.pipeline.DepthMapPass$1.invoke(DepthMapPass.kt:25)
	at de.fabmax.kool.pipeline.DepthMapPass$1.invoke(DepthMapPass.kt:22)
	at de.fabmax.kool.pipeline.RenderPass.afterCollectDrawCommands(RenderPass.kt:124)
	at de.fabmax.kool.pipeline.RenderPass.collectDrawCommands(RenderPass.kt:81)
	at de.fabmax.kool.scene.Scene.renderScene(Scene.kt:87)
	at de.fabmax.kool.KoolContext.render(KoolContext.kt:186)
	at de.fabmax.kool.platform.Lwjgl3Context.renderFrame$kool_core(Lwjgl3Context.kt:134)
	at de.fabmax.kool.platform.Lwjgl3Context.run(Lwjgl3Context.kt:108)
	at net.ravage.engine.scenes.RavageScene.<init>(RavageScene.kt:52)
	at net.ravage.engine.world.WorldCreator.<init>(WorldCreator.kt:10)
	at net.ravage.engine.RavageEngine.<init>(RavageEngine.kt:19)
	at net.ravage.engine.RavageApplicationKt.main(RavageApplication.kt:29)
	at net.ravage.engine.RavageApplicationKt.main(RavageApplication.kt)

Feature request - "try Vulkan"

It is basically an option you can put onto the createContext() function, which sets the default rendering engine to vulkan, however, if the setup fails, for example if the GPU doesn't support vulkan, it reverts the rendering engine to OpenGL and creates a new context, closing the second one. That way the GPUs that can support vulkan will have vulkan, and those that cannot will be automatically switched to OpenGL.

Potential name would be a boolean called "autoRevert"

No issue, just wanted to say that this is awesome

Hey man, I've gotta say that out of all the graphics libraries I've tried so far this is one of, if not, the best! Great job on making this awesome 3d kotlin game engine, the only bad thing I can say about this project is the lack of tutorials 😅

kool-project.json not found

Hi, I'm having some problems to run the editor template, I get a IllegalStateException("kool-project.json not found") when I run the project from local intellij command (because run application is not available on the gradle project)

Is there something I missed ? And I also can't figure out how the kool-editor works (I have nothing on the mainscreen except two panels), I know it's in early state, but I would like to get the example working :)

Thanks in advance fabmax, and the vulkan fix is quite good, you explained that the vulkan rendering was a bit hacky, but glad it works again !

Physx JNI fix

You probably got an email from my friend Julian about the engine and the jni not loading. I found the issue, in one of the enums which calls native functions upon initializing, it tries to do so before the static block that loads the native libraries. I know this is also an issue with physx jni, but the reason I post this here is because the simplest fix is when creating a KoolContext, it should call de.fabmax.physxjni.Loader.load() upon making the context, that way this issue does not occur.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.