From 37ee76e9fcf9855aebc7f5c51dd871b8a81b59b5 Mon Sep 17 00:00:00 2001 From: Edgaru089 Date: Tue, 1 Feb 2022 23:48:39 +0800 Subject: [PATCH] entity: physics rewrite --- internal/entity/.physics.go.swp | Bin 20480 -> 0 bytes internal/entity/entities/player.go | 7 +- internal/entity/entity.go | 25 +- internal/entity/physics.go | 379 ++++++++++++----------------- internal/game/imgui.go | 4 +- internal/game/logic.go | 3 +- internal/util/itype/rect.go | 6 +- internal/util/itype/vec.go | 8 + internal/world/block.go | 25 +- 9 files changed, 201 insertions(+), 256 deletions(-) delete mode 100644 internal/entity/.physics.go.swp diff --git a/internal/entity/.physics.go.swp b/internal/entity/.physics.go.swp deleted file mode 100644 index 594ecabaab3497e6dd71ffd2da55e78fb63a9f52..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20480 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 WtC!{jy5x(<aBL4--5h+Lj diff --git a/internal/entity/entities/player.go b/internal/entity/entities/player.go index 03b4a19..8624796 100644 --- a/internal/entity/entities/player.go +++ b/internal/entity/entities/player.go @@ -16,8 +16,11 @@ func init() { func (PlayerBehaviour) Name() string { return "player" } -func (PlayerBehaviour) Hitbox(pos itype.Vec3d, dataset itype.Dataset) itype.Vec3d { - return itype.Vec3d{0.6, 1.8, 0.6} +func (PlayerBehaviour) Hitbox(pos itype.Vec3d, dataset itype.Dataset) []itype.Boxd { + return []itype.Boxd{{ + OffX: -0.3, OffY: 0, OffZ: -0.3, + SizeX: 0.6, SizeY: 1.8, SizeZ: 0.6, + }} } func (PlayerBehaviour) EyeHeight(pos itype.Vec3d, dataset itype.Dataset) float64 { diff --git a/internal/entity/entity.go b/internal/entity/entity.go index c60bdb4..81723c7 100644 --- a/internal/entity/entity.go +++ b/internal/entity/entity.go @@ -3,27 +3,26 @@ package entity import ( "time" - "edgaru089.ml/go/gl01/internal/util" "edgaru089.ml/go/gl01/internal/util/itype" "edgaru089.ml/go/gl01/internal/world" ) // EntityBehaviour describes the behaviour of a type of entity with the same Name. +// It should hold no data of its own. type EntityBehaviour interface { // Name returns the type Name of the behaviour. Name() string - // Hitbox gets the size of the hitbox of the entity. + // Hitbox gets the hitbox(s) of the entity. // - // The hitbox is positioned with the position coordinates pointing to - // the center of the bottom of the box. - Hitbox(pos itype.Vec3d, dataset itype.Dataset) itype.Vec3d + // The hitbox is in entity-local coordinates, originating from the position. + Hitbox(pos itype.Vec3d, dataset itype.Dataset) []itype.Boxd // EyeHeight gets the height of the eye of the entity. EyeHeight(pos itype.Vec3d, dataset itype.Dataset) float64 // Update is called on every frame. - // It should be used to update physics. + // It should be used to update the behaviour of the entity. Update(pos itype.Vec3d, dataset itype.Dataset, world *world.World, deltaTime time.Duration) } @@ -45,14 +44,14 @@ func RegisterEntityBehaviour(b EntityBehaviour) bool { // Entity is a struct holding a Behaviour, a Position and a Speed. type Entity struct { b EntityBehaviour - pos, speed itype.Vec3d // pos has a origin of the **center of the bottom** of the hitbox + pos, speed itype.Vec3d // pos should have a origin of the **center of the bottom** of the hitbox ds itype.Dataset name string // a shortcut to b.Name(), the typename + // physics stuff onGround bool worldbox []itype.Boxd - hp []itype.Vec3d } func NewEntity(typename string, pos itype.Vec3d) *Entity { @@ -84,16 +83,16 @@ func (e *Entity) SetSpeed(speed itype.Vec3d) { e.speed = speed } -func (e *Entity) Hitbox() itype.Vec3d { +func (e *Entity) Hitbox() []itype.Boxd { return e.b.Hitbox(e.pos, e.ds) } func (e *Entity) Accelerate(x, y, z float64) { - /*e.speed[0] += x + e.speed[0] += x e.speed[1] += y - e.speed[2] += z*/ - vec := itype.Vec3d{x, y, z} - e.speed = util.BunnyhopAccelerate(vec, e.speed, vec.Length(), 8) + e.speed[2] += z + /*vec := itype.Vec3d{x, y, z} + e.speed = util.BunnyhopAccelerate(vec, e.speed, vec.Length(), 8)*/ } func (e *Entity) OnGround() bool { diff --git a/internal/entity/physics.go b/internal/entity/physics.go index 794001d..e2726df 100644 --- a/internal/entity/physics.go +++ b/internal/entity/physics.go @@ -9,262 +9,187 @@ import ( "edgaru089.ml/go/gl01/internal/world" ) -func (e *Entity) worldHitbox(hitbox itype.Vec3d) itype.Boxd { - return itype.Boxd{ - OffX: e.pos[0] - hitbox[0]/2, - OffY: e.pos[1], - OffZ: e.pos[2] - hitbox[2]/2, - SizeX: hitbox[0], - SizeY: hitbox[1], - SizeZ: hitbox[2], +// WorldHitbox returns the hitboxes of the entity, in world coordinates. +func (e *Entity) WorldHitbox() []itype.Boxd { + boxes := e.Hitbox() + for i := range boxes { + boxes[i] = boxes[i].Offset(e.pos) } -} - -// WorldHitbox returns the hitbox of the entity, in world coordinates. -func (e *Entity) WorldHitbox() itype.Boxd { - return e.worldHitbox(e.b.Hitbox(e.pos, e.ds)) -} - -// the distance between hitpoints, in blocks -const HitpointMeshLen = 0.4 - -func (e *Entity) boxHitpoints(points []itype.Vec3d, hitbox itype.Boxd) []itype.Vec3d { - - box := hitbox - base := box.MinPoint() - points = points[0:0] - - // add the bounding points first - points = append(points, - base, - base.Add(itype.Vec3d{box.SizeX, 0, 0}), - base.Add(itype.Vec3d{0, box.SizeY, 0}), - base.Add(itype.Vec3d{0, 0, box.SizeZ}), - base.Add(itype.Vec3d{box.SizeX, box.SizeY, 0}), - base.Add(itype.Vec3d{0, box.SizeY, box.SizeZ}), - base.Add(itype.Vec3d{box.SizeX, 0, box.SizeZ}), - base.Add(itype.Vec3d{box.SizeX, box.SizeY, box.SizeZ}), - ) - return points + return boxes } // MoveEps is a small value used to indicate a tiny amount more than zero. const MoveEps = 1e-7 -// tells if the point is already stuck inside a block hitbox -func pointStuck(point itype.Vec3d, w *world.World) bool { - blockid := point.Floor() - block := w.Block(blockid) - return block.Id != 0 && - block.Behaviour.Appearance( - point.Floor(), - block.Aux, - block.Dataset, - w, - ).Hitbox.Offset(blockid.ToFloat64()).Contains(point) +// move attempts to move the entity by delta in the world. +// It does not change anything in e. +func (e *Entity) move(delta itype.Vec3d, w *world.World) (finDelta itype.Vec3d, finVelocity itype.Vec3d, onGround bool) { + + // Get the hitboxes and the blocks region covering it + hb := e.WorldHitbox() + hbmin, hbmax := itype.Vec3d{1000000000, 1000000000, 1000000000}, itype.Vec3d{-1000000000, -1000000000, -1000000000} + for _, b := range hb { + bmin, bmax := b.MinPoint(), b.MaxPoint() + hbmin[0] = util.Mind(hbmin[0], bmin[0]) + hbmin[1] = util.Mind(hbmin[1], bmin[1]) + hbmin[2] = util.Mind(hbmin[2], bmin[2]) + hbmax[0] = util.Maxd(hbmax[0], bmax[0]) + hbmax[1] = util.Maxd(hbmax[1], bmax[1]) + hbmax[2] = util.Maxd(hbmax[2], bmax[2]) + } + + // So this is the region of blocks we should calculate + imin, imax := hbmin.Floor().Addv(-1, -1, -1), hbmax.Ceiling().Addv(1, 1, 1) + + finDelta = delta + finVelocity = e.speed + for x := imin[0]; x <= imax[0]; x++ { + for y := imin[1]; y <= imax[1]; y++ { + for z := imin[2]; z <= imax[2]; z++ { + blockDelta, blockVelocity, blockOnGround := e.moveBlock(hb, delta, itype.Vec3i{x, y, z}, w) + finDelta[0] = util.AbsMind(finDelta[0], blockDelta[0]) + finDelta[1] = util.AbsMind(finDelta[1], blockDelta[1]) + finDelta[2] = util.AbsMind(finDelta[2], blockDelta[2]) + finVelocity[0] = util.AbsMind(finVelocity[0], blockVelocity[0]) + finVelocity[1] = util.AbsMind(finVelocity[1], blockVelocity[1]) + finVelocity[2] = util.AbsMind(finVelocity[2], blockVelocity[2]) + onGround = onGround || blockOnGround + } + } + } + + // Y- + if delta[1] < 0 { + onGround = onGround || e.onGround + } else { + onGround = false + } + + return } -// the move series functions should be called with e.hp already filled +// moveBlock simulates moving the entity by delta, and tests collision with the block hibbox. +func (e *Entity) moveBlock(worldHitbox []itype.Boxd, delta itype.Vec3d, blockCoord itype.Vec3i, w *world.World) (blockDelta itype.Vec3d, finVelocity itype.Vec3d, onGround bool) { + blockDelta = delta + finVelocity = e.speed + onGround = false -func (e *Entity) moveX(delta float64, hitbox itype.Boxd, w *world.World) { - if math.Abs(delta) < MoveEps*10 { + var blockHitbox []itype.Boxd + if block := w.Block(blockCoord); block.Id != 0 { + blocka := block.Appearance(blockCoord) + if !blocka.NotSolid { + blockHitbox = w.Block(blockCoord).Appearance(blockCoord).Hitbox + for i := range blockHitbox { + blockHitbox[i] = blockHitbox[i].Offset(blockCoord.ToFloat64()) + } + } + } + + if len(blockHitbox) == 0 { return } - var hit bool = false - var deltaMin float64 = 10000 + for _, eb := range worldHitbox { + for _, bb := range blockHitbox { + // Already intersecting: return + if ok, _ := eb.Intersect(bb); ok { + continue + } - if delta > 1-MoveEps { - delta = 1 - MoveEps - } - if delta < -1+MoveEps { - delta = -1 + MoveEps - } - - // X+ / X- - for _, p := range e.hp { - if pointStuck(p, w) { - continue - } - - dest := p.Addv(delta, 0, 0) - blockid := dest.Floor() - block := w.Block(blockid) - - var deltaDone float64 - if block.Id == 0 { - deltaDone = delta - } else { // block.Id!=0 - app := world.GetBlockAppearance(blockid, block.Id, block.Aux, block.Dataset, w) - blockBox := app.Hitbox.Offset(blockid.ToFloat64()) - if !app.NotSolid && blockBox.Contains(dest) { // Hit! - hit = true - if delta > 0 { // moving X+, hit on the X- face - deltaDone = blockBox.OffX - (e.pos[0] + hitbox.SizeX/2) - MoveEps - //log.Print("Hit X+: On Block ", blockid, ", delta=", delta, ", coord=", e.pos, ", p=", p, ", dest=", dest, ", deltaDone=", deltaDone) - } else { // moving X-, hit on the X+ face - deltaDone = blockBox.OffX + blockBox.SizeX - (e.pos[0] - hitbox.SizeX/2) + MoveEps - //log.Print("Hit X-: On Block ", blockid, ", delta=", delta, ", coord=", e.pos, ", p=", p, ", dest=", dest, ", deltaDone=", deltaDone) + // X + if ok, _ := eb.Offsetv(blockDelta[0], 0, 0).Intersect(bb); ok { + if blockDelta[0] > 0 { + blockDelta[0] = bb.OffX - (eb.OffX + eb.SizeX) - MoveEps + //log.Printf("Hit(X+): On (X%d,Y%d,Z%d), delta=%v\n", blockCoord[0], blockCoord[1], blockCoord[2], blockDelta) + } else { + blockDelta[0] = (bb.OffX + bb.SizeX) - eb.OffX + MoveEps + //log.Printf("Hit(X-): On (X%d,Y%d,Z%d), delta=%v\n", blockCoord[0], blockCoord[1], blockCoord[2], blockDelta) } - } else { - deltaDone = delta + finVelocity[0] = 0 + } + + // Y + if ok, _ := eb.Offsetv(0, blockDelta[1], 0).Intersect(bb); ok { + if blockDelta[1] > 0 { + blockDelta[1] = bb.OffY - (eb.OffY + eb.SizeY) - MoveEps + //log.Printf("Hit(Y+): On (X%d,Y%d,Z%d), delta=%v\n", blockCoord[0], blockCoord[1], blockCoord[2], blockDelta) + } else { + onGround = true + blockDelta[1] = (bb.OffY + bb.SizeY) - eb.OffY + MoveEps + //log.Printf("Hit(Y-): On (X%d,Y%d,Z%d), delta=%v\n", blockCoord[0], blockCoord[1], blockCoord[2], blockDelta) + } + finVelocity[1] = 0 + } + + // Z + if ok, _ := eb.Offsetv(0, 0, blockDelta[2]).Intersect(bb); ok { + if blockDelta[2] > 0 { + blockDelta[2] = bb.OffZ - (eb.OffZ + eb.SizeZ) - MoveEps + //log.Printf("Hit(Z+): On (X%d,Y%d,Z%d), delta=%v\n", blockCoord[0], blockCoord[1], blockCoord[2], blockDelta) + } else { + blockDelta[2] = (bb.OffZ + bb.SizeZ) - eb.OffZ + MoveEps + //log.Printf("Hit(Z-): On (X%d,Y%d,Z%d), delta=%v\n", blockCoord[0], blockCoord[1], blockCoord[2], blockDelta) + } + finVelocity[2] = 0 } } - - deltaMin = util.AbsMind(deltaMin, deltaDone) } - if hit { - e.speed[0] = 0 - } - e.pos[0] += deltaMin -} -func (e *Entity) moveY(delta float64, hitbox itype.Boxd, w *world.World) { - if math.Abs(delta) < MoveEps*10 { - return - } - - var hit bool = false - var deltaMin float64 = 10000 - - if delta > 1-MoveEps { - delta = 1 - MoveEps - } - if delta < -1+MoveEps { - delta = -1 + MoveEps - } - - // Y+ / Y- - for _, p := range e.hp { - if pointStuck(p, w) { - continue - } - - dest := p.Addv(0, delta, 0) - blockid := dest.Floor() - block := w.Block(blockid) - - var deltaDone float64 - if block.Id == 0 { - deltaDone = delta - } else { // block.Id!=0 - app := world.GetBlockAppearance(blockid, block.Id, block.Aux, block.Dataset, w) - blockBox := app.Hitbox.Offset(blockid.ToFloat64()) - if !app.NotSolid && blockBox.Contains(dest) { // Hit! - hit = true - if delta > 0 { // moving Y+, hit on the Y- face - deltaDone = blockBox.OffY - (e.pos[1] + hitbox.SizeY) - MoveEps - //log.Print("Hit Y+: On Block ", blockid, ", delta=", delta, ", coord=", e.pos, ", p=", p, ", dest=", dest,", deltaDone=",deltaDone) - } else { // moving Y-, hit on the Y+ face (on the ground) - deltaDone = blockBox.OffY + blockBox.SizeY - e.pos[1] + MoveEps - //log.Print("Hit Y-: On Block ", blockid, ", delta=", delta, ", coord=", e.pos, ", p=", p, ", dest=", dest,", deltaDone=",deltaDone) - /*if !e.onGround { - log.Print("onGround = true") - }*/ - e.onGround = true - } - } else { - deltaDone = delta - } - } - - deltaMin = util.AbsMind(deltaMin, deltaDone) - } - - if hit { - e.speed[1] = 0 - } - - if math.Abs(deltaMin) > MoveEps*10 { - /*if e.onGround { - log.Print("onGround = false") - }*/ - e.onGround = false - } - e.pos[1] += deltaMin -} -func (e *Entity) moveZ(delta float64, hitbox itype.Boxd, w *world.World) { - if math.Abs(delta) < MoveEps*10 { - return - } - - var hit bool = false - var deltaMin float64 = 10000 - - if delta > 1-MoveEps { - delta = 1 - MoveEps - } - if delta < -1+MoveEps { - delta = -1 + MoveEps - } - - // Z+ / Z- - for _, p := range e.hp { - if pointStuck(p, w) { - continue - } - - dest := p.Addv(0, 0, delta) - blockid := dest.Floor() - block := w.Block(blockid) - - var deltaDone float64 - if block.Id == 0 { - deltaDone = delta - } else { // block.Id!=0 - app := world.GetBlockAppearance(blockid, block.Id, block.Aux, block.Dataset, w) - blockBox := app.Hitbox.Offset(blockid.ToFloat64()) - if !app.NotSolid && blockBox.Contains(dest) { // Hit! - hit = true - if delta > 0 { // moving Z+, hit on the Z- face - deltaDone = util.Maxd(blockBox.OffZ-(e.pos[2]+hitbox.SizeZ/2)-MoveEps, 0) - //log.Print("Hit Z+: On Block ", blockid, ", delta=", delta, ", coord=", e.pos, ", p=", p, ", dest=", dest, ", deltaDone=", deltaDone) - } else { // moving Z-, hit on the Z+ face - deltaDone = util.Mind(blockBox.OffZ+blockBox.SizeZ-(e.pos[2]-hitbox.SizeZ/2)+MoveEps, 0) - //log.Print("Hit Z-: On Block ", blockid, ", delta=", delta, ", coord=", e.pos, ", p=", p, ", dest=", dest, ", deltaDone=", deltaDone) - } - } else { - deltaDone = delta - } - } - - deltaMin = util.AbsMind(deltaMin, deltaDone) - } - - if hit { - e.speed[2] = 0 - } - e.pos[2] += deltaMin + return } -const gravity float64 = 26 -const deaclc float64 = 28 +const gravity = 26 +const deaclc = 28 +const maxspeed = 8 func (e *Entity) Update(world *world.World, deltaTime time.Duration) { - hitbox := e.b.Hitbox(e.pos, e.ds) - box := e.worldHitbox(hitbox) + /* + delta, vec, onGround := e.move(itype.Vec3d{e.speed[0] * deltaTime.Seconds()}, world) + e.pos = e.pos.Add(delta) + e.speed = vec + delta, vec, onGround = e.move(e.speed.Multiply(deltaTime.Seconds()), world) + e.pos = e.pos.Add(delta) + e.speed = vec + delta, vec, onGround = e.move(e.speed.Multiply(deltaTime.Seconds()), world) + e.pos = e.pos.Add(delta) + e.speed = vec - e.hp = e.hp[0:0] - e.hp = e.boxHitpoints(e.hp, box) + e.onGround = onGround + */ - e.moveX(e.speed[0]*deltaTime.Seconds(), box, world) - e.moveY(e.speed[1]*deltaTime.Seconds(), box, world) - e.moveZ(e.speed[2]*deltaTime.Seconds(), box, world) + var deltaSpeed itype.Vec3d + if e.onGround { + speed := math.Sqrt(e.speed[0]*e.speed[0] + e.speed[2]*e.speed[2]) + if speed > MoveEps { + decrease := deaclc * deltaTime.Seconds() + factor := util.Maxd(util.Mind(speed-decrease, 6), 0) / speed - speed := math.Sqrt(e.speed[0]*e.speed[0] + e.speed[2]*e.speed[2]) - if speed > MoveEps { - decrease := deaclc * deltaTime.Seconds() - if !e.onGround { - decrease /= 10 + deltaSpeed[0] = -e.speed[0] * (1 - factor) + deltaSpeed[2] = -e.speed[2] * (1 - factor) + } else { + e.speed[0] = 0 + e.speed[2] = 0 } - factor := util.Maxd(util.Mind(speed-decrease, 9), 0) / speed - - e.speed[0] *= factor - e.speed[2] *= factor } + deltaSpeed[1] = -gravity * deltaTime.Seconds() - e.speed[1] -= gravity * deltaTime.Seconds() + delta := e.speed.Multiply(deltaTime.Seconds()).Add(deltaSpeed.Multiply(deltaTime.Seconds() / 2)) + e.speed = e.speed.Add(deltaSpeed) + + findelta, vec, onGround := e.move(delta, world) + e.pos = e.pos.Add(findelta) + e.speed = vec + e.onGround = onGround + + if !e.onGround { + vecXZ := itype.Vec2d{e.speed[0], e.speed[2]} + if vecXZ.Length() > maxspeed { + vecXZ = vecXZ.Normalize().Multiply(maxspeed) + } + e.speed[0] = vecXZ[0] + e.speed[2] = vecXZ[1] + } e.b.Update(e.pos, e.ds, world, deltaTime) } diff --git a/internal/game/imgui.go b/internal/game/imgui.go index e7d3603..1bcae21 100644 --- a/internal/game/imgui.go +++ b/internal/game/imgui.go @@ -3,6 +3,7 @@ package game import ( "fmt" "log" + "math" "os" "runtime" @@ -52,7 +53,8 @@ func (g *Game) imgui() { if imgui.BeginV("Player", nil, imgui.WindowFlagsAlwaysAutoResize) { pos := g.player.Position() vel := g.player.Speed() - imgui.Text(fmt.Sprintf("Pos: (%.5f, %.5f, %.5f), Vel: (%.5f, %.5f, %.5f)", pos[0], pos[1], pos[2], vel[0], vel[1], vel[2])) + igwrap.Text("Pos: (%.5f, %.5f, %.5f), Vel: (%.5f, %.5f, %.5f)", pos[0], pos[1], pos[2], vel[0], vel[1], vel[2]) + igwrap.Text("VelXZ=%.5f, VelXYZ=%.5f", math.Sqrt(vel[0]*vel[0]+vel[2]*vel[2]), vel.Length()) } imgui.End() diff --git a/internal/game/logic.go b/internal/game/logic.go index 2f3c349..fe600d0 100644 --- a/internal/game/logic.go +++ b/internal/game/logic.go @@ -166,6 +166,7 @@ func (g *Game) Init(win *glfw.Window) { igwrap.MouseScrollCallback(xpos, ypos) } }) + } const airAccel = 0.1 @@ -234,7 +235,7 @@ func (g *Game) Update(win *glfw.Window, delta time.Duration) { io.ViewPos = g.player.EyePosition() io.ViewDir = itype.Vec3d(mgl64.Rotate3DY(float64(g.rotY.Radians())).Mul3(mgl64.Rotate3DZ(float64(itype.Degrees(g.rotZ)))).Mul3x1(mgl64.Vec3{1, 0, 0})) - render.Framewire.PushBox(g.player.WorldHitbox().ToFloat32(), color.White) + render.Framewire.PushBox(g.player.WorldHitbox()[0].ToFloat32(), color.White) if g.player.Position()[1] < -100 { g.player.SetPosition(itype.Vec3d{18, 80, 18}) diff --git a/internal/util/itype/rect.go b/internal/util/itype/rect.go index bb40b55..194bbad 100644 --- a/internal/util/itype/rect.go +++ b/internal/util/itype/rect.go @@ -137,15 +137,15 @@ func pointIntersect(n, m, p, q float64) (min, len float64) { return arr[1], arr[2] - arr[1] } -func (box1 Boxd) Intersects(box2 Boxd) (result Boxd) { +func (box1 Boxd) Intersect(box2 Boxd) (ok bool, intersect Boxd) { a, b := pointIntersect(box1.OffX, box1.OffX+box1.SizeX, box2.OffX, box2.OffX+box2.SizeX) c, d := pointIntersect(box1.OffY, box1.OffY+box1.SizeY, box2.OffY, box2.OffY+box2.SizeY) e, f := pointIntersect(box1.OffZ, box1.OffZ+box1.SizeZ, box2.OffZ, box2.OffZ+box2.SizeZ) if b == 0 || d == 0 || f == 0 { - return Boxd{} + return false, Boxd{} } else { - return Boxd{ + return true, Boxd{ OffX: a, SizeX: b, OffY: c, diff --git a/internal/util/itype/vec.go b/internal/util/itype/vec.go index fcb1237..d108b4f 100644 --- a/internal/util/itype/vec.go +++ b/internal/util/itype/vec.go @@ -205,6 +205,14 @@ func (v Vec3d) Floor() Vec3i { } } +func (v Vec3d) Ceiling() Vec3i { + return Vec3i{ + int(math.Ceil(v[0])), + int(math.Ceil(v[1])), + int(math.Ceil(v[2])), + } +} + func (v Vec3d) ToFloat32() Vec3f { return Vec3f{float32(v[0]), float32(v[1]), float32(v[2])} } diff --git a/internal/world/block.go b/internal/world/block.go index af536e7..6e24b79 100644 --- a/internal/world/block.go +++ b/internal/world/block.go @@ -20,10 +20,10 @@ const ( type BlockAppearance struct { Name string // A short name, like "stone" or "dirt", used for texture lookups Transparent bool // Is block transparent? - NotSolid bool // Is block not solid, i.e., has no solid hitbox? (this makes the zero value reasonable) + NotSolid bool // Is block not solid, i.e., has no hitbox at all? (this makes the zero value reasonable) Light int // The light level it emits, 0 is none - Hitbox itype.Boxd // Hitbox, in block-local coordinates; empty slice means a default hitbox of 1x1x1 + Hitbox []itype.Boxd // Hitbox, in block-local coordinates; empty slice means a default hitbox of 1x1x1 RenderType BlockRenderType // Rendering type, defaults to OneTexture (zero value) @@ -124,11 +124,11 @@ func DoneRegisteringBlockBehaviour() { // GetBlockAppearance gets the block appearance of the given block in the fastest way possible. func GetBlockAppearance(position itype.Vec3i, id, aux int, data itype.Dataset, world *World) BlockAppearance { if app, ok := appearance[id]; ok { // Cache - if app.Hitbox == (itype.Boxd{}) { - app.Hitbox = itype.Boxd{ + if len(app.Hitbox) == 0 { + app.Hitbox = []itype.Boxd{{ OffX: 0, OffY: 0, OffZ: 0, SizeX: 1, SizeY: 1, SizeZ: 1, - } + }} } return app } @@ -140,11 +140,11 @@ func GetBlockAppearance(position itype.Vec3i, id, aux int, data itype.Dataset, w } app := b.Appearance(position, aux, data, world) - if app.Hitbox == (itype.Boxd{}) { - app.Hitbox = itype.Boxd{ + if len(app.Hitbox) == 0 { + app.Hitbox = []itype.Boxd{{ OffX: 0, OffY: 0, OffZ: 0, SizeX: 1, SizeY: 1, SizeZ: 1, - } + }} } return app } @@ -165,7 +165,14 @@ type Block struct { // Appearance is a shortcut for Behaviour.Appearance(). // It returns the Appearance of the block with the given parameters. func (b Block) Appearance(position itype.Vec3i) BlockAppearance { - return b.Behaviour.Appearance(position, b.Aux, b.Dataset, b.World) + app := b.Behaviour.Appearance(position, b.Aux, b.Dataset, b.World) + if !app.NotSolid && len(app.Hitbox) == 0 { + app.Hitbox = []itype.Boxd{{ + OffX: 0, OffY: 0, OffZ: 0, + SizeX: 1, SizeY: 1, SizeZ: 1, + }} + } + return app } // BlockUpdate is a shortcut for Behaviour.BlockUpdate().