diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..956f1a3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +*/build* +*/*.blend? +*/Sources/Main.hx +*/Sources/arm/node/* +*/khafile.js +config.arm +_themes.json diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..4b308e9 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,7 @@ +# The zlib/libpng License + +This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. +Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +This notice may not be removed or altered from any source distribution. diff --git a/README.md b/README.md index 0a1c56f..bb00c66 100644 --- a/README.md +++ b/README.md @@ -1 +1,5 @@ -Leenkx template projects migrated from armory \ No newline at end of file +# leenkx_templates + +Templates to accompany Leenkx SDK. Check out the [manual](https://github.com/leenkx3d/leenkx/wiki). + +Note: Download the package from [releases](https://github.com/leenkx3d/leenkx_templates/releases). Only use the latest revision of this repository if you are using [git Leenkx](https://github.com/leenkx3d/leenkx/wiki/gitversion). diff --git a/archery/Assets/aim.png b/archery/Assets/aim.png new file mode 100644 index 0000000..55c7558 Binary files /dev/null and b/archery/Assets/aim.png differ diff --git a/archery/Assets/checker_rough.png b/archery/Assets/checker_rough.png new file mode 100644 index 0000000..5fad6a5 Binary files /dev/null and b/archery/Assets/checker_rough.png differ diff --git a/archery/Assets/finger.jpg b/archery/Assets/finger.jpg new file mode 100644 index 0000000..0229042 Binary files /dev/null and b/archery/Assets/finger.jpg differ diff --git a/archery/Assets/grid.png b/archery/Assets/grid.png new file mode 100644 index 0000000..84c984b Binary files /dev/null and b/archery/Assets/grid.png differ diff --git a/archery/Assets/grid_b.png b/archery/Assets/grid_b.png new file mode 100644 index 0000000..00f0673 Binary files /dev/null and b/archery/Assets/grid_b.png differ diff --git a/archery/Bundled/bow_draw.wav b/archery/Bundled/bow_draw.wav new file mode 100644 index 0000000..31fa0b5 Binary files /dev/null and b/archery/Bundled/bow_draw.wav differ diff --git a/archery/Bundled/bow_hit.wav b/archery/Bundled/bow_hit.wav new file mode 100644 index 0000000..8799684 Binary files /dev/null and b/archery/Bundled/bow_hit.wav differ diff --git a/archery/Bundled/bow_shoot.wav b/archery/Bundled/bow_shoot.wav new file mode 100644 index 0000000..41e4e7a Binary files /dev/null and b/archery/Bundled/bow_shoot.wav differ diff --git a/archery/Bundled/canvas/MyCanvas.files b/archery/Bundled/canvas/MyCanvas.files new file mode 100644 index 0000000..8215e93 --- /dev/null +++ b/archery/Bundled/canvas/MyCanvas.files @@ -0,0 +1 @@ +../../Assets/aim.png diff --git a/archery/Bundled/canvas/MyCanvas.json b/archery/Bundled/canvas/MyCanvas.json new file mode 100644 index 0000000..66ebb41 --- /dev/null +++ b/archery/Bundled/canvas/MyCanvas.json @@ -0,0 +1 @@ +{"name":"untitled","x":0,"y":0,"width":960,"height":540,"elements":[{"id":0,"type":1,"name":"Image","event":"","x":0,"y":0,"width":64,"height":64,"text":"Image","asset":"aim.png","color":-1,"anchor":4,"children":[],"visible":true,"rotation":0}],"assets":[{"name":"aim.png","file":"../../Assets/aim.png","id":0}],"theme":"Default Light"} \ No newline at end of file diff --git a/archery/Sources/lnx/FirstPersonController.hx b/archery/Sources/lnx/FirstPersonController.hx new file mode 100644 index 0000000..366e23d --- /dev/null +++ b/archery/Sources/lnx/FirstPersonController.hx @@ -0,0 +1,167 @@ +package lnx; + +import iron.math.Vec4; +import iron.math.Quat; +import iron.math.Mat4; +import iron.system.Input; +import iron.object.Object; +import iron.object.BoneAnimation; +import leenkx.trait.physics.PhysicsWorld; +import leenkx.trait.internal.CameraController; + +class FirstPersonController extends CameraController { + +#if (!lnx_physics) + public function new() { super(); } +#else + + static inline var rotationSpeed = 0.5; + + public function new() { + super(); + notifyOnInit(function() { + PhysicsWorld.active.notifyOnPreUpdate(preUpdate); + notifyOnUpdate(update); + + notifyOnRemove(function() { + PhysicsWorld.active.removePreUpdate(preUpdate); + }); + + }); + } + + function findAnimation(o:Object):BoneAnimation { + if (o.animation != null) return cast o.animation; + for (c in o.children) { + var co = findAnimation(c); + if (co != null) return co; + } + return null; + } + + var xVec = Vec4.xAxis(); + var zVec = Vec4.zAxis(); + var angle = 0.0; + var nextFrameRot = 0.0; + var anim:BoneAnimation = null; + var q = new Quat(); + var mat = Mat4.identity(); + + function preUpdate() { + if (Input.occupied || !body.ready) return; + + var mouse = Input.getMouse(); + var kb = Input.getKeyboard(); + + if (mouse.started() && !mouse.locked) mouse.lock(); + else if (kb.started("escape") && mouse.locked) mouse.unlock(); + + if (nextFrameRot != 0.0) { + var origin = object.getChild("CameraOrigin"); + origin.transform.rotate(xVec, nextFrameRot); + origin.transform.buildMatrix(); + } + nextFrameRot = 0; + + if (mouse.moved) { + var d = mouse.movementY / 250; + if (angle + d < 0.25 && angle + d > -0.25) { + angle += d; + nextFrameRot = mouse.movementY / 250 * rotationSpeed; + } + } + + if (anim == null) { + anim = findAnimation(object.getChild("Armature")); + anim.notifyOnUpdate(updateBones); + } + + if (mouse.moved) transform.rotate(zVec, -mouse.movementX / 250 * rotationSpeed); + body.syncTransform(); + } + + function updateBones() { + // Fetch bone + var bone1 = anim.getBone("mixamorig:LeftForeLnx"); + var bone2 = anim.getBone("mixamorig:RightForeLnx"); + // Fetch bone matrix - this is in local bone space for now + var m1 = anim.getBoneMat(bone1); + var m2 = anim.getBoneMat(bone2); + var m1b = anim.getBoneMatBlend(bone1); + var m2b = anim.getBoneMatBlend(bone2); + var a1 = anim.getAbsMat(bone1.parent); + var a2 = anim.getAbsMat(bone2.parent); + + // Rotate hand bones to aim with gun + // Some raw math follows.. + var tx = m1._30; + var ty = m1._31; + var tz = m1._32; + m1._30 = 0; + m1._31 = 0; + m1._32 = 0; + mat.getInverse(a1); + q.fromAxisAngle(mat.right(), angle); + m1.applyQuat(q); + m1._30 = tx; + m1._31 = ty; + m1._32 = tz; + + var tx = m2._30; + var ty = m2._31; + var tz = m2._32; + m2._30 = 0; + m2._31 = 0; + m2._32 = 0; + mat.getInverse(a2); + var v = mat.right(); + v.mult(-1); + // Todo: We do not do inverse kinematics just yet, right hand moves unnaturally + // Point the hand down to the ground for now + q.fromAxisAngle(v, -angle / 3.0); + m2.applyQuat(q); + m2._30 = tx; + m2._31 = ty; + m2._32 = tz; + + // Animation blending is in progress, we need to rotate those bones too + if (m1b != null && m2b != null) { + var tx = m1b._30; + var ty = m1b._31; + var tz = m1b._32; + m1b._30 = 0; + m1b._31 = 0; + m1b._32 = 0; + mat.getInverse(a1); + q.fromAxisAngle(mat.right(), angle); + m1b.applyQuat(q); + m1b._30 = tx; + m1b._31 = ty; + m1b._32 = tz; + + var tx = m2b._30; + var ty = m2b._31; + var tz = m2b._32; + m2b._30 = 0; + m2b._31 = 0; + m2b._32 = 0; + mat.getInverse(a2); + var v = mat.right(); + v.mult(-1); + q.fromAxisAngle(v, -angle / 3.0); + m2b.applyQuat(q); + m2b._30 = tx; + m2b._31 = ty; + m2b._32 = tz; + } + } + + function update() { + if (!body.ready) return; + + // Keep vertical + body.setAngularFactor(0, 0, 0); + camera.buildMatrix(); + } +#end +} diff --git a/archery/archery.blend b/archery/archery.blend new file mode 100644 index 0000000..467a88b Binary files /dev/null and b/archery/archery.blend differ diff --git a/first_person/Assets/aim.png b/first_person/Assets/aim.png new file mode 100644 index 0000000..55c7558 Binary files /dev/null and b/first_person/Assets/aim.png differ diff --git a/first_person/Assets/checker_rough.png b/first_person/Assets/checker_rough.png new file mode 100644 index 0000000..5fad6a5 Binary files /dev/null and b/first_person/Assets/checker_rough.png differ diff --git a/first_person/Assets/decal1.png b/first_person/Assets/decal1.png new file mode 100644 index 0000000..7af3033 Binary files /dev/null and b/first_person/Assets/decal1.png differ diff --git a/first_person/Assets/decal2.png b/first_person/Assets/decal2.png new file mode 100644 index 0000000..fd545e1 Binary files /dev/null and b/first_person/Assets/decal2.png differ diff --git a/first_person/Assets/finger.jpg b/first_person/Assets/finger.jpg new file mode 100644 index 0000000..0229042 Binary files /dev/null and b/first_person/Assets/finger.jpg differ diff --git a/first_person/Assets/grid.png b/first_person/Assets/grid.png new file mode 100644 index 0000000..84c984b Binary files /dev/null and b/first_person/Assets/grid.png differ diff --git a/first_person/Assets/grid_b.png b/first_person/Assets/grid_b.png new file mode 100644 index 0000000..00f0673 Binary files /dev/null and b/first_person/Assets/grid_b.png differ diff --git a/first_person/Bundled/canvas/MyCanvas.files b/first_person/Bundled/canvas/MyCanvas.files new file mode 100644 index 0000000..8215e93 --- /dev/null +++ b/first_person/Bundled/canvas/MyCanvas.files @@ -0,0 +1 @@ +../../Assets/aim.png diff --git a/first_person/Bundled/canvas/MyCanvas.json b/first_person/Bundled/canvas/MyCanvas.json new file mode 100644 index 0000000..7e4c337 --- /dev/null +++ b/first_person/Bundled/canvas/MyCanvas.json @@ -0,0 +1 @@ +{"name":"untitled","x":0,"y":0,"width":1920,"height":1080,"elements":[{"id":0,"type":1,"name":"Image","event":"","x":0,"y":0,"width":60,"height":60,"rotation":0,"text":"Image","asset":"aim.png","progress_at":0,"progress_total":100,"strength":1,"alignment":0,"anchor":4,"parent":null,"children":[],"visible":true}],"assets":[{"name":"aim.png","file":"../../Assets/aim.png","id":0}],"theme":"Default Light"} \ No newline at end of file diff --git a/first_person/Bundled/fire0.wav b/first_person/Bundled/fire0.wav new file mode 100644 index 0000000..e591890 Binary files /dev/null and b/first_person/Bundled/fire0.wav differ diff --git a/first_person/Bundled/fire1.wav b/first_person/Bundled/fire1.wav new file mode 100644 index 0000000..cdd0727 Binary files /dev/null and b/first_person/Bundled/fire1.wav differ diff --git a/first_person/Bundled/step0.wav b/first_person/Bundled/step0.wav new file mode 100644 index 0000000..0201d28 Binary files /dev/null and b/first_person/Bundled/step0.wav differ diff --git a/first_person/Bundled/step1.wav b/first_person/Bundled/step1.wav new file mode 100644 index 0000000..c5a5f89 Binary files /dev/null and b/first_person/Bundled/step1.wav differ diff --git a/first_person/Sources/lnx/FirstPersonController.hx b/first_person/Sources/lnx/FirstPersonController.hx new file mode 100644 index 0000000..abbf422 --- /dev/null +++ b/first_person/Sources/lnx/FirstPersonController.hx @@ -0,0 +1,277 @@ +package lnx; + +import iron.math.Vec4; +import iron.math.Quat; +import iron.math.Mat4; +import iron.system.Input; +import iron.object.Object; +import iron.object.BoneAnimation; +import iron.system.Time; +import iron.system.Audio; +import leenkx.trait.physics.PhysicsWorld; +import leenkx.trait.internal.CameraController; + +class FirstPersonController extends CameraController { + +#if (!lnx_physics) + public function new() { super(); } +#else + + static inline var rotationSpeed = 1.0; + + var stepTime = 0.0; + var turnTime = 0.0; + + var soundStep0:kha.Sound = null; + var soundStep1:kha.Sound = null; + var soundsLoaded = 0; + + var xVec = Vec4.xAxis(); + var zVec = Vec4.zAxis(); + var angle = 0.0; + var nextFrameRot = 0.0; + var armature:Object; + var anim:BoneAnimation; + var q = new Quat(); + var mat = Mat4.identity(); + + var nextIdle = false; + var jumpTime = 0.0; + var firingTime = 0.0; + var speed = 1.0; + var dir = new Vec4(); + var lastLook:Vec4; + var state = "idle"; + + public function new() { + super(); + + iron.data.Data.getSound("step0.wav", function(sound:kha.Sound) { soundStep0 = sound; soundsLoaded++; }); + iron.data.Data.getSound("step1.wav", function(sound:kha.Sound) { soundStep1 = sound; soundsLoaded++; }); + + notifyOnInit(function() { + PhysicsWorld.active.notifyOnPreUpdate(preUpdate); + notifyOnUpdate(update); + + notifyOnRemove(function() { + PhysicsWorld.active.removePreUpdate(preUpdate); + }); + + armature = object.getChild("Armature"); + anim = findAnimation(armature); + anim.notifyOnUpdate(updateBones); + lastLook = armature.transform.look().normalize(); + }); + } + + function findAnimation(o:Object):BoneAnimation { + if (o.animation != null) return cast o.animation; + for (c in o.children) { + var co = findAnimation(c); + if (co != null) return co; + } + return null; + } + + function updateBones() { + + // Fetch bone + var bone1 = anim.getBone("mixamorig:LeftForeLnx"); + var bone2 = anim.getBone("mixamorig:RightForeLnx"); + + // Fetch bone matrix - this is in local bone space for now + var m1 = anim.getBoneMat(bone1); + var m2 = anim.getBoneMat(bone2); + var m1b = anim.getBoneMatBlend(bone1); + var m2b = anim.getBoneMatBlend(bone2); + var a1 = anim.getAbsMat(bone1.parent); + var a2 = anim.getAbsMat(bone2.parent); + + // Rotate hand bones to aim with gun + // Some raw math follows.. + var tx = m1._30; + var ty = m1._31; + var tz = m1._32; + m1._30 = 0; + m1._31 = 0; + m1._32 = 0; + mat.getInverse(a1); + q.fromAxisAngle(mat.right(), -angle); + m1.applyQuat(q); + m1._30 = tx; + m1._31 = ty; + m1._32 = tz; + + var tx = m2._30; + var ty = m2._31; + var tz = m2._32; + m2._30 = 0; + m2._31 = 0; + m2._32 = 0; + mat.getInverse(a2); + var v = mat.right(); + v.mult(-1); + // Todo: Do inverse kinematics here, right hand moves unnaturally + q.fromAxisAngle(v, -1.6); + // q.fromAxisAngle(v, angle); + m2.applyQuat(q); + m2._30 = tx; + m2._31 = ty; + m2._32 = tz; + + // Animation blending is in progress, we need to rotate those bones too + if (m1b != null && m2b != null) { + var tx = m1b._30; + var ty = m1b._31; + var tz = m1b._32; + m1b._30 = 0; + m1b._31 = 0; + m1b._32 = 0; + mat.getInverse(a1); + q.fromAxisAngle(mat.right(), -angle); + m1b.applyQuat(q); + m1b._30 = tx; + m1b._31 = ty; + m1b._32 = tz; + + var tx = m2b._30; + var ty = m2b._31; + var tz = m2b._32; + m2b._30 = 0; + m2b._31 = 0; + m2b._32 = 0; + mat.getInverse(a2); + var v = mat.right(); + v.mult(-1); + q.fromAxisAngle(v, -1.6); + // q.fromAxisAngle(v, angle); + m2b.applyQuat(q); + m2b._30 = tx; + m2b._31 = ty; + m2b._32 = tz; + } + } + + function preUpdate() { + if (Input.occupied || !body.ready) return; + + var mouse = Input.getMouse(); + var kb = Input.getKeyboard(); + + if (mouse.started() && !mouse.locked) mouse.lock(); + else if (kb.started("escape") && mouse.locked) mouse.unlock(); + + if (nextFrameRot != 0.0) { + var origin = object.getChild("CameraOrigin"); + origin.transform.rotate(xVec, nextFrameRot); + origin.transform.buildMatrix(); + } + nextFrameRot = 0; + + if (mouse.moved) { + var d = mouse.movementY / 250; + if (angle + d > 1.5) return; + if (angle + d < -0.25) return; + angle += d; + nextFrameRot = -mouse.movementY / 250 * rotationSpeed; + } + + if (mouse.moved) transform.rotate(zVec, -mouse.movementX / 250 * rotationSpeed); + body.syncTransform(); + } + + function update() { + if (!body.ready) return; + var look = armature.transform.look().normalize(); + + if (jump) { + if (jumpTime >= 0.4){ + body.applyImpulse(new Vec4(0, 0, 16)); + jumpTime = 0; + jump = false; + } + } + jumpTime += Time.delta; + + // Move + dir.set(0, 0, 0); + if (moveForward) dir.add(transform.look()); + if (moveBackward) dir.add(transform.look().mult(-1)); + if (moveLeft) dir.add(transform.right().mult(-1)); + if (moveRight) dir.add(transform.right()); + + // Push down + var btvec = body.getLinearVelocity(); + body.setLinearVelocity(0.0, 0.0, btvec.z - 1.0); + + if (moveForward || moveBackward || moveLeft || moveRight) { + var action = moveForward ? "run" : + moveBackward ? "back" : + moveLeft ? "left" : "right"; + setState(action); + + var kb = iron.system.Input.getKeyboard(); + if (kb.down("shift")) speed = 1.6; + else speed = 1.0; + + dir.mult(speed * 5); + body.activate(); + body.setLinearVelocity(dir.x, dir.y, btvec.z - 1.0); + + stepTime += Time.delta; + if (stepTime > 0.38 / speed) { + stepTime = 0; + if (soundsLoaded == 2) { + Audio.play(Std.random(2) == 0 ? soundStep0 : soundStep1); + } + } + } + // Play correct state + else if (state != "fire" || state != "idle" || state != "turn") { + var mouse = iron.system.Input.getMouse(); + if (mouse.down("left")) { + firingTime = 0.0; + setState("fire", 2.0); + } + else { + // var angle = getAngle(look, lastLook); + // if (Math.abs(angle) > 0.01) { + // setState("turn", angle > 0 ? 1 : -1, 0); + // turnTime = 0; + // } + // else if (turnTime > 0.25){ + setState("idle", 2.0); + // } + // else turnTime += Time.delta; + } + } + + if (state == "fire") firingTime += Time.delta; + else firingTime = 0.0; + + // Keep vertical + body.setAngularFactor(0, 0, 0); + camera.buildMatrix(); + + lastLook.setFrom(look); + } + + // function getAngle(va:Vec4, vb:Vec4) { + // var vn = Vec4.zAxis(); + // var dot = va.dot(vb); + // var det = va.x * vb.y * vn.z + + // vb.x * vn.y * va.z + + // vn.x * va.y * vb.z - + // va.z * vb.y * vn.x - + // vb.z * vn.y * va.x - + // vn.z * va.y * vb.x; + // return Math.atan2(det, dot); + // } + + function setState(s:String, speed = 1.0, blend = 0.2) { + if (s == state) return; + state = s; + anim.play(s, null, blend, speed); + } +#end +} diff --git a/first_person/Sources/lnx/GunController.hx b/first_person/Sources/lnx/GunController.hx new file mode 100644 index 0000000..667192a --- /dev/null +++ b/first_person/Sources/lnx/GunController.hx @@ -0,0 +1,71 @@ +package lnx; + +import iron.Trait; +import iron.system.Input; +import iron.object.Object; +import iron.object.Transform; +import iron.system.Audio; +import iron.system.Time; +import leenkx.trait.physics.RigidBody; + +@:keep +class GunController extends Trait { + +#if (!lnx_physics) + public function new() { super(); } +#else + + @prop + public var fireFreq = 0.2; + + var firePoint:Transform; + var fireStrength = 25; + var lastFire = 0.0; + + var soundFire0:kha.Sound = null; + var soundFire1:kha.Sound = null; + var soundsLoaded = 0; + + public function new() { + super(); + + iron.data.Data.getSound("fire0.wav", function(sound:kha.Sound) { soundFire0 = sound; soundsLoaded++; }); + iron.data.Data.getSound("fire1.wav", function(sound:kha.Sound) { soundFire1 = sound; soundsLoaded++; }); + + notifyOnInit(function() { + firePoint = object.getChild("ProjectileSpawn").transform; + }); + + notifyOnUpdate(function() { + var mouse = Input.getMouse(); + lastFire += Time.delta; + if ((mouse.down("left") && lastFire > fireFreq) || mouse.started("left")) { + shoot(); + if (soundsLoaded == 2) { + Audio.play(Std.random(3) == 0 ? soundFire1 : soundFire0); + } + lastFire = 0.0; + } + }); + } + + function shoot() { + // Spawn projectile + iron.Scene.active.spawnObject("Projectile", null, function(o:Object) { + o.transform.loc.x = firePoint.worldx(); + o.transform.loc.y = firePoint.worldy(); + o.transform.loc.z = firePoint.worldz(); + o.transform.buildMatrix(); + // Apply force + var rb:RigidBody = o.getTrait(RigidBody); + rb.syncTransform(); + var look = object.transform.look().normalize(); + rb.setLinearVelocity(look.x * fireStrength, look.y * fireStrength, look.z * fireStrength); + // Remove projectile after a period of time + kha.Scheduler.addTimeTask(function() { + o.remove(); + }, 10); + }); + } +#end +} diff --git a/first_person/first_person.blend b/first_person/first_person.blend new file mode 100644 index 0000000..a8e39fa Binary files /dev/null and b/first_person/first_person.blend differ diff --git a/platformer/Assets/checker_rough.png b/platformer/Assets/checker_rough.png new file mode 100644 index 0000000..5fad6a5 Binary files /dev/null and b/platformer/Assets/checker_rough.png differ diff --git a/platformer/Assets/finger.jpg b/platformer/Assets/finger.jpg new file mode 100644 index 0000000..0229042 Binary files /dev/null and b/platformer/Assets/finger.jpg differ diff --git a/platformer/Assets/grid.png b/platformer/Assets/grid.png new file mode 100644 index 0000000..84c984b Binary files /dev/null and b/platformer/Assets/grid.png differ diff --git a/platformer/Assets/grid_b.png b/platformer/Assets/grid_b.png new file mode 100644 index 0000000..00f0673 Binary files /dev/null and b/platformer/Assets/grid_b.png differ diff --git a/platformer/Bundled/canvas/MyCanvas.json b/platformer/Bundled/canvas/MyCanvas.json new file mode 100644 index 0000000..a273ecc --- /dev/null +++ b/platformer/Bundled/canvas/MyCanvas.json @@ -0,0 +1 @@ +{"name":"untitled","x":0,"y":0,"width":960,"height":540,"elements":[{"id":0,"type":0,"name":"Gems","event":"","x":0,"y":0,"width":150,"height":44,"rotation":0,"text":"0","color":-1,"anchor":0,"parent":null,"children":[],"visible":true}],"assets":[]} \ No newline at end of file diff --git a/platformer/Bundled/coin.wav b/platformer/Bundled/coin.wav new file mode 100644 index 0000000..b070ee4 Binary files /dev/null and b/platformer/Bundled/coin.wav differ diff --git a/platformer/Bundled/step0.wav b/platformer/Bundled/step0.wav new file mode 100644 index 0000000..0201d28 Binary files /dev/null and b/platformer/Bundled/step0.wav differ diff --git a/platformer/Bundled/step1.wav b/platformer/Bundled/step1.wav new file mode 100644 index 0000000..c5a5f89 Binary files /dev/null and b/platformer/Bundled/step1.wav differ diff --git a/platformer/Sources/lnx/GemTrait.hx b/platformer/Sources/lnx/GemTrait.hx new file mode 100644 index 0000000..11423c8 --- /dev/null +++ b/platformer/Sources/lnx/GemTrait.hx @@ -0,0 +1,41 @@ +package lnx; + +class GemTrait extends iron.Trait { + + static var gemsCollected = 0; + static var player:iron.object.Object = null; + static var coinSound:kha.Sound = null; + + public function new() { + super(); + + if (coinSound == null) { + notifyOnInit(function() { + iron.data.Data.getSound("coin.wav", function(sound:kha.Sound) { + coinSound = sound; + }); + }); + } + + notifyOnUpdate(function() { + object.transform.rotate(iron.math.Vec4.zAxis(), 0.05); + + if (player == null) player = iron.Scene.active.getChild("Player"); + var w1 = object.transform.world; + var w2 = player.transform.world; + var d = iron.math.Vec4.distance(w1.getLoc(), w2.getLoc()); + + // Collect gem + if (d < 0.8) { + gemsCollected++; + object.remove(); + + // Update UI + var canvas = iron.Scene.active.getTrait(leenkx.trait.internal.CanvasScript); + canvas.getElement("Gems").text = gemsCollected + ""; + + if (coinSound != null) iron.system.Audio.play(coinSound); + } + }); + } +} diff --git a/platformer/Sources/lnx/ThirdPersonController.hx b/platformer/Sources/lnx/ThirdPersonController.hx new file mode 100644 index 0000000..faf58f8 --- /dev/null +++ b/platformer/Sources/lnx/ThirdPersonController.hx @@ -0,0 +1,153 @@ +package lnx; + +import iron.math.Vec4; +import iron.math.Quat; +import iron.math.Mat4; +import iron.system.Input; +import iron.object.Object; +import iron.object.BoneAnimation; +import iron.system.Time; +import iron.system.Audio; +import leenkx.trait.physics.PhysicsWorld; +import leenkx.trait.internal.CameraController; + +class ThirdPersonController extends CameraController { + +#if (!lnx_physics) + public function new() { super(); } +#else + + static inline var rotationSpeed = 1.0; + + var stepTime = 0.0; + var soundStep0:kha.Sound = null; + var soundStep1:kha.Sound = null; + var soundsLoaded = 0; + + var xVec = Vec4.xAxis(); + var zVec = Vec4.zAxis(); + var nextFrameRot = 0.0; + var armature:Object; + var anim:BoneAnimation; + + var speed = 1.0; + var dir = new Vec4(); + var state = "idle"; + var jumping = false; + + public function new() { + super(); + + iron.data.Data.getSound("step0.wav", function(sound:kha.Sound) { soundStep0 = sound; soundsLoaded++; }); + iron.data.Data.getSound("step1.wav", function(sound:kha.Sound) { soundStep1 = sound; soundsLoaded++; }); + + notifyOnInit(function() { + PhysicsWorld.active.notifyOnPreUpdate(preUpdate); + notifyOnUpdate(update); + + notifyOnRemove(function() { + PhysicsWorld.active.removePreUpdate(preUpdate); + }); + + armature = object.getChild("Armature"); + anim = findAnimation(armature); + }); + } + + function findAnimation(o:Object):BoneAnimation { + if (o.animation != null) return cast o.animation; + for (c in o.children) { + var co = findAnimation(c); + if (co != null) return co; + } + return null; + } + + function preUpdate() { + if (Input.occupied || !body.ready) return; + + var mouse = Input.getMouse(); + var kb = Input.getKeyboard(); + + if (mouse.started() && !mouse.locked) mouse.lock(); + else if (kb.started("escape") && mouse.locked) mouse.unlock(); + + if (nextFrameRot != 0.0) { + var origin = object.getChild("CameraOrigin"); + origin.transform.rotate(xVec, nextFrameRot); + origin.transform.buildMatrix(); + } + nextFrameRot = 0; + + if (mouse.moved) { + nextFrameRot = -mouse.movementY / 250 * rotationSpeed; + transform.rotate(zVec, -mouse.movementX / 250 * rotationSpeed); + } + + body.syncTransform(); + } + + function update() { + if (!body.ready) return; + + var kb = iron.system.Input.getKeyboard(); + + // Move + dir.set(0, 0, 0); + if (moveForward) dir.add(transform.look()); + if (moveBackward) dir.add(transform.look().mult(-1)); + if (moveLeft) dir.add(transform.right().mult(-1)); + if (moveRight) dir.add(transform.right()); + + // Push down + var btvec = body.getLinearVelocity(); + body.setLinearVelocity(0.0, 0.0, btvec.z - 1.0); + + if (moveForward || moveBackward || moveLeft || moveRight) { + var action = moveForward ? "run" : + moveBackward ? "back" : + moveLeft ? "left" : "right"; + setState(action); + + if (kb.down("shift")) speed = 1.6; + else speed = 1.0; + + dir.mult(speed * 5); + body.activate(); + body.setLinearVelocity(dir.x, dir.y, btvec.z - 1.0); + + stepTime += Time.delta; + if (stepTime > 0.38 / speed) { + stepTime = 0; + if (soundsLoaded == 2) { + Audio.play(Std.random(2) == 0 ? soundStep0 : soundStep1); + } + } + } + // Play correct state + else { + setState("idle", 1.0); + } + + if (jump && !jumping) { + jumping = true; + state = "jump"; + body.applyImpulse(new Vec4(0, 0, 20)); + anim.time = 0; + anim.frameIndex = 0; + // anim.play(state, function() { jumping = false; }, 0.0, 1.2); + iron.system.Tween.timer(0.5, () -> jumping = false); + } + + // Keep vertical + body.setAngularFactor(0, 0, 0); + camera.buildMatrix(); + } + + function setState(s:String, speed = 1.0, blend = 0.2) { + if (s == state || jumping) return; + state = s; + anim.play(s, null, blend, speed); + } +#end +} diff --git a/platformer/platformer.blend b/platformer/platformer.blend new file mode 100644 index 0000000..307b5e7 Binary files /dev/null and b/platformer/platformer.blend differ diff --git a/race_track/Assets/checker_rough.png b/race_track/Assets/checker_rough.png new file mode 100644 index 0000000..5fad6a5 Binary files /dev/null and b/race_track/Assets/checker_rough.png differ diff --git a/race_track/Assets/finger.jpg b/race_track/Assets/finger.jpg new file mode 100644 index 0000000..0229042 Binary files /dev/null and b/race_track/Assets/finger.jpg differ diff --git a/race_track/Assets/grid.png b/race_track/Assets/grid.png new file mode 100644 index 0000000..482fdc4 Binary files /dev/null and b/race_track/Assets/grid.png differ diff --git a/race_track/Assets/grid_b.png b/race_track/Assets/grid_b.png new file mode 100644 index 0000000..00f0673 Binary files /dev/null and b/race_track/Assets/grid_b.png differ diff --git a/race_track/Bundled/canvas/MyCanvas.files b/race_track/Bundled/canvas/MyCanvas.files new file mode 100644 index 0000000..e69de29 diff --git a/race_track/Bundled/canvas/MyCanvas.json b/race_track/Bundled/canvas/MyCanvas.json new file mode 100644 index 0000000..f243c70 --- /dev/null +++ b/race_track/Bundled/canvas/MyCanvas.json @@ -0,0 +1 @@ +{"name":"untitled","x":0,"y":0,"width":1280,"height":720,"elements":[{"id":15,"type":9,"name":"MenuContainer","event":"","x":935,"y":61,"width":332,"height":402,"rotation":0,"text":"My Shape","asset":"","color":-1258291200,"anchor":5,"parent":null,"children":[2,3,6,8,11,12,13,14],"visible":true},{"id":2,"type":6,"name":"SSR","event":"","x":24,"y":179,"width":150,"height":44,"rotation":0,"text":"SSR","asset":"","color":-1,"anchor":0,"parent":15,"children":[],"visible":true},{"id":3,"type":6,"name":"SSAO","event":"","x":24,"y":229,"width":150,"height":44,"rotation":0,"text":"SSAO","asset":"","color":-1,"anchor":0,"parent":15,"children":[],"visible":true},{"id":6,"type":6,"name":"Bloom","event":"","x":24,"y":129,"width":150,"height":44,"rotation":0,"text":"Bloom","asset":"","color":-1,"anchor":0,"parent":15,"children":[],"visible":true},{"id":8,"type":8,"name":"Shadows","event":"","x":24,"y":79,"width":150,"height":44,"rotation":0,"text":"High;Medium;Low","asset":"","color":-1,"anchor":0,"parent":15,"children":[],"visible":true},{"id":11,"type":0,"name":"Title","event":"","x":4,"y":9,"width":377,"height":44,"rotation":0,"text":"Graphics Settings","asset":"","color":-1,"anchor":0,"parent":15,"children":[],"visible":true},{"id":12,"type":0,"name":"ShadowsTitle","event":"","x":164,"y":79,"width":184,"height":28,"rotation":0,"text":"Shadows","asset":"","color":-6381922,"anchor":0,"parent":15,"children":[],"visible":true},{"id":13,"type":2,"name":"Apply","event":"apply_settings","x":24,"y":329,"width":150,"height":44,"rotation":0,"text":"Apply","asset":"","color":-1,"anchor":0,"parent":15,"children":[],"visible":true},{"id":14,"type":6,"name":"Voxels","event":"","x":24,"y":279,"width":150,"height":44,"rotation":0,"text":"Voxels","asset":"","color":-1,"anchor":0,"parent":15,"children":[],"visible":true},{"id":16,"type":2,"name":"Menu","event":"toggle_menu","x":1120,"y":5,"width":150,"height":44,"rotation":0,"text":"Menu","asset":"","color":-1,"anchor":2,"parent":null,"children":[],"visible":true},{"id":17,"type":0,"name":"Time","event":"","x":0,"y":0,"width":202,"height":65,"rotation":0,"text":"0:0","asset":"","color":-1,"anchor":0,"parent":null,"children":[],"visible":true}],"assets":[]} \ No newline at end of file diff --git a/race_track/Sources/lnx/SettingsTrait.hx b/race_track/Sources/lnx/SettingsTrait.hx new file mode 100644 index 0000000..dc1f569 --- /dev/null +++ b/race_track/Sources/lnx/SettingsTrait.hx @@ -0,0 +1,68 @@ +package lnx; + +import iron.Scene; +import leenkx.system.Event; +import leenkx.data.Config; +import leenkx.trait.internal.CanvasScript; +import leenkx.renderpath.RenderPathCreator; + +class SettingsTrait extends iron.Trait { + + var canvas:CanvasScript; + var envStrength = 0.0; + + public function new() { + super(); + + notifyOnInit(function() { + canvas = Scene.active.getTrait(CanvasScript); + + // Init UI to values loaded from config.lnx file + canvas.notifyOnReady(function() { + canvas.getElement("MenuContainer").visible = false; + canvas.getHandle("SSAO").selected = Config.raw.rp_ssgi; + canvas.getHandle("SSR").selected = Config.raw.rp_ssr; + canvas.getHandle("Bloom").selected = Config.raw.rp_bloom; + canvas.getHandle("Voxels").selected = Config.raw.rp_gi; + canvas.getHandle("Shadows").position = getShadowQuality(Config.raw.rp_shadowmap_cascade); + }); + + // Button events + Event.add("toggle_menu", toggleMenu); + Event.add("apply_settings", applySettings); + }); + } + + function toggleMenu() { + var e = canvas.getElement("MenuContainer"); + e.visible = !e.visible; + } + + function applySettings() { + + // Apply render path settings + Config.raw.rp_ssgi = canvas.getHandle("SSAO").selected; + Config.raw.rp_ssr = canvas.getHandle("SSR").selected; + Config.raw.rp_bloom = canvas.getHandle("Bloom").selected; + Config.raw.rp_gi = canvas.getHandle("Voxels").selected; + Config.raw.rp_shadowmap_cascade = getShadowMapSize(canvas.getHandle("Shadows").position); + RenderPathCreator.applyConfig(); + + // Lower envmap strength when voxel ao is disabled + var p = iron.Scene.active.world.probe.raw; + if (envStrength == 0) envStrength = p.strength; + p.strength = Config.raw.rp_gi ? envStrength : envStrength / 3; + + // Save config.lnx file + Config.save(); + } + + inline function getShadowQuality(i:Int):Int { + // 0 - High, 1 - Medium, 2 - Low + return i == 4096 ? 0 : i == 2048 ? 1 : 2; + } + + inline function getShadowMapSize(i:Int):Int { + return i == 0 ? 4096 : i == 1 ? 2048 : 1024; + } +} diff --git a/race_track/Sources/lnx/TrackTimer.hx b/race_track/Sources/lnx/TrackTimer.hx new file mode 100644 index 0000000..0d188fb --- /dev/null +++ b/race_track/Sources/lnx/TrackTimer.hx @@ -0,0 +1,55 @@ +package lnx; + +import iron.Scene; +import iron.object.Transform; +using leenkx.object.TransformExtension; + +class TrackTimer extends iron.Trait { + + var time = 0.0; + var running = false; + var finished = false; + var triggers:Array = []; + + public function new() { + super(); + + notifyOnInit(function() { + var canvas = Scene.active.getTrait(leenkx.trait.internal.CanvasScript); + var t = object.transform; + + // Retrieve triggers + var t0 = Scene.active.getChild("Trigger0").transform; // Start line + var t1 = Scene.active.getChild("Trigger1").transform; + var t2 = Scene.active.getChild("Trigger2").transform; + var t3 = Scene.active.getChild("Trigger3").transform; + + notifyOnUpdate(function() { + + if (!running && !finished) { + // Start line crossed, begin lap + running = t.overlap(t0); + } + else { + // Update timer + if (!finished) { + time += iron.system.Time.delta; + var s = Std.int(time); + var ms = Std.int((time - s) * 100); + canvas.getElement("Time").text = s + "." + ms; + } + + // Mark touched triggers + if (t.overlap(t1) && triggers.indexOf(t1) == -1) triggers.push(t1); + if (t.overlap(t2) && triggers.indexOf(t2) == -1) triggers.push(t2); + if (t.overlap(t3) && triggers.indexOf(t3) == -1) triggers.push(t3); + + // Crossed finish line + if (t.overlap(t0) && triggers.length == 3) { + finished = true; + } + } + }); + }); + } +} diff --git a/race_track/race_track.blend b/race_track/race_track.blend new file mode 100644 index 0000000..05ca393 Binary files /dev/null and b/race_track/race_track.blend differ diff --git a/third_person/Assets/aim.png b/third_person/Assets/aim.png new file mode 100644 index 0000000..55c7558 Binary files /dev/null and b/third_person/Assets/aim.png differ diff --git a/third_person/Assets/checker_rough.png b/third_person/Assets/checker_rough.png new file mode 100644 index 0000000..5fad6a5 Binary files /dev/null and b/third_person/Assets/checker_rough.png differ diff --git a/third_person/Assets/decal1.png b/third_person/Assets/decal1.png new file mode 100644 index 0000000..7af3033 Binary files /dev/null and b/third_person/Assets/decal1.png differ diff --git a/third_person/Assets/decal2.png b/third_person/Assets/decal2.png new file mode 100644 index 0000000..fd545e1 Binary files /dev/null and b/third_person/Assets/decal2.png differ diff --git a/third_person/Assets/finger.jpg b/third_person/Assets/finger.jpg new file mode 100644 index 0000000..0229042 Binary files /dev/null and b/third_person/Assets/finger.jpg differ diff --git a/third_person/Assets/grid.png b/third_person/Assets/grid.png new file mode 100644 index 0000000..84c984b Binary files /dev/null and b/third_person/Assets/grid.png differ diff --git a/third_person/Assets/grid_b.png b/third_person/Assets/grid_b.png new file mode 100644 index 0000000..00f0673 Binary files /dev/null and b/third_person/Assets/grid_b.png differ diff --git a/third_person/Bundled/canvas/MyCanvas.files b/third_person/Bundled/canvas/MyCanvas.files new file mode 100644 index 0000000..8215e93 --- /dev/null +++ b/third_person/Bundled/canvas/MyCanvas.files @@ -0,0 +1 @@ +../../Assets/aim.png diff --git a/third_person/Bundled/canvas/MyCanvas.json b/third_person/Bundled/canvas/MyCanvas.json new file mode 100644 index 0000000..8eb93cf --- /dev/null +++ b/third_person/Bundled/canvas/MyCanvas.json @@ -0,0 +1 @@ +{"name":"untitled","x":0,"y":0,"width":1920,"height":1080,"elements":[{"id":1,"type":1,"name":"Crosshair","event":"","x":0,"y":0,"width":60,"height":60,"rotation":0,"text":"","asset":"aim.png","progress_at":0,"progress_total":100,"strength":1,"alignment":0,"anchor":4,"parent":null,"children":[],"visible":true}],"assets":[{"name":"aim.png","file":"../../Assets/aim.png","id":0}],"theme":"Default Light"} \ No newline at end of file diff --git a/third_person/Bundled/fire0.wav b/third_person/Bundled/fire0.wav new file mode 100644 index 0000000..e591890 Binary files /dev/null and b/third_person/Bundled/fire0.wav differ diff --git a/third_person/Bundled/fire1.wav b/third_person/Bundled/fire1.wav new file mode 100644 index 0000000..cdd0727 Binary files /dev/null and b/third_person/Bundled/fire1.wav differ diff --git a/third_person/Bundled/step0.wav b/third_person/Bundled/step0.wav new file mode 100644 index 0000000..0201d28 Binary files /dev/null and b/third_person/Bundled/step0.wav differ diff --git a/third_person/Bundled/step1.wav b/third_person/Bundled/step1.wav new file mode 100644 index 0000000..c5a5f89 Binary files /dev/null and b/third_person/Bundled/step1.wav differ diff --git a/third_person/Sources/lnx/GunController.hx b/third_person/Sources/lnx/GunController.hx new file mode 100644 index 0000000..667192a --- /dev/null +++ b/third_person/Sources/lnx/GunController.hx @@ -0,0 +1,71 @@ +package lnx; + +import iron.Trait; +import iron.system.Input; +import iron.object.Object; +import iron.object.Transform; +import iron.system.Audio; +import iron.system.Time; +import leenkx.trait.physics.RigidBody; + +@:keep +class GunController extends Trait { + +#if (!lnx_physics) + public function new() { super(); } +#else + + @prop + public var fireFreq = 0.2; + + var firePoint:Transform; + var fireStrength = 25; + var lastFire = 0.0; + + var soundFire0:kha.Sound = null; + var soundFire1:kha.Sound = null; + var soundsLoaded = 0; + + public function new() { + super(); + + iron.data.Data.getSound("fire0.wav", function(sound:kha.Sound) { soundFire0 = sound; soundsLoaded++; }); + iron.data.Data.getSound("fire1.wav", function(sound:kha.Sound) { soundFire1 = sound; soundsLoaded++; }); + + notifyOnInit(function() { + firePoint = object.getChild("ProjectileSpawn").transform; + }); + + notifyOnUpdate(function() { + var mouse = Input.getMouse(); + lastFire += Time.delta; + if ((mouse.down("left") && lastFire > fireFreq) || mouse.started("left")) { + shoot(); + if (soundsLoaded == 2) { + Audio.play(Std.random(3) == 0 ? soundFire1 : soundFire0); + } + lastFire = 0.0; + } + }); + } + + function shoot() { + // Spawn projectile + iron.Scene.active.spawnObject("Projectile", null, function(o:Object) { + o.transform.loc.x = firePoint.worldx(); + o.transform.loc.y = firePoint.worldy(); + o.transform.loc.z = firePoint.worldz(); + o.transform.buildMatrix(); + // Apply force + var rb:RigidBody = o.getTrait(RigidBody); + rb.syncTransform(); + var look = object.transform.look().normalize(); + rb.setLinearVelocity(look.x * fireStrength, look.y * fireStrength, look.z * fireStrength); + // Remove projectile after a period of time + kha.Scheduler.addTimeTask(function() { + o.remove(); + }, 10); + }); + } +#end +} diff --git a/third_person/Sources/lnx/ThirdPersonController.hx b/third_person/Sources/lnx/ThirdPersonController.hx new file mode 100644 index 0000000..29c7405 --- /dev/null +++ b/third_person/Sources/lnx/ThirdPersonController.hx @@ -0,0 +1,264 @@ +package lnx; + +import iron.math.Vec4; +import iron.math.Quat; +import iron.math.Mat4; +import iron.system.Input; +import iron.object.Object; +import iron.object.BoneAnimation; +import iron.system.Time; +import iron.system.Audio; +import leenkx.trait.physics.PhysicsWorld; +import leenkx.trait.internal.CameraController; + +class ThirdPersonController extends CameraController { + +#if (!lnx_physics) + public function new() { super(); } +#else + + static inline var rotationSpeed = 1.0; + + var stepTime = 0.0; + var turnTime = 0.0; + + var soundStep0:kha.Sound = null; + var soundStep1:kha.Sound = null; + var soundsLoaded = 0; + + var xVec = Vec4.xAxis(); + var zVec = Vec4.zAxis(); + var angle = 0.0; + var nextFrameRot = 0.0; + var armature:Object; + var anim:BoneAnimation; + var q = new Quat(); + var mat = Mat4.identity(); + + var nextIdle = false; + var firingTime = 0.0; + var speed = 1.0; + var dir = new Vec4(); + var lastLook:Vec4; + var state = "idle"; + + public function new() { + super(); + + iron.data.Data.getSound("step0.wav", function(sound:kha.Sound) { soundStep0 = sound; soundsLoaded++; }); + iron.data.Data.getSound("step1.wav", function(sound:kha.Sound) { soundStep1 = sound; soundsLoaded++; }); + + notifyOnInit(function() { + PhysicsWorld.active.notifyOnPreUpdate(preUpdate); + notifyOnUpdate(update); + + notifyOnRemove(function() { + PhysicsWorld.active.removePreUpdate(preUpdate); + }); + + armature = object.getChild("Armature"); + anim = findAnimation(armature); + anim.notifyOnUpdate(updateBones); + lastLook = armature.transform.look().normalize(); + }); + } + + function findAnimation(o:Object):BoneAnimation { + if (o.animation != null) return cast o.animation; + for (c in o.children) { + var co = findAnimation(c); + if (co != null) return co; + } + return null; + } + + function updateBones() { + + // Fetch bone + var bone1 = anim.getBone("mixamorig:LeftForeLnx"); + var bone2 = anim.getBone("mixamorig:RightForeLnx"); + + // Fetch bone matrix - this is in local bone space for now + var m1 = anim.getBoneMat(bone1); + var m2 = anim.getBoneMat(bone2); + var m1b = anim.getBoneMatBlend(bone1); + var m2b = anim.getBoneMatBlend(bone2); + var a1 = anim.getAbsMat(bone1.parent); + var a2 = anim.getAbsMat(bone2.parent); + + // Rotate hand bones to aim with gun + // Some raw math follows.. + var tx = m1._30; + var ty = m1._31; + var tz = m1._32; + m1._30 = 0; + m1._31 = 0; + m1._32 = 0; + mat.getInverse(a1); + q.fromAxisAngle(mat.right(), -angle); + m1.applyQuat(q); + m1._30 = tx; + m1._31 = ty; + m1._32 = tz; + + var tx = m2._30; + var ty = m2._31; + var tz = m2._32; + m2._30 = 0; + m2._31 = 0; + m2._32 = 0; + mat.getInverse(a2); + var v = mat.right(); + v.mult(-1); + q.fromAxisAngle(v, angle); + m2.applyQuat(q); + m2._30 = tx; + m2._31 = ty; + m2._32 = tz; + + // Animation blending is in progress, we need to rotate those bones too + if (m1b != null && m2b != null) { + var tx = m1b._30; + var ty = m1b._31; + var tz = m1b._32; + m1b._30 = 0; + m1b._31 = 0; + m1b._32 = 0; + mat.getInverse(a1); + q.fromAxisAngle(mat.right(), -angle); + m1b.applyQuat(q); + m1b._30 = tx; + m1b._31 = ty; + m1b._32 = tz; + + var tx = m2b._30; + var ty = m2b._31; + var tz = m2b._32; + m2b._30 = 0; + m2b._31 = 0; + m2b._32 = 0; + mat.getInverse(a2); + var v = mat.right(); + v.mult(-1); + q.fromAxisAngle(v, angle); + m2b.applyQuat(q); + m2b._30 = tx; + m2b._31 = ty; + m2b._32 = tz; + } + } + + function preUpdate() { + if (Input.occupied || !body.ready) return; + + var mouse = Input.getMouse(); + var kb = Input.getKeyboard(); + + if (mouse.started() && !mouse.locked) mouse.lock(); + else if (kb.started("escape") && mouse.locked) mouse.unlock(); + + if (nextFrameRot != 0.0) { + var origin = object.getChild("CameraOrigin"); + origin.transform.rotate(xVec, nextFrameRot); + origin.transform.buildMatrix(); + } + nextFrameRot = 0; + + if (mouse.moved) { + var d = mouse.movementY / 250; + if (angle + d > 1.5) return; + if (angle + d < -0.25) return; + angle += d; + nextFrameRot = -mouse.movementY / 250 * rotationSpeed; + } + + if (mouse.moved) transform.rotate(zVec, -mouse.movementX / 250 * rotationSpeed); + body.syncTransform(); + } + + function update() { + if (!body.ready) return; + var look = armature.transform.look().normalize(); + + // Move + dir.set(0, 0, 0); + if (moveForward) dir.add(transform.look()); + if (moveBackward) dir.add(transform.look().mult(-1)); + if (moveLeft) dir.add(transform.right().mult(-1)); + if (moveRight) dir.add(transform.right()); + + // Push down + var btvec = body.getLinearVelocity(); + body.setLinearVelocity(0.0, 0.0, btvec.z - 1.0); + + if (moveForward || moveBackward || moveLeft || moveRight) { + var action = moveForward ? "run" : + moveBackward ? "back" : + moveLeft ? "left" : "right"; + setState(action); + + var kb = iron.system.Input.getKeyboard(); + if (kb.down("shift")) speed = 1.6; + else speed = 1.0; + + dir.mult(speed * 5); + body.activate(); + body.setLinearVelocity(dir.x, dir.y, btvec.z - 1.0); + + stepTime += Time.delta; + if (stepTime > 0.38 / speed) { + stepTime = 0; + if (soundsLoaded == 2) { + Audio.play(Std.random(2) == 0 ? soundStep0 : soundStep1); + } + } + } + // Play correct state + else if (state != "fire" || state != "idle" || state != "turn") { + var mouse = iron.system.Input.getMouse(); + if (mouse.down("left")) { + firingTime = 0.0; + setState("fire", 2.0); + } + else { + // var angle = getAngle(look, lastLook); + // if (Math.abs(angle) > 0.01) { + // setState("turn", angle > 0 ? 1 : -1, 0); + // turnTime = 0; + // } + // else if (turnTime > 0.25){ + setState("idle", 2.0); + // } + // else turnTime += Time.delta; + } + } + + if (state == "fire") firingTime += Time.delta; + else firingTime = 0.0; + + // Keep vertical + body.setAngularFactor(0, 0, 0); + camera.buildMatrix(); + + lastLook.setFrom(look); + } + + // function getAngle(va:Vec4, vb:Vec4) { + // var vn = Vec4.zAxis(); + // var dot = va.dot(vb); + // var det = va.x * vb.y * vn.z + + // vb.x * vn.y * va.z + + // vn.x * va.y * vb.z - + // va.z * vb.y * vn.x - + // vb.z * vn.y * va.x - + // vn.z * va.y * vb.x; + // return Math.atan2(det, dot); + // } + + function setState(s:String, speed = 1.0, blend = 0.2) { + if (s == state) return; + state = s; + anim.play(s, null, blend, speed); + } +#end +} diff --git a/third_person/third_person.blend b/third_person/third_person.blend new file mode 100644 index 0000000..1334147 Binary files /dev/null and b/third_person/third_person.blend differ diff --git a/third_person_terrain/Assets/aim.png b/third_person_terrain/Assets/aim.png new file mode 100644 index 0000000..55c7558 Binary files /dev/null and b/third_person_terrain/Assets/aim.png differ diff --git a/third_person_terrain/Assets/checker_rough.png b/third_person_terrain/Assets/checker_rough.png new file mode 100644 index 0000000..5fad6a5 Binary files /dev/null and b/third_person_terrain/Assets/checker_rough.png differ diff --git a/third_person_terrain/Assets/finger.jpg b/third_person_terrain/Assets/finger.jpg new file mode 100644 index 0000000..0229042 Binary files /dev/null and b/third_person_terrain/Assets/finger.jpg differ diff --git a/third_person_terrain/Assets/grid.png b/third_person_terrain/Assets/grid.png new file mode 100644 index 0000000..84c984b Binary files /dev/null and b/third_person_terrain/Assets/grid.png differ diff --git a/third_person_terrain/Assets/grid_b.png b/third_person_terrain/Assets/grid_b.png new file mode 100644 index 0000000..00f0673 Binary files /dev/null and b/third_person_terrain/Assets/grid_b.png differ diff --git a/third_person_terrain/Bundled/canvas/MyCanvas.files b/third_person_terrain/Bundled/canvas/MyCanvas.files new file mode 100644 index 0000000..8215e93 --- /dev/null +++ b/third_person_terrain/Bundled/canvas/MyCanvas.files @@ -0,0 +1 @@ +../../Assets/aim.png diff --git a/third_person_terrain/Bundled/canvas/MyCanvas.json b/third_person_terrain/Bundled/canvas/MyCanvas.json new file mode 100644 index 0000000..7e4c337 --- /dev/null +++ b/third_person_terrain/Bundled/canvas/MyCanvas.json @@ -0,0 +1 @@ +{"name":"untitled","x":0,"y":0,"width":1920,"height":1080,"elements":[{"id":0,"type":1,"name":"Image","event":"","x":0,"y":0,"width":60,"height":60,"rotation":0,"text":"Image","asset":"aim.png","progress_at":0,"progress_total":100,"strength":1,"alignment":0,"anchor":4,"parent":null,"children":[],"visible":true}],"assets":[{"name":"aim.png","file":"../../Assets/aim.png","id":0}],"theme":"Default Light"} \ No newline at end of file diff --git a/third_person_terrain/Bundled/fire0.wav b/third_person_terrain/Bundled/fire0.wav new file mode 100644 index 0000000..e591890 Binary files /dev/null and b/third_person_terrain/Bundled/fire0.wav differ diff --git a/third_person_terrain/Bundled/fire1.wav b/third_person_terrain/Bundled/fire1.wav new file mode 100644 index 0000000..cdd0727 Binary files /dev/null and b/third_person_terrain/Bundled/fire1.wav differ diff --git a/third_person_terrain/Bundled/slices/heightmap_01.png b/third_person_terrain/Bundled/slices/heightmap_01.png new file mode 100644 index 0000000..590cbe6 Binary files /dev/null and b/third_person_terrain/Bundled/slices/heightmap_01.png differ diff --git a/third_person_terrain/Bundled/step0.wav b/third_person_terrain/Bundled/step0.wav new file mode 100644 index 0000000..0201d28 Binary files /dev/null and b/third_person_terrain/Bundled/step0.wav differ diff --git a/third_person_terrain/Bundled/step1.wav b/third_person_terrain/Bundled/step1.wav new file mode 100644 index 0000000..c5a5f89 Binary files /dev/null and b/third_person_terrain/Bundled/step1.wav differ diff --git a/third_person_terrain/README.md b/third_person_terrain/README.md new file mode 100644 index 0000000..c85d4d3 --- /dev/null +++ b/third_person_terrain/README.md @@ -0,0 +1,2 @@ +Please check the following example to learn about the terrain construction: +https://github.com/leenkx3d/leenkx_examples/terrain diff --git a/third_person_terrain/Sources/lnx/GunController.hx b/third_person_terrain/Sources/lnx/GunController.hx new file mode 100644 index 0000000..667192a --- /dev/null +++ b/third_person_terrain/Sources/lnx/GunController.hx @@ -0,0 +1,71 @@ +package lnx; + +import iron.Trait; +import iron.system.Input; +import iron.object.Object; +import iron.object.Transform; +import iron.system.Audio; +import iron.system.Time; +import leenkx.trait.physics.RigidBody; + +@:keep +class GunController extends Trait { + +#if (!lnx_physics) + public function new() { super(); } +#else + + @prop + public var fireFreq = 0.2; + + var firePoint:Transform; + var fireStrength = 25; + var lastFire = 0.0; + + var soundFire0:kha.Sound = null; + var soundFire1:kha.Sound = null; + var soundsLoaded = 0; + + public function new() { + super(); + + iron.data.Data.getSound("fire0.wav", function(sound:kha.Sound) { soundFire0 = sound; soundsLoaded++; }); + iron.data.Data.getSound("fire1.wav", function(sound:kha.Sound) { soundFire1 = sound; soundsLoaded++; }); + + notifyOnInit(function() { + firePoint = object.getChild("ProjectileSpawn").transform; + }); + + notifyOnUpdate(function() { + var mouse = Input.getMouse(); + lastFire += Time.delta; + if ((mouse.down("left") && lastFire > fireFreq) || mouse.started("left")) { + shoot(); + if (soundsLoaded == 2) { + Audio.play(Std.random(3) == 0 ? soundFire1 : soundFire0); + } + lastFire = 0.0; + } + }); + } + + function shoot() { + // Spawn projectile + iron.Scene.active.spawnObject("Projectile", null, function(o:Object) { + o.transform.loc.x = firePoint.worldx(); + o.transform.loc.y = firePoint.worldy(); + o.transform.loc.z = firePoint.worldz(); + o.transform.buildMatrix(); + // Apply force + var rb:RigidBody = o.getTrait(RigidBody); + rb.syncTransform(); + var look = object.transform.look().normalize(); + rb.setLinearVelocity(look.x * fireStrength, look.y * fireStrength, look.z * fireStrength); + // Remove projectile after a period of time + kha.Scheduler.addTimeTask(function() { + o.remove(); + }, 10); + }); + } +#end +} diff --git a/third_person_terrain/Sources/lnx/ThirdPersonController.hx b/third_person_terrain/Sources/lnx/ThirdPersonController.hx new file mode 100644 index 0000000..29c7405 --- /dev/null +++ b/third_person_terrain/Sources/lnx/ThirdPersonController.hx @@ -0,0 +1,264 @@ +package lnx; + +import iron.math.Vec4; +import iron.math.Quat; +import iron.math.Mat4; +import iron.system.Input; +import iron.object.Object; +import iron.object.BoneAnimation; +import iron.system.Time; +import iron.system.Audio; +import leenkx.trait.physics.PhysicsWorld; +import leenkx.trait.internal.CameraController; + +class ThirdPersonController extends CameraController { + +#if (!lnx_physics) + public function new() { super(); } +#else + + static inline var rotationSpeed = 1.0; + + var stepTime = 0.0; + var turnTime = 0.0; + + var soundStep0:kha.Sound = null; + var soundStep1:kha.Sound = null; + var soundsLoaded = 0; + + var xVec = Vec4.xAxis(); + var zVec = Vec4.zAxis(); + var angle = 0.0; + var nextFrameRot = 0.0; + var armature:Object; + var anim:BoneAnimation; + var q = new Quat(); + var mat = Mat4.identity(); + + var nextIdle = false; + var firingTime = 0.0; + var speed = 1.0; + var dir = new Vec4(); + var lastLook:Vec4; + var state = "idle"; + + public function new() { + super(); + + iron.data.Data.getSound("step0.wav", function(sound:kha.Sound) { soundStep0 = sound; soundsLoaded++; }); + iron.data.Data.getSound("step1.wav", function(sound:kha.Sound) { soundStep1 = sound; soundsLoaded++; }); + + notifyOnInit(function() { + PhysicsWorld.active.notifyOnPreUpdate(preUpdate); + notifyOnUpdate(update); + + notifyOnRemove(function() { + PhysicsWorld.active.removePreUpdate(preUpdate); + }); + + armature = object.getChild("Armature"); + anim = findAnimation(armature); + anim.notifyOnUpdate(updateBones); + lastLook = armature.transform.look().normalize(); + }); + } + + function findAnimation(o:Object):BoneAnimation { + if (o.animation != null) return cast o.animation; + for (c in o.children) { + var co = findAnimation(c); + if (co != null) return co; + } + return null; + } + + function updateBones() { + + // Fetch bone + var bone1 = anim.getBone("mixamorig:LeftForeLnx"); + var bone2 = anim.getBone("mixamorig:RightForeLnx"); + + // Fetch bone matrix - this is in local bone space for now + var m1 = anim.getBoneMat(bone1); + var m2 = anim.getBoneMat(bone2); + var m1b = anim.getBoneMatBlend(bone1); + var m2b = anim.getBoneMatBlend(bone2); + var a1 = anim.getAbsMat(bone1.parent); + var a2 = anim.getAbsMat(bone2.parent); + + // Rotate hand bones to aim with gun + // Some raw math follows.. + var tx = m1._30; + var ty = m1._31; + var tz = m1._32; + m1._30 = 0; + m1._31 = 0; + m1._32 = 0; + mat.getInverse(a1); + q.fromAxisAngle(mat.right(), -angle); + m1.applyQuat(q); + m1._30 = tx; + m1._31 = ty; + m1._32 = tz; + + var tx = m2._30; + var ty = m2._31; + var tz = m2._32; + m2._30 = 0; + m2._31 = 0; + m2._32 = 0; + mat.getInverse(a2); + var v = mat.right(); + v.mult(-1); + q.fromAxisAngle(v, angle); + m2.applyQuat(q); + m2._30 = tx; + m2._31 = ty; + m2._32 = tz; + + // Animation blending is in progress, we need to rotate those bones too + if (m1b != null && m2b != null) { + var tx = m1b._30; + var ty = m1b._31; + var tz = m1b._32; + m1b._30 = 0; + m1b._31 = 0; + m1b._32 = 0; + mat.getInverse(a1); + q.fromAxisAngle(mat.right(), -angle); + m1b.applyQuat(q); + m1b._30 = tx; + m1b._31 = ty; + m1b._32 = tz; + + var tx = m2b._30; + var ty = m2b._31; + var tz = m2b._32; + m2b._30 = 0; + m2b._31 = 0; + m2b._32 = 0; + mat.getInverse(a2); + var v = mat.right(); + v.mult(-1); + q.fromAxisAngle(v, angle); + m2b.applyQuat(q); + m2b._30 = tx; + m2b._31 = ty; + m2b._32 = tz; + } + } + + function preUpdate() { + if (Input.occupied || !body.ready) return; + + var mouse = Input.getMouse(); + var kb = Input.getKeyboard(); + + if (mouse.started() && !mouse.locked) mouse.lock(); + else if (kb.started("escape") && mouse.locked) mouse.unlock(); + + if (nextFrameRot != 0.0) { + var origin = object.getChild("CameraOrigin"); + origin.transform.rotate(xVec, nextFrameRot); + origin.transform.buildMatrix(); + } + nextFrameRot = 0; + + if (mouse.moved) { + var d = mouse.movementY / 250; + if (angle + d > 1.5) return; + if (angle + d < -0.25) return; + angle += d; + nextFrameRot = -mouse.movementY / 250 * rotationSpeed; + } + + if (mouse.moved) transform.rotate(zVec, -mouse.movementX / 250 * rotationSpeed); + body.syncTransform(); + } + + function update() { + if (!body.ready) return; + var look = armature.transform.look().normalize(); + + // Move + dir.set(0, 0, 0); + if (moveForward) dir.add(transform.look()); + if (moveBackward) dir.add(transform.look().mult(-1)); + if (moveLeft) dir.add(transform.right().mult(-1)); + if (moveRight) dir.add(transform.right()); + + // Push down + var btvec = body.getLinearVelocity(); + body.setLinearVelocity(0.0, 0.0, btvec.z - 1.0); + + if (moveForward || moveBackward || moveLeft || moveRight) { + var action = moveForward ? "run" : + moveBackward ? "back" : + moveLeft ? "left" : "right"; + setState(action); + + var kb = iron.system.Input.getKeyboard(); + if (kb.down("shift")) speed = 1.6; + else speed = 1.0; + + dir.mult(speed * 5); + body.activate(); + body.setLinearVelocity(dir.x, dir.y, btvec.z - 1.0); + + stepTime += Time.delta; + if (stepTime > 0.38 / speed) { + stepTime = 0; + if (soundsLoaded == 2) { + Audio.play(Std.random(2) == 0 ? soundStep0 : soundStep1); + } + } + } + // Play correct state + else if (state != "fire" || state != "idle" || state != "turn") { + var mouse = iron.system.Input.getMouse(); + if (mouse.down("left")) { + firingTime = 0.0; + setState("fire", 2.0); + } + else { + // var angle = getAngle(look, lastLook); + // if (Math.abs(angle) > 0.01) { + // setState("turn", angle > 0 ? 1 : -1, 0); + // turnTime = 0; + // } + // else if (turnTime > 0.25){ + setState("idle", 2.0); + // } + // else turnTime += Time.delta; + } + } + + if (state == "fire") firingTime += Time.delta; + else firingTime = 0.0; + + // Keep vertical + body.setAngularFactor(0, 0, 0); + camera.buildMatrix(); + + lastLook.setFrom(look); + } + + // function getAngle(va:Vec4, vb:Vec4) { + // var vn = Vec4.zAxis(); + // var dot = va.dot(vb); + // var det = va.x * vb.y * vn.z + + // vb.x * vn.y * va.z + + // vn.x * va.y * vb.z - + // va.z * vb.y * vn.x - + // vb.z * vn.y * va.x - + // vn.z * va.y * vb.x; + // return Math.atan2(det, dot); + // } + + function setState(s:String, speed = 1.0, blend = 0.2) { + if (s == state) return; + state = s; + anim.play(s, null, blend, speed); + } +#end +} diff --git a/third_person_terrain/third_person_terrain.blend b/third_person_terrain/third_person_terrain.blend new file mode 100644 index 0000000..e522707 Binary files /dev/null and b/third_person_terrain/third_person_terrain.blend differ diff --git a/top_down/Assets/checker_rough.png b/top_down/Assets/checker_rough.png new file mode 100644 index 0000000..5fad6a5 Binary files /dev/null and b/top_down/Assets/checker_rough.png differ diff --git a/top_down/Assets/finger.jpg b/top_down/Assets/finger.jpg new file mode 100644 index 0000000..0229042 Binary files /dev/null and b/top_down/Assets/finger.jpg differ diff --git a/top_down/Assets/grid.png b/top_down/Assets/grid.png new file mode 100644 index 0000000..84c984b Binary files /dev/null and b/top_down/Assets/grid.png differ diff --git a/top_down/Assets/grid_b.png b/top_down/Assets/grid_b.png new file mode 100644 index 0000000..00f0673 Binary files /dev/null and b/top_down/Assets/grid_b.png differ diff --git a/top_down/Bundled/canvas/MyCanvas.files b/top_down/Bundled/canvas/MyCanvas.files new file mode 100644 index 0000000..e69de29 diff --git a/top_down/Bundled/canvas/MyCanvas.json b/top_down/Bundled/canvas/MyCanvas.json new file mode 100644 index 0000000..f33a3b0 --- /dev/null +++ b/top_down/Bundled/canvas/MyCanvas.json @@ -0,0 +1 @@ +{"name":"untitled","x":0,"y":0,"width":1280,"height":720,"elements":[{"id":15,"type":9,"name":"MenuContainer","event":"","x":10,"y":61,"width":332,"height":402,"rotation":0,"text":"My Shape","asset":"","color":-1258291200,"anchor":0,"parent":null,"children":[2,3,6,8,11,12,13,14],"visible":true},{"id":2,"type":6,"name":"SSR","event":"","x":24,"y":179,"width":150,"height":44,"rotation":0,"text":"SSR","asset":"","color":-1,"anchor":0,"parent":15,"children":[],"visible":true},{"id":3,"type":6,"name":"SSAO","event":"","x":24,"y":229,"width":150,"height":44,"rotation":0,"text":"SSAO","asset":"","color":-1,"anchor":0,"parent":15,"children":[],"visible":true},{"id":6,"type":6,"name":"Bloom","event":"","x":24,"y":129,"width":150,"height":44,"rotation":0,"text":"Bloom","asset":"","color":-1,"anchor":0,"parent":15,"children":[],"visible":true},{"id":8,"type":8,"name":"Shadows","event":"","x":24,"y":79,"width":150,"height":44,"rotation":0,"text":"High;Medium;Low","asset":"","color":-1,"anchor":0,"parent":15,"children":[],"visible":true},{"id":11,"type":0,"name":"Title","event":"","x":4,"y":9,"width":377,"height":44,"rotation":0,"text":"Graphics Settings","asset":"","color":-1,"anchor":0,"parent":15,"children":[],"visible":true},{"id":12,"type":0,"name":"ShadowsTitle","event":"","x":164,"y":79,"width":184,"height":28,"rotation":0,"text":"Shadows","asset":"","color":-6381922,"anchor":0,"parent":15,"children":[],"visible":true},{"id":13,"type":2,"name":"Apply","event":"apply_settings","x":24,"y":329,"width":150,"height":44,"rotation":0,"text":"Apply","asset":"","color":-1,"anchor":0,"parent":15,"children":[],"visible":true},{"id":14,"type":6,"name":"Voxels","event":"","x":24,"y":279,"width":150,"height":44,"rotation":0,"text":"Voxels","asset":"","color":-1,"anchor":0,"parent":15,"children":[],"visible":true},{"id":16,"type":2,"name":"Menu","event":"toggle_menu","x":5,"y":5,"width":150,"height":44,"rotation":0,"text":"Menu","asset":"","color":-1,"anchor":0,"parent":null,"children":[],"visible":true}],"assets":[]} \ No newline at end of file diff --git a/top_down/Bundled/config.lnx b/top_down/Bundled/config.lnx new file mode 100644 index 0000000..55a0a52 --- /dev/null +++ b/top_down/Bundled/config.lnx @@ -0,0 +1,20 @@ +{ + "rp_bloom": true, + "rp_dynres": false, + "rp_gi": true, + "rp_motionblur": false, + "rp_shadowmap_cascade": 2048, + "rp_shadowmap_cube": 512, + "rp_ssgi": true, + "rp_ssr": true, + "rp_supersample": 1.0, + "window_h": 720, + "window_maximizable": false, + "window_minimizable": true, + "window_mode": 0, + "window_msaa": 1, + "window_resizable": false, + "window_scale": 1.0, + "window_vsync": true, + "window_w": 1280 +} \ No newline at end of file diff --git a/top_down/Bundled/step0.wav b/top_down/Bundled/step0.wav new file mode 100644 index 0000000..0201d28 Binary files /dev/null and b/top_down/Bundled/step0.wav differ diff --git a/top_down/Bundled/step1.wav b/top_down/Bundled/step1.wav new file mode 100644 index 0000000..c5a5f89 Binary files /dev/null and b/top_down/Bundled/step1.wav differ diff --git a/top_down/Bundled/sword0.wav b/top_down/Bundled/sword0.wav new file mode 100644 index 0000000..9b6799c Binary files /dev/null and b/top_down/Bundled/sword0.wav differ diff --git a/top_down/Bundled/sword1.wav b/top_down/Bundled/sword1.wav new file mode 100644 index 0000000..94fd858 Binary files /dev/null and b/top_down/Bundled/sword1.wav differ diff --git a/top_down/Sources/lnx/PlayerController.hx b/top_down/Sources/lnx/PlayerController.hx new file mode 100644 index 0000000..df4c05a --- /dev/null +++ b/top_down/Sources/lnx/PlayerController.hx @@ -0,0 +1,136 @@ +package lnx; + +import iron.math.Vec4; +import iron.object.Object; +import iron.object.BoneAnimation; +import iron.system.Time; +import iron.system.Audio; +import iron.system.Input; +import leenkx.trait.physics.RigidBody; + +class PlayerController extends iron.Trait { + +#if (!lnx_physics) + public function new() { super(); } +#else + + var mouse:Mouse = null; + var keyboard:Keyboard = null; + var gamepad:Gamepad = null; + + var body:RigidBody; + var anim:BoneAnimation; + var armature:Object; + + var stepTime = 0.0; + var dir = new Vec4(); + var lastDir = new Vec4(); + var lastLook:Vec4; + var state = "idle"; + + var soundStep0:kha.Sound = null; + var soundStep1:kha.Sound = null; + var soundSword0:kha.Sound = null; + var soundSword1:kha.Sound = null; + var stepSoundsLoaded = 0; + var swordSoundsLoaded = 0; + + public function new() { + super(); + + // Load sounds + iron.data.Data.getSound("step0.wav", function(sound:kha.Sound) { soundStep0 = sound; stepSoundsLoaded++; }); + iron.data.Data.getSound("step1.wav", function(sound:kha.Sound) { soundStep1 = sound; stepSoundsLoaded++; }); + iron.data.Data.getSound("sword0.wav", function(sound:kha.Sound) { soundSword0 = sound; swordSoundsLoaded++; }); + iron.data.Data.getSound("sword1.wav", function(sound:kha.Sound) { soundSword1 = sound; swordSoundsLoaded++; }); + + notifyOnInit(init); + notifyOnUpdate(update); + } + + function init() { + // Get input devices + mouse = Input.getMouse(); + keyboard = Input.getKeyboard(); + gamepad = Input.getGamepad(0); + + // Store references + body = object.getTrait(RigidBody); + armature = object.getChild("Armature"); + anim = cast armature.children[0].animation; + } + + function update() { + // Movement + dir.set(0, 0, 0); + if (keyboard.down("w")) dir.y = 1.0; + if (keyboard.down("s")) dir.y = -1.0; + if (keyboard.down("a")) dir.x = -1.0; + if (keyboard.down("d")) dir.x = 1.0; + if (gamepad != null && Math.abs(gamepad.leftStick.x) > 0.1) dir.x = gamepad.leftStick.x; + if (gamepad != null && Math.abs(gamepad.leftStick.y) > 0.1) dir.y = gamepad.leftStick.y; + dir.normalize(); + + // Rotate + var q = new iron.math.Quat(); + q.fromTo(Vec4.yAxis(), new Vec4(lastDir.x, lastDir.y, 0.0)); + armature.transform.rot.lerp(armature.transform.rot, q, 0.25); + armature.transform.buildMatrix(); + + updateAnim(); + updateBody(); + + if (dir.length() > 0) lastDir.setFrom(dir); + } + + function updateAnim() { + var look = armature.transform.look().normalize(); + + // Move + if (dir.length() > 0) { + setState("run", 1.0); + + // Step sounds + stepTime += Time.delta; + if (stepTime > 0.3) { + stepTime = 0; + if (stepSoundsLoaded == 2) { + Audio.play(Std.random(2) == 0 ? soundStep0 : soundStep1); + } + } + } + // Slash + else if (state == "idle") { + if (mouse.down("left") || (gamepad != null && gamepad.down("r2") > 0.0)) { + var r = Std.random(2); + setState(r == 0 ? "slash" : "slash2", 1.5, 0.0, function() { setState("idle", 1.0, 0.0); }); + iron.system.Tween.timer(0.3, function() { + if (swordSoundsLoaded == 2) { + Audio.play(r == 0 ? soundSword0 : soundSword1); + } + }); + } + } + // Idle + else if (state != "slash" && state != "slash2") { + setState("idle", 1.0); + } + } + + function updateBody() { + if (!body.ready) return; + + body.syncTransform(); + body.activate(); + var linvel = body.getLinearVelocity(); + body.setLinearVelocity(dir.x * 6, dir.y * 6, linvel.z - 1.0); // Push down + body.setAngularFactor(0, 0, 0); // Keep vertical + } + + function setState(s:String, speed:Float, blend = 0.2, onComplete:Void->Void = null) { + if (s == state) return; + state = s; + anim.play(s, onComplete, blend, speed); + } +#end +} diff --git a/top_down/Sources/lnx/SettingsTrait.hx b/top_down/Sources/lnx/SettingsTrait.hx new file mode 100644 index 0000000..dc1f569 --- /dev/null +++ b/top_down/Sources/lnx/SettingsTrait.hx @@ -0,0 +1,68 @@ +package lnx; + +import iron.Scene; +import leenkx.system.Event; +import leenkx.data.Config; +import leenkx.trait.internal.CanvasScript; +import leenkx.renderpath.RenderPathCreator; + +class SettingsTrait extends iron.Trait { + + var canvas:CanvasScript; + var envStrength = 0.0; + + public function new() { + super(); + + notifyOnInit(function() { + canvas = Scene.active.getTrait(CanvasScript); + + // Init UI to values loaded from config.lnx file + canvas.notifyOnReady(function() { + canvas.getElement("MenuContainer").visible = false; + canvas.getHandle("SSAO").selected = Config.raw.rp_ssgi; + canvas.getHandle("SSR").selected = Config.raw.rp_ssr; + canvas.getHandle("Bloom").selected = Config.raw.rp_bloom; + canvas.getHandle("Voxels").selected = Config.raw.rp_gi; + canvas.getHandle("Shadows").position = getShadowQuality(Config.raw.rp_shadowmap_cascade); + }); + + // Button events + Event.add("toggle_menu", toggleMenu); + Event.add("apply_settings", applySettings); + }); + } + + function toggleMenu() { + var e = canvas.getElement("MenuContainer"); + e.visible = !e.visible; + } + + function applySettings() { + + // Apply render path settings + Config.raw.rp_ssgi = canvas.getHandle("SSAO").selected; + Config.raw.rp_ssr = canvas.getHandle("SSR").selected; + Config.raw.rp_bloom = canvas.getHandle("Bloom").selected; + Config.raw.rp_gi = canvas.getHandle("Voxels").selected; + Config.raw.rp_shadowmap_cascade = getShadowMapSize(canvas.getHandle("Shadows").position); + RenderPathCreator.applyConfig(); + + // Lower envmap strength when voxel ao is disabled + var p = iron.Scene.active.world.probe.raw; + if (envStrength == 0) envStrength = p.strength; + p.strength = Config.raw.rp_gi ? envStrength : envStrength / 3; + + // Save config.lnx file + Config.save(); + } + + inline function getShadowQuality(i:Int):Int { + // 0 - High, 1 - Medium, 2 - Low + return i == 4096 ? 0 : i == 2048 ? 1 : 2; + } + + inline function getShadowMapSize(i:Int):Int { + return i == 0 ? 4096 : i == 1 ? 2048 : 1024; + } +} diff --git a/top_down/top_down.blend b/top_down/top_down.blend new file mode 100644 index 0000000..d2993cb Binary files /dev/null and b/top_down/top_down.blend differ diff --git a/twin_stick/Assets/checker_rough.png b/twin_stick/Assets/checker_rough.png new file mode 100644 index 0000000..5fad6a5 Binary files /dev/null and b/twin_stick/Assets/checker_rough.png differ diff --git a/twin_stick/Assets/finger.jpg b/twin_stick/Assets/finger.jpg new file mode 100644 index 0000000..0229042 Binary files /dev/null and b/twin_stick/Assets/finger.jpg differ diff --git a/twin_stick/Assets/grid.png b/twin_stick/Assets/grid.png new file mode 100644 index 0000000..84c984b Binary files /dev/null and b/twin_stick/Assets/grid.png differ diff --git a/twin_stick/Assets/grid_b.png b/twin_stick/Assets/grid_b.png new file mode 100644 index 0000000..00f0673 Binary files /dev/null and b/twin_stick/Assets/grid_b.png differ diff --git a/twin_stick/Bundled/canvas/MyCanvas.files b/twin_stick/Bundled/canvas/MyCanvas.files new file mode 100644 index 0000000..e69de29 diff --git a/twin_stick/Bundled/canvas/MyCanvas.json b/twin_stick/Bundled/canvas/MyCanvas.json new file mode 100644 index 0000000..f33a3b0 --- /dev/null +++ b/twin_stick/Bundled/canvas/MyCanvas.json @@ -0,0 +1 @@ +{"name":"untitled","x":0,"y":0,"width":1280,"height":720,"elements":[{"id":15,"type":9,"name":"MenuContainer","event":"","x":10,"y":61,"width":332,"height":402,"rotation":0,"text":"My Shape","asset":"","color":-1258291200,"anchor":0,"parent":null,"children":[2,3,6,8,11,12,13,14],"visible":true},{"id":2,"type":6,"name":"SSR","event":"","x":24,"y":179,"width":150,"height":44,"rotation":0,"text":"SSR","asset":"","color":-1,"anchor":0,"parent":15,"children":[],"visible":true},{"id":3,"type":6,"name":"SSAO","event":"","x":24,"y":229,"width":150,"height":44,"rotation":0,"text":"SSAO","asset":"","color":-1,"anchor":0,"parent":15,"children":[],"visible":true},{"id":6,"type":6,"name":"Bloom","event":"","x":24,"y":129,"width":150,"height":44,"rotation":0,"text":"Bloom","asset":"","color":-1,"anchor":0,"parent":15,"children":[],"visible":true},{"id":8,"type":8,"name":"Shadows","event":"","x":24,"y":79,"width":150,"height":44,"rotation":0,"text":"High;Medium;Low","asset":"","color":-1,"anchor":0,"parent":15,"children":[],"visible":true},{"id":11,"type":0,"name":"Title","event":"","x":4,"y":9,"width":377,"height":44,"rotation":0,"text":"Graphics Settings","asset":"","color":-1,"anchor":0,"parent":15,"children":[],"visible":true},{"id":12,"type":0,"name":"ShadowsTitle","event":"","x":164,"y":79,"width":184,"height":28,"rotation":0,"text":"Shadows","asset":"","color":-6381922,"anchor":0,"parent":15,"children":[],"visible":true},{"id":13,"type":2,"name":"Apply","event":"apply_settings","x":24,"y":329,"width":150,"height":44,"rotation":0,"text":"Apply","asset":"","color":-1,"anchor":0,"parent":15,"children":[],"visible":true},{"id":14,"type":6,"name":"Voxels","event":"","x":24,"y":279,"width":150,"height":44,"rotation":0,"text":"Voxels","asset":"","color":-1,"anchor":0,"parent":15,"children":[],"visible":true},{"id":16,"type":2,"name":"Menu","event":"toggle_menu","x":5,"y":5,"width":150,"height":44,"rotation":0,"text":"Menu","asset":"","color":-1,"anchor":0,"parent":null,"children":[],"visible":true}],"assets":[]} \ No newline at end of file diff --git a/twin_stick/Bundled/fire0.wav b/twin_stick/Bundled/fire0.wav new file mode 100644 index 0000000..e591890 Binary files /dev/null and b/twin_stick/Bundled/fire0.wav differ diff --git a/twin_stick/Bundled/fire1.wav b/twin_stick/Bundled/fire1.wav new file mode 100644 index 0000000..cdd0727 Binary files /dev/null and b/twin_stick/Bundled/fire1.wav differ diff --git a/twin_stick/Bundled/step0.wav b/twin_stick/Bundled/step0.wav new file mode 100644 index 0000000..0201d28 Binary files /dev/null and b/twin_stick/Bundled/step0.wav differ diff --git a/twin_stick/Bundled/step1.wav b/twin_stick/Bundled/step1.wav new file mode 100644 index 0000000..c5a5f89 Binary files /dev/null and b/twin_stick/Bundled/step1.wav differ diff --git a/twin_stick/Sources/lnx/GunController.hx b/twin_stick/Sources/lnx/GunController.hx new file mode 100644 index 0000000..7bb0119 --- /dev/null +++ b/twin_stick/Sources/lnx/GunController.hx @@ -0,0 +1,73 @@ +package lnx; + +import iron.Trait; +import iron.system.Input; +import iron.system.Audio; +import iron.system.Time; +import iron.object.Object; +import iron.object.Transform; +import leenkx.trait.physics.RigidBody; + +class GunController extends Trait { + +#if (!lnx_physics) + public function new() { super(); } +#else + + @prop + public var fireFreq = 0.15; + var firePoint:Transform; + var lastFire = 0.0; + + var soundFire0:kha.Sound = null; + var soundFire1:kha.Sound = null; + var soundsLoaded = 0; + + public function new() { + super(); + + iron.data.Data.getSound("fire0.wav", function(sound:kha.Sound) { soundFire0 = sound; soundsLoaded++; }); + iron.data.Data.getSound("fire1.wav", function(sound:kha.Sound) { soundFire1 = sound; soundsLoaded++; }); + + var mouse = Input.getMouse(); + var gamepad = Input.getGamepad(0); + + notifyOnInit(function() { + firePoint = object.getChild('ProjectileSpawn').transform; + }); + + notifyOnUpdate(function() { + lastFire += Time.delta; + if (lastFire > fireFreq) { + if (mouse.down("left") || (gamepad != null && gamepad.down("r2") > 0.0)) { + shoot(); + } + } + }); + } + + function shoot() { + // Spawn projectile + iron.Scene.active.spawnObject('Projectile', null, function(o:Object) { + o.transform.loc.x = firePoint.worldx(); + o.transform.loc.y = firePoint.worldy(); + o.transform.loc.z = firePoint.worldz(); + o.transform.buildMatrix(); + // Apply force + var rb:RigidBody = o.getTrait(RigidBody); + rb.syncTransform(); + var look = object.transform.look().normalize(); + rb.setLinearVelocity(look.x * 15, look.y * 15, look.z * 15); + // Remove projectile after a period of time + kha.Scheduler.addTimeTask(o.remove, 10); + }); + + // Play sound + if (soundsLoaded == 2) { + Audio.play(Std.random(3) == 0 ? soundFire1 : soundFire0); + } + + lastFire = 0.0; + } +#end +} diff --git a/twin_stick/Sources/lnx/PlayerController.hx b/twin_stick/Sources/lnx/PlayerController.hx new file mode 100644 index 0000000..2521601 --- /dev/null +++ b/twin_stick/Sources/lnx/PlayerController.hx @@ -0,0 +1,185 @@ +package lnx; + +import iron.math.Vec4; +import iron.math.Vec2; +import iron.math.RayCaster; +import iron.Scene; +import iron.object.Object; +import iron.object.BoneAnimation; +import iron.system.Time; +import iron.system.Audio; +import iron.system.Input; +import leenkx.trait.physics.RigidBody; + +class PlayerController extends iron.Trait { + +#if (!lnx_physics) + public function new() { super(); } +#else + + var soundStep0:kha.Sound = null; + var soundStep1:kha.Sound = null; + var soundsLoaded = 0; + + var mouse:Mouse = null; + var keyboard:Keyboard = null; + var gamepad:Gamepad = null; + + var body:RigidBody; + var anim:BoneAnimation; + var armature:Object; + + var stepTime = 0.0; + var turnTime = 0.0; + var dir = new Vec4(); + var lastLook:Vec4; + var state = "idle"; + + public function new() { + super(); + + // Load sounds + iron.data.Data.getSound("step0.wav", function(sound:kha.Sound) { soundStep0 = sound; soundsLoaded++; }); + iron.data.Data.getSound("step1.wav", function(sound:kha.Sound) { soundStep1 = sound; soundsLoaded++; }); + + notifyOnInit(init); + notifyOnUpdate(update); + } + + function init() { + // Get input devices + mouse = Input.getMouse(); + keyboard = Input.getKeyboard(); + gamepad = Input.getGamepad(0); + + // Store references + body = object.getTrait(RigidBody); + armature = object.getChild("Armature"); + anim = cast armature.children[0].animation; + lastLook = armature.transform.look().normalize(); + } + + function update() { + // Movement + dir.set(0, 0, 0); + if (keyboard.down("w")) dir.y = 1.0; + if (keyboard.down("s")) dir.y = -1.0; + if (keyboard.down("a")) dir.x = -1.0; + if (keyboard.down("d")) dir.x = 1.0; + if (gamepad != null && Math.abs(gamepad.leftStick.x) > 0.1) dir.x = gamepad.leftStick.x; + if (gamepad != null && Math.abs(gamepad.leftStick.y) > 0.1) dir.y = gamepad.leftStick.y; + dir.normalize(); + + // Mouse control + var mouse_pos = new Vec2(mouse.x,mouse.y); + var hit_pos = project_mouse_pos(mouse_pos); + + if (hit_pos != null) + { + var center = new Vec4(hit_pos.x,hit_pos.y,0); + var eye = armature.transform.world.getLoc(); + eye.set(eye.x,eye.y,0); + + var target = center.sub(eye); + + armature.transform.rot.fromTo(Vec4.yAxis(), target.normalize()); + } + + // Gamepad control + if (gamepad != null) { + if (Math.abs(gamepad.rightStick.x) > 0.7 || Math.abs(gamepad.rightStick.y) > 0.7) { + armature.transform.rot.fromTo(Vec4.yAxis(), new Vec4(gamepad.rightStick.x, gamepad.rightStick.y, 0.0)); + } + } + + armature.transform.buildMatrix(); + updateAnim(); + updateBody(); + } + + function project_mouse_pos(input:Vec2){ + var camera = Scene.active.camera; + + var start = new Vec4(); + var end = new Vec4(); + + var hit_pos = RayCaster.planeIntersect(Vec4.zAxis(),new Vec4(0,0,1),input.x,input.y,camera); + return hit_pos; + } + + function getAngle(va:Vec4, vb:Vec4) { + var vn = Vec4.zAxis(); + var dot = va.dot(vb); + var det = va.x * vb.y * vn.z + + vb.x * vn.y * va.z + + vn.x * va.y * vb.z - + va.z * vb.y * vn.x - + vb.z * vn.y * va.x - + vn.z * va.y * vb.x; + return Math.atan2(det, dot); + } + + function updateAnim() { + var look = armature.transform.look().normalize(); + + // Move + if (dir.length() > 0) { + var action = ""; + // Angle from look direction to move direction + // 0 to PI * 2 + var angle = getAngle(dir, look) + Math.PI; + var step = Math.PI / 4; + if (angle < step) action = "back"; + else if (angle < step * 3) action = "left"; + else if (angle < step * 5) action = "run"; + else if (angle < step * 7) action = "right"; + else action = "back"; + + setState(action, 1.0); + + // Step sounds + stepTime += Time.delta; + if (stepTime > 0.3) { + stepTime = 0; + if (soundsLoaded == 2) { + Audio.play(Std.random(2) == 0 ? soundStep0 : soundStep1); + } + } + } + // Shoot + else if (mouse.down("left") || (gamepad != null && gamepad.down("r2") > 0.0)) { + setState("fire", 2.0); + } + // Idle + else { + var angle = getAngle(look, lastLook); + if (Math.abs(angle) > 0.01) { + setState("turn", angle > 0 ? 1 : -1, 0); + turnTime = 0; + } + else if (turnTime > 0.25){ + setState("idle", 2.0); + } + else turnTime += Time.delta; + } + + lastLook.setFrom(look); + } + + function updateBody() { + if (!body.ready) return; + + body.syncTransform(); + body.activate(); + var linvel = body.getLinearVelocity(); + body.setLinearVelocity(dir.x * 6, dir.y * 6, linvel.z - 1.0); // Push down + body.setAngularFactor(0, 0, 0); // Keep vertical + } + + function setState(s:String, speed:Float, blend = 0.2) { + if (s == state) return; + state = s; + anim.play(s, null, blend, speed); + } +#end +} diff --git a/twin_stick/Sources/lnx/SettingsTrait.hx b/twin_stick/Sources/lnx/SettingsTrait.hx new file mode 100644 index 0000000..dc1f569 --- /dev/null +++ b/twin_stick/Sources/lnx/SettingsTrait.hx @@ -0,0 +1,68 @@ +package lnx; + +import iron.Scene; +import leenkx.system.Event; +import leenkx.data.Config; +import leenkx.trait.internal.CanvasScript; +import leenkx.renderpath.RenderPathCreator; + +class SettingsTrait extends iron.Trait { + + var canvas:CanvasScript; + var envStrength = 0.0; + + public function new() { + super(); + + notifyOnInit(function() { + canvas = Scene.active.getTrait(CanvasScript); + + // Init UI to values loaded from config.lnx file + canvas.notifyOnReady(function() { + canvas.getElement("MenuContainer").visible = false; + canvas.getHandle("SSAO").selected = Config.raw.rp_ssgi; + canvas.getHandle("SSR").selected = Config.raw.rp_ssr; + canvas.getHandle("Bloom").selected = Config.raw.rp_bloom; + canvas.getHandle("Voxels").selected = Config.raw.rp_gi; + canvas.getHandle("Shadows").position = getShadowQuality(Config.raw.rp_shadowmap_cascade); + }); + + // Button events + Event.add("toggle_menu", toggleMenu); + Event.add("apply_settings", applySettings); + }); + } + + function toggleMenu() { + var e = canvas.getElement("MenuContainer"); + e.visible = !e.visible; + } + + function applySettings() { + + // Apply render path settings + Config.raw.rp_ssgi = canvas.getHandle("SSAO").selected; + Config.raw.rp_ssr = canvas.getHandle("SSR").selected; + Config.raw.rp_bloom = canvas.getHandle("Bloom").selected; + Config.raw.rp_gi = canvas.getHandle("Voxels").selected; + Config.raw.rp_shadowmap_cascade = getShadowMapSize(canvas.getHandle("Shadows").position); + RenderPathCreator.applyConfig(); + + // Lower envmap strength when voxel ao is disabled + var p = iron.Scene.active.world.probe.raw; + if (envStrength == 0) envStrength = p.strength; + p.strength = Config.raw.rp_gi ? envStrength : envStrength / 3; + + // Save config.lnx file + Config.save(); + } + + inline function getShadowQuality(i:Int):Int { + // 0 - High, 1 - Medium, 2 - Low + return i == 4096 ? 0 : i == 2048 ? 1 : 2; + } + + inline function getShadowMapSize(i:Int):Int { + return i == 0 ? 4096 : i == 1 ? 2048 : 1024; + } +} diff --git a/twin_stick/twin_stick.blend b/twin_stick/twin_stick.blend new file mode 100644 index 0000000..d2cb07c Binary files /dev/null and b/twin_stick/twin_stick.blend differ