-
Notifications
You must be signed in to change notification settings - Fork 140
/
trace.go
107 lines (92 loc) · 2.34 KB
/
trace.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
package trace
import (
"github.com/df-mc/dragonfly/server/block/cube"
"github.com/go-gl/mathgl/mgl64"
"math"
)
// TraverseBlocks performs a ray trace between the start and end coordinates.
// A function 'f' is passed which is called for each voxel, if f returns false, the function will return.
// TraverseBlocks panics if the start and end positions are the same.
func TraverseBlocks(start, end mgl64.Vec3, f func(pos cube.Pos) (con bool)) {
dir := end.Sub(start).Normalize()
if dir.LenSqr() <= 0.0 {
panic("start and end points are the same, giving a zero direction vector")
}
b := cube.PosFromVec3(start)
step := signVec3(dir)
stepX, stepY, stepZ := int(step[0]), int(step[1]), int(step[2])
max := boundaryVec3(start, dir)
delta := safeDivideVec3(step, dir)
r := start.Sub(end).Len()
for {
if !f(b) {
return
}
if max[0] < max[1] && max[0] < max[2] {
if max[0] > r {
return
}
b[0] += stepX
max[0] += delta[0]
} else if max[1] < max[2] {
if max[1] > r {
return
}
b[1] += stepY
max[1] += delta[1]
} else {
if max[2] > r {
return
}
b[2] += stepZ
max[2] += delta[2]
}
}
}
// safeDivideVec3 ...
func safeDivideVec3(dividend, divisor mgl64.Vec3) mgl64.Vec3 {
return mgl64.Vec3{
safeDivide(dividend[0], divisor[0]),
safeDivide(dividend[1], divisor[1]),
safeDivide(dividend[2], divisor[2]),
}
}
// safeDivide divides the dividend by the divisor, but if the divisor is 0, it returns 0.
func safeDivide(dividend, divisor float64) float64 {
if divisor == 0.0 {
return 0.0
}
return dividend / divisor
}
// boundaryVec3 ...
func boundaryVec3(v1, v2 mgl64.Vec3) mgl64.Vec3 {
return mgl64.Vec3{boundary(v1[0], v2[0]), boundary(v1[1], v2[1]), boundary(v1[2], v2[2])}
}
// boundary returns the distance that must be travelled on an axis from the start point with the direction vector
// component to cross a block boundary.
func boundary(start, dir float64) float64 {
if dir == 0.0 {
return math.Inf(1)
}
if dir < 0.0 {
start, dir = -start, -dir
if math.Floor(start) == start {
return 0.0
}
}
return (1 - (start - math.Floor(start))) / dir
}
// signVec3 ...
func signVec3(v1 mgl64.Vec3) mgl64.Vec3 {
return mgl64.Vec3{sign(v1[0]), sign(v1[1]), sign(v1[2])}
}
// sign ...
func sign(f float64) float64 {
switch {
case f > 0.0:
return 1.0
case f < 0.0:
return -1.0
}
return 0.0
}