-
Notifications
You must be signed in to change notification settings - Fork 140
/
aabb.go
146 lines (124 loc) · 3.3 KB
/
aabb.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
package trace
import (
"github.com/df-mc/dragonfly/server/block/cube"
"github.com/df-mc/dragonfly/server/entity/physics"
"github.com/go-gl/mathgl/mgl64"
"math"
)
// AABBResult is the result of a basic ray trace collision with a bounding box.
type AABBResult struct {
bb physics.AABB
pos mgl64.Vec3
face cube.Face
}
// AABB ...
func (r AABBResult) AABB() physics.AABB {
return r.bb
}
// Position ...
func (r AABBResult) Position() mgl64.Vec3 {
return r.pos
}
// Face ...
func (r AABBResult) Face() cube.Face {
return r.face
}
// AABBIntercept performs a ray trace and calculates the point on the AABB's edge nearest to the start position that the ray trace
// collided with.
// AABBIntercept returns a AABBResult with the colliding vector closest to the start position, if no colliding point was found,
// a zero AABBResult is returned and ok is false.
func AABBIntercept(bb physics.AABB, start, end mgl64.Vec3) (result AABBResult, ok bool) {
min, max := bb.Min(), bb.Max()
v1 := vec3OnLineWithX(start, end, min[0])
v2 := vec3OnLineWithX(start, end, max[0])
v3 := vec3OnLineWithY(start, end, min[1])
v4 := vec3OnLineWithY(start, end, max[1])
v5 := vec3OnLineWithZ(start, end, min[2])
v6 := vec3OnLineWithZ(start, end, max[2])
if v1 != nil && !bb.Vec3WithinYZ(*v1) {
v1 = nil
}
if v2 != nil && !bb.Vec3WithinYZ(*v2) {
v2 = nil
}
if v3 != nil && !bb.Vec3WithinXZ(*v3) {
v3 = nil
}
if v4 != nil && !bb.Vec3WithinXZ(*v4) {
v4 = nil
}
if v5 != nil && !bb.Vec3WithinXY(*v5) {
v5 = nil
}
if v6 != nil && !bb.Vec3WithinXY(*v6) {
v6 = nil
}
var (
vec *mgl64.Vec3
dist = math.MaxFloat64
)
for _, v := range [...]*mgl64.Vec3{v1, v2, v3, v4, v5, v6} {
if v == nil {
continue
}
if d := start.Sub(*v).LenSqr(); d < dist {
vec = v
dist = d
}
}
if vec == nil {
return
}
var f cube.Face
switch vec {
case v1:
f = cube.FaceWest
case v2:
f = cube.FaceEast
case v3:
f = cube.FaceDown
case v4:
f = cube.FaceUp
case v5:
f = cube.FaceNorth
case v6:
f = cube.FaceSouth
}
return AABBResult{bb: bb, pos: *vec, face: f}, true
}
// vec3OnLineWithX returns an mgl64.Vec3 on the line between mgl64.Vec3 a and b with an X value passed. If no such vec3
// could be found, the bool returned is false.
func vec3OnLineWithX(a, b mgl64.Vec3, x float64) *mgl64.Vec3 {
if mgl64.FloatEqual(b[0], a[0]) {
return nil
}
f := (x - a[0]) / (b[0] - a[0])
if f < 0 || f > 1 {
return nil
}
return &mgl64.Vec3{x, a[1] + (b[1]-a[1])*f, a[2] + (b[2]-a[2])*f}
}
// vec3OnLineWithY returns an mgl64.Vec3 on the line between mgl64.Vec3 a and b with a Y value passed. If no such vec3
// could be found, the bool returned is false.
func vec3OnLineWithY(a, b mgl64.Vec3, y float64) *mgl64.Vec3 {
if mgl64.FloatEqual(a[1], b[1]) {
return nil
}
f := (y - a[1]) / (b[1] - a[1])
if f < 0 || f > 1 {
return nil
}
return &mgl64.Vec3{a[0] + (b[0]-a[0])*f, y, a[2] + (b[2]-a[2])*f}
}
// vec3OnLineWithZ returns an mgl64.Vec3 on the line between mgl64.Vec3 a and b with a Z value passed. If no such vec3
// could be found, the bool returned is false.
func vec3OnLineWithZ(a, b mgl64.Vec3, z float64) *mgl64.Vec3 {
if mgl64.FloatEqual(a[2], b[2]) {
return nil
}
f := (z - a[2]) / (b[2] - a[2])
if f < 0 || f > 1 {
return nil
}
return &mgl64.Vec3{a[0] + (b[0]-a[0])*f, a[1] + (b[1]-a[1])*f, z}
}