-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathbox_seg_sweep1.go
More file actions
166 lines (134 loc) · 3.71 KB
/
box_seg_sweep1.go
File metadata and controls
166 lines (134 loc) · 3.71 KB
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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
package coll
import (
"math"
"github.com/setanarut/v"
)
// BoxSegmentSweep1 sweep a moving box against a static line segment.
//
// If h is not nil and a collision is detected, it will be populated with:
// - Normal: Collision surface normal for the box
// - Data: Normalized time of impact (0.0 to 1.0) along the movement path
func BoxSegmentSweep1(s *Segment, a *AABB, deltaA v.Vec, h *Hit) bool {
var lineMin, lineMax v.Vec
aabbCenter := a.Pos
aabbMin := a.Min()
aabbMax := a.Max()
normalizedDelta := deltaA.Unit()
// calculate line bounds
lineDir := s.B.Sub(s.A)
if lineDir.X > 0.0 {
lineMin.X = s.A.X
lineMax.X = s.B.X
} else {
lineMin.X = s.B.X
lineMax.X = s.A.X
}
if lineDir.Y > 0.0 {
lineMin.Y = s.A.Y
lineMax.Y = s.B.Y
} else {
lineMin.Y = s.B.Y
lineMax.Y = s.A.Y
}
// get aabb's center to line.A distance
lineAabbDist := s.A.Sub(aabbCenter)
// get the line's normal
// if the dot product of it and the delta is larger than 0,
// it means the line's normal is facing away from the sweep
lineNormal := SegmentNormal(s.A, s.B)
hitNormal := lineNormal
hitTime := 0.0
outTime := 1.0
// calculate the radius of the box in respect to the line normal
r := a.Half.X*math.Abs(lineNormal.X) + a.Half.Y*math.Abs(lineNormal.Y)
// distance from box to line in respect to the line normal
boxProj := lineAabbDist.Dot(lineNormal)
dProj := deltaA.Dot(lineNormal)
// inverse the radius if required
if dProj < 0 {
r *= -1
}
// calculate first and last overlap times,
// as if we're dealing with a line rather than a segment
hitTime = max((boxProj-r)/dProj, hitTime)
outTime = min((boxProj+r)/dProj, outTime)
// run standard AABBvsAABB sweep
// against an AABB constructed from the extents of the line segment
// X axis overlap
if deltaA.X < 0 {
// sweeping left
if aabbMax.X < lineMin.X {
return false
}
hit := (lineMax.X - aabbMin.X) / deltaA.X
out := (lineMin.X - aabbMax.X) / deltaA.X
outTime = min(out, outTime)
if hit >= hitTime && hit <= outTime {
// box is hitting the line on its end:
// adjust the normal accordingly
hitNormal = v.Right
}
hitTime = max(hit, hitTime)
} else if deltaA.X > 0 {
// sweeping right
if aabbMin.X > lineMax.X {
return false
}
hit := (lineMin.X - aabbMax.X) / deltaA.X
out := (lineMax.X - aabbMin.X) / deltaA.X
outTime = min(out, outTime)
if hit >= hitTime && hit <= outTime {
hitNormal = v.Left
}
hitTime = max(hit, hitTime)
} else if lineMin.X > aabbMax.X || lineMax.X < aabbMin.X {
return false
}
if hitTime > outTime {
return false
}
// Y axis overlap
if deltaA.Y < 0 {
// sweeping up
if aabbMax.Y < lineMin.Y {
return false
}
hit := (lineMax.Y - aabbMin.Y) / deltaA.Y
out := (lineMin.Y - aabbMax.Y) / deltaA.Y
outTime = min(out, outTime)
if hit >= hitTime && hit <= outTime {
hitNormal = v.Down
}
hitTime = max(hit, hitTime)
} else if deltaA.Y > 0 {
// sweeping down
if aabbMin.Y > lineMax.Y {
return false
}
hit := (lineMin.Y - aabbMax.Y) / deltaA.Y
out := (lineMax.Y - aabbMin.Y) / deltaA.Y
outTime = min(out, outTime)
if hit >= hitTime && hit <= outTime {
hitNormal = v.Up
}
hitTime = max(hit, hitTime)
} else if lineMin.Y > aabbMax.Y || lineMax.Y < aabbMin.Y {
return false
}
if hitTime > outTime {
return false
}
// ignore this line if its normal is facing away from the sweep delta
// check for this only at this point to account for a possibly changed hitNormal
// from a hit on the line's end
//
// also ignore this line its normal is facing away from the adjusted hitNormal
if normalizedDelta.Dot(hitNormal) > 0 || lineNormal.Dot(hitNormal) < 0 {
return false
}
if h != nil {
h.Normal = hitNormal
h.Data = hitTime
}
return true
}