Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions kool-demo/src/commonMain/kotlin/de/fabmax/kool/demo/Demos.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import de.fabmax.kool.demo.helloworld.*
import de.fabmax.kool.demo.pathtracing.PathTracingDemo
import de.fabmax.kool.demo.pbr.PbrDemo
import de.fabmax.kool.demo.physics.box2d.mixer.MixerDemo
import de.fabmax.kool.demo.physics.box2d.platformer.PlatformerDemo
import de.fabmax.kool.demo.physics.collision.CollisionDemo
import de.fabmax.kool.demo.physics.joints.JointsDemo
import de.fabmax.kool.demo.physics.manybodies.ManyBodiesDemo
Expand Down Expand Up @@ -56,6 +57,7 @@ object Demos {
entry("phys-joints", "Chain Drive") { JointsDemo() }
entry("physics", "Rigid Bodies") { CollisionDemo() }
entry("mixer2d", "2D Mixer") { MixerDemo() }
entry("platformer2d", "2D Platformer") { PlatformerDemo() }
}

val graphicsDemos = Category("Graphics", false, 0.25f, 0.45f).apply {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package de.fabmax.kool.demo.physics.box2d.platformer

import de.fabmax.kool.KoolContext
import de.fabmax.kool.demo.DemoScene
import de.fabmax.kool.math.Vec2f
import de.fabmax.kool.math.Vec3f
import de.fabmax.kool.modules.ksl.KslUnlitShader
import de.fabmax.kool.physics2d.BodyType
import de.fabmax.kool.physics2d.ChainDef
import de.fabmax.kool.physics2d.Geometry
import de.fabmax.kool.physics2d.Physics2dWorld
import de.fabmax.kool.physics2d.createBody
import de.fabmax.kool.scene.OrbitInputTransform
import de.fabmax.kool.scene.Scene
import de.fabmax.kool.scene.addColorMesh
import de.fabmax.kool.scene.addLineMesh
import de.fabmax.kool.scene.addPointMesh
import de.fabmax.kool.scene.defaultOrbitCamera
import de.fabmax.kool.scene.zPlanePan
import de.fabmax.kool.util.Color
import de.fabmax.kool.util.InterpolatableSimulation

private val chains = (100 downTo -100 step 20).map {
Vec3f(it.toFloat(), 0f, 0f)
}

class PlatformerDemo : DemoScene("Box2D Platformer Demo") {
private val world = Physics2dWorld()

override fun Scene.setupMainScene(ctx: KoolContext) {
defaultOrbitCamera(0f, 0f).apply {
maxZoom = 150.0
zoom = 75.0
setTranslation(0f, 25f, 0f)
panMethod = zPlanePan()
leftDragMethod = OrbitInputTransform.DragMethod.PAN
rightDragMethod = OrbitInputTransform.DragMethod.NONE
}

world.registerHandlers(this)

addLineMesh {
for (i in 0 until chains.size - 1) {
addLine(chains[i], chains[i + 1], Color.GREEN)
}

shader = KslUnlitShader {
color { vertexColor() }
pipeline {
lineWidth = 1f
}
}
}

addPointMesh {
chains.forEach {
addPoint(it, 10f, Color.RED)
}
}

val playerBody = world.createBody(BodyType.Dynamic, Vec2f(10f, 10f))
playerBody.attachShape(Geometry.Box(2.5f, 2.5f))

val chainBody = world.createBody(BodyType.Static, Vec2f(0f, 0f))
val chainDef = ChainDef(chains.map { Vec2f(it.x, it.y) })
chainBody.attachChain(chainDef)

val mesh = addColorMesh {
generate {
rect {
size.set(5f, 5f)
}
}

shader = KslUnlitShader {
color { Color.WHITE }
}
}

world.simulationListeners += object : InterpolatableSimulation {
override fun simulateStep(timeStep: Float) {
mesh.transform.setIdentity().translate(playerBody.position.x, playerBody.position.y, 0f)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ enum class BodyType {

class Body internal constructor(bodyDef: BodyDef, internal val bodyId: BodyId) {
private val shapes = mutableMapOf<ShapeId, Pair<Geometry, ShapeDef>>()
private val chains = mutableListOf<ChainId>()
val type: BodyType = bodyDef.type

var isValid = true; private set
Expand Down Expand Up @@ -81,6 +82,11 @@ class Body internal constructor(bodyDef: BodyDef, internal val bodyId: BodyId) {
shapes[shapeId] = geometry to shapeDef
}

fun attachChain(chainDef: ChainDef) {
val chainId = bodyId.addChain(chainDef)
chains += chainId
}

fun setPose(position: Vec2f, rotation: Rotation) {
checkIsValid()
bodyId.setPose(Pose2f(position, rotation))
Expand Down Expand Up @@ -119,6 +125,8 @@ internal expect fun BodyId.setAngularVelocity(angularVelocity: Float)

internal expect fun BodyId.setTargetTransform(target: Pose2f, duration: Float)

internal expect fun BodyId.addChain(chainDef: ChainDef): ChainId

internal expect object BodyDefDefaults {
val linearVelocity: Vec2f
val angularVelocity: Float
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,8 @@ internal data class WorldId(val id: Long) {
override fun equals(other: Any?): Boolean = (other as? WorldId)?.id == id
override fun hashCode(): Int = id.toInt() xor (id ushr 32).toInt()
}

internal data class ChainId(val id: Long) {
override fun equals(other: Any?): Boolean = (other as? ChainId)?.id == id
override fun hashCode(): Int = id.toInt() xor (id ushr 32).toInt()
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package de.fabmax.kool.physics2d

import de.fabmax.kool.math.Vec2f

data class ShapeDef(
val material: SurfaceMaterial = SurfaceMaterial.DEFAULT,
val density: Float = ShapeDefDefaults.density,
Expand All @@ -17,6 +19,13 @@ data class ShapeDef(
}
}

data class ChainDef(
val points: List<Vec2f>,
val isLoop: Boolean = false,
val material: SurfaceMaterial = SurfaceMaterial.DEFAULT,
val filter: Filter = Filter.DEFAULT,
)

data class Filter(
val categoryBits: Long = ShapeDefDefaults.categoryBits,
val groupIndex: Int = ShapeDefDefaults.groupIndex,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,20 @@ internal actual fun createBody(bodyDef: BodyDef, worldId: WorldId): BodyId = sco
BodyId(B2_Body.createBody(worldId.id, b2BodyDef))
}

internal actual fun BodyId.addChain(chainDef: ChainDef): ChainId = scopedMem {
val b2ChainDef = allocChainDef()
B2_Chain.defaultChainDef(b2ChainDef)

val points = allocVec2Array(chainDef.points)

b2ChainDef.points = points.get(0)
b2ChainDef.count = points.length
b2ChainDef.isLoop = chainDef.isLoop

val chainId = B2_Chain.createChain(id, b2ChainDef)
ChainId(chainId)
}

internal actual fun BodyId.destroy() = B2_Body.destroyBody(id)

internal actual fun BodyId.addShape(geometry: Geometry, shapeDef: ShapeDef): ShapeId = scopedMem {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,14 @@ fun MemoryStack.allocVec2(x: Float, y: Float) = b2Vec2.createAt(this, MemoryStac
it.y = y
}

fun MemoryStack.allocVec2Array(points: List<Vec2f>) =
b2Vec2Array.createAt(this, MemoryStack::nmalloc, points.size).also {
for (i in points.indices) {
val point = allocVec2(points[i].x, points[i].y)
it.set(i, point)
}
}

fun MemoryStack.allocRotation(r: Rotation) = b2Rot.createAt(this, MemoryStack::nmalloc).also {
it.s = r.sin
it.c = r.cos
Expand Down Expand Up @@ -43,6 +51,7 @@ fun MemoryStack.allocCircle(radius: Float) = b2Circle.createAt(this, MemoryStack
it.radius = radius
}

fun MemoryStack.allocChainDef() = b2ChainDef.createAt(this, MemoryStack::nmalloc)
fun MemoryStack.allocBodyDef() = b2BodyDef.createAt(this, MemoryStack::nmalloc)
fun MemoryStack.allocShapeDef() = b2ShapeDef.createAt(this, MemoryStack::nmalloc)
fun MemoryStack.allocWordDef() = b2WorldDef.createAt(this, MemoryStack::nmalloc)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package de.fabmax.kool.physics2d

import box2d.*
import box2d.prototypes.B2_Body
import box2d.prototypes.B2_Chain
import box2d.prototypes.B2_Geometry
import box2d.prototypes.B2_Shape
import de.fabmax.kool.math.MutableVec2f
Expand Down Expand Up @@ -67,6 +68,20 @@ internal actual fun BodyId.addShape(geometry: Geometry, shapeDef: ShapeDef): Sha
ShapeId(shapeId)
}

internal actual fun BodyId.addChain(chainDef: ChainDef): ChainId = scopedMem {
val b2ChainDef = allocChainDef()
B2_Chain.defaultChainDef(b2ChainDef)

val points = allocVec2Array(chainDef.points)

b2ChainDef.points = points.get(0)
b2ChainDef.count = points.length
b2ChainDef.isLoop = chainDef.isLoop

val chainId = B2_Chain.createChain(id, b2ChainDef)
ChainId(chainId)
}

internal actual fun BodyId.getPose(result: MutablePose2f) {
val pos = B2_Body.getPosition(id)
val rot = B2_Body.getRotation(id)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@ fun ScopedMemory.allocVec2(x: Float, y: Float) = autoDelete(b2Vec2(), b2Vec2::de
it.y = y
}

fun ScopedMemory.allocVec2Array(points: List<Vec2f>) = autoDelete(b2Vec2Array(points.size), b2Vec2Array::destroy).also {
for (i in points.indices) {
val point = allocVec2(points[i].x, points[i].y)
it.set(i, point)
}
}

fun ScopedMemory.allocRotation(r: Rotation) = autoDelete(b2Rot(), b2Rot::destroy).also {
it.s = r.sin
it.c = r.cos
Expand Down Expand Up @@ -44,6 +51,7 @@ fun ScopedMemory.allocCircle(radius: Float) = autoDelete(b2Circle(), b2Circle::d
it.radius = radius
}

fun ScopedMemory.allocChainDef() = autoDelete(b2ChainDef(), b2ChainDef::destroy)
fun ScopedMemory.allocBodyDef() = autoDelete(b2BodyDef(), b2BodyDef::destroy)
fun ScopedMemory.allocShapeDef() = autoDelete(b2ShapeDef(), b2ShapeDef::destroy)
fun ScopedMemory.allocWordDef() = autoDelete(b2WorldDef(), b2WorldDef::destroy)
Expand Down
Loading