prism collism
This commit is contained in:
parent
67a8f03cbf
commit
94915072f0
5 changed files with 143 additions and 4 deletions
|
@ -93,3 +93,11 @@ func (b Box) XY() image.Rectangle {
|
||||||
Max: b.Max.XY(),
|
Max: b.Max.XY(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// XZ returns the image.Rectangle representing the box if we forgot about Y.
|
||||||
|
func (b Box) XZ() image.Rectangle {
|
||||||
|
return image.Rectangle{
|
||||||
|
Min: b.Min.XZ(),
|
||||||
|
Max: b.Max.XZ(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package engine
|
package engine
|
||||||
|
|
||||||
import "image"
|
import (
|
||||||
|
"image"
|
||||||
|
)
|
||||||
|
|
||||||
// ID implements Identifier directly (as a string value).
|
// ID implements Identifier directly (as a string value).
|
||||||
type ID string
|
type ID string
|
||||||
|
@ -65,3 +67,97 @@ func cfloat(p image.Point) (x, y float64) {
|
||||||
func dot(p, q image.Point) int {
|
func dot(p, q image.Point) int {
|
||||||
return p.X*q.X + p.Y*q.Y
|
return p.X*q.X + p.Y*q.Y
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// polygonContains reports if a polygon contains a point
|
||||||
|
func polygonContains(polygon []image.Point, p image.Point) bool {
|
||||||
|
for i, p1 := range polygon {
|
||||||
|
p2 := polygon[(i+1)%len(polygon)]
|
||||||
|
// ∆(p p1 p2) should have positive signed area
|
||||||
|
p1, p2 = p1.Sub(p), p2.Sub(p)
|
||||||
|
if p2.X*p1.Y-p1.X*p2.Y < 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// polygonRectOverlap reports if a polygon overlaps a rectangle.
|
||||||
|
func polygonRectOverlap(polygon []image.Point, rect image.Rectangle) bool {
|
||||||
|
// There's a good chance a vertex from one thing is inside the other...
|
||||||
|
|
||||||
|
// Check if any vertex of the polygon is inside the rect.
|
||||||
|
for _, p := range polygon {
|
||||||
|
if p.In(rect) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reduce Max to the inclusive bound.
|
||||||
|
rect.Max = rect.Max.Sub(image.Pt(1, 1))
|
||||||
|
|
||||||
|
// Check if any vertex of the rect is inside the polygon.
|
||||||
|
if polygonContains(polygon, rect.Min) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if polygonContains(polygon, rect.Max) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if polygonContains(polygon, image.Pt(rect.Min.X, rect.Max.Y)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if polygonContains(polygon, image.Pt(rect.Max.X, rect.Min.Y)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only remaining cases involve line intersection between the rect and poly.
|
||||||
|
|
||||||
|
// Since rect is an axis-aligned rectangle, we only need vertical and
|
||||||
|
// horizontal line intersection tests.
|
||||||
|
// Walk each edge of polygon. Only a point and the ∆x, ∆y are needed.
|
||||||
|
for i, p := range polygon {
|
||||||
|
q := polygon[(i+1)%len(polygon)]
|
||||||
|
d := q.Sub(p)
|
||||||
|
// If the polygon edge is not vertical, test left and right sides
|
||||||
|
if d.X != 0 {
|
||||||
|
if d.X < 0 {
|
||||||
|
d = d.Mul(-1)
|
||||||
|
}
|
||||||
|
min := (rect.Min.Y - p.Y) * d.X
|
||||||
|
max := (rect.Max.Y - p.Y) * d.X
|
||||||
|
// Test left side of rect
|
||||||
|
if (rect.Min.X >= p.X || rect.Min.X >= q.X) && (rect.Min.X <= p.X || rect.Min.X <= q.X) {
|
||||||
|
if t := (rect.Min.X - p.X) * d.Y; min <= t && t <= max {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Test right side of rect
|
||||||
|
if (rect.Max.X >= p.X || rect.Max.X >= q.X) && (rect.Max.X <= p.X || rect.Max.X <= q.X) {
|
||||||
|
if t := (rect.Max.X - p.X) * d.Y; min <= t && t <= max {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If the polygon edge is not horizontal, test the top and bottom sides
|
||||||
|
if d.Y != 0 {
|
||||||
|
if d.Y < 0 {
|
||||||
|
d = d.Mul(-1)
|
||||||
|
}
|
||||||
|
min := (rect.Min.X - p.X) * d.Y
|
||||||
|
max := (rect.Max.X - p.X) * d.Y
|
||||||
|
// Test top side of rect
|
||||||
|
if (rect.Min.Y >= p.Y && rect.Min.Y >= q.Y) && (rect.Min.Y <= p.Y && rect.Min.Y <= q.Y) {
|
||||||
|
if t := (rect.Min.Y - p.Y) * d.X; min <= t && t <= max {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test bottom side of rect
|
||||||
|
if (rect.Max.Y >= p.Y && rect.Max.Y >= q.Y) && (rect.Max.Y <= p.Y && rect.Max.Y <= q.Y) {
|
||||||
|
if t := (rect.Max.Y - p.Y) * d.X; min <= t && t <= max {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
|
@ -23,7 +23,12 @@ func (p Int3) String() string {
|
||||||
|
|
||||||
// XY applies the Z-forgetting projection. (It returns just X and Y.)
|
// XY applies the Z-forgetting projection. (It returns just X and Y.)
|
||||||
func (p Int3) XY() image.Point {
|
func (p Int3) XY() image.Point {
|
||||||
return image.Point{p.X, p.Y}
|
return image.Point{X: p.X, Y: p.Y}
|
||||||
|
}
|
||||||
|
|
||||||
|
// XZ applies the Y-forgetting projection. (It returns just X and Z (as Y).)
|
||||||
|
func (p Int3) XZ() image.Point {
|
||||||
|
return image.Point{X: p.X, Y: p.Z}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add performs vector addition.
|
// Add performs vector addition.
|
||||||
|
|
|
@ -40,6 +40,7 @@ type PrismMap struct {
|
||||||
DrawOffset image.Point // offset applies to whole map
|
DrawOffset image.Point // offset applies to whole map
|
||||||
PosToWorld IntMatrix3x4 // p.pos -> world voxelspace
|
PosToWorld IntMatrix3x4 // p.pos -> world voxelspace
|
||||||
PrismSize Int3 // in world voxelspace units
|
PrismSize Int3 // in world voxelspace units
|
||||||
|
PrismTop []image.Point // polygon vertices anticlockwise, Y means Z
|
||||||
Sheet Sheet
|
Sheet Sheet
|
||||||
|
|
||||||
game *Game
|
game *Game
|
||||||
|
@ -50,6 +51,7 @@ func (m *PrismMap) CollidesWith(b Box) bool {
|
||||||
if m.Ersatz {
|
if m.Ersatz {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// To find the prisms need to test, we need to invert PosToWorld.
|
// To find the prisms need to test, we need to invert PosToWorld.
|
||||||
|
|
||||||
// Step 1: subtract whatever the translation component of PosToWorld is,
|
// Step 1: subtract whatever the translation component of PosToWorld is,
|
||||||
|
@ -80,11 +82,31 @@ func (m *PrismMap) CollidesWith(b Box) bool {
|
||||||
if !b.Overlaps(cb) {
|
if !b.Overlaps(cb) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// TODO: take into account the prism shape
|
// Take into account the prism shape
|
||||||
|
r := b.XZ().Sub(wp.XZ())
|
||||||
|
if polygonRectOverlap(m.PrismTop, r) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
// Here's the test-every-prism approach
|
||||||
|
for pos := range m.Map {
|
||||||
|
// Map it back to worldspace to get a bounding box for the prism
|
||||||
|
wp := m.PosToWorld.Apply(pos)
|
||||||
|
cb := Box{Min: wp, Max: wp.Add(m.PrismSize)}
|
||||||
|
if !b.Overlaps(cb) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Take into account the prism shape
|
||||||
|
r := b.XZ().Sub(wp.XZ())
|
||||||
|
if polygonRectOverlap(m.PrismTop, r) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
8
main.go
8
main.go
|
@ -121,6 +121,14 @@ func level1() *engine.Scene {
|
||||||
2: [4]int{8, 0, 16, 0},
|
2: [4]int{8, 0, 16, 0},
|
||||||
},
|
},
|
||||||
PrismSize: engine.Int3{X: 32, Y: 16, Z: 16},
|
PrismSize: engine.Int3{X: 32, Y: 16, Z: 16},
|
||||||
|
PrismTop: []image.Point{
|
||||||
|
{X: 8, Y: 0},
|
||||||
|
{X: 0, Y: 8},
|
||||||
|
{X: 8, Y: 16},
|
||||||
|
{X: 23, Y: 16},
|
||||||
|
{X: 31, Y: 8},
|
||||||
|
{X: 23, Y: 0},
|
||||||
|
},
|
||||||
Sheet: engine.Sheet{
|
Sheet: engine.Sheet{
|
||||||
CellSize: image.Pt(32, 32),
|
CellSize: image.Pt(32, 32),
|
||||||
Src: engine.ImageRef{Path: "assets/hexprism32.png"},
|
Src: engine.ImageRef{Path: "assets/hexprism32.png"},
|
||||||
|
|
Loading…
Reference in a new issue