-
Notifications
You must be signed in to change notification settings - Fork 3
/
rprism.go
115 lines (108 loc) · 2.86 KB
/
rprism.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
package main
import (
"fmt"
"math"
)
// An RPrism is a rectangular prism with sides parallel to the axis planes.
// It is defined by a point and extends in the positive X, Y, and Z dimensions
// as given by Dim.
type RPrism struct {
Pos Vec3 // corner with smallest X, Y, Z
Dim [3]float64 // X, Y, Z
Mat *Material `json:"-"`
MatName string `json:"mat"`
}
func (p *RPrism) Initialize(materials map[string]*Material) error {
m, ok := materials[p.MatName]
if !ok {
return fmt.Errorf("cannot find material %s", p.MatName)
}
p.Mat = m
return nil
}
// a, b, and c are dimensions (i.e. each is one of X, Y, Z)
// For example, for the unit cube, to find an intersection with the side on the YZ plane (x=0), you might call
// with arguments:
// bcPlane = 0
// minB = 0
// maxB = 1
// minC = 0
// maxC = 1
// v and d are r.V and r.D vectors in the order [a, b, c].
func rprismIntersects(q *rprismIntersectQ) (float64, bool) {
t := (q.bcPlane - q.v[0]) / q.d[0]
if t < 0 {
return 0, false
}
b := q.v[1] + t*q.d[1]
c := q.v[2] + t*q.d[2]
if b >= q.minB && b <= q.maxB && c >= q.minC && c <= q.maxC {
return t, true
}
return 0, false
}
type rprismIntersectQ struct {
v, d [3]float64
bcPlane, minB, maxB, minC, maxC float64
normal Vec3
}
// P(t) = r.V.X + t*r.D.X = x1
// t = (x1 - r.V.X) / r.D.X
func (p *RPrism) Intersect(r Ray) (float64, *Material, Vec3, Vec3, bool) {
queries := []*rprismIntersectQ{
{
[3]float64{r.V.X, r.V.Y, r.V.Z},
[3]float64{r.D.X, r.D.Y, r.D.Z},
p.Pos.X, p.Pos.Y, p.Pos.Y + p.Dim[1], p.Pos.Z, p.Pos.Z + p.Dim[2],
Vec3{-1, 0, 0},
},
{
[3]float64{r.V.X, r.V.Y, r.V.Z},
[3]float64{r.D.X, r.D.Y, r.D.Z},
p.Pos.X + p.Dim[0], p.Pos.Y, p.Pos.Y + p.Dim[1], p.Pos.Z, p.Pos.Z + p.Dim[2],
Vec3{1, 0, 0},
},
{
[3]float64{r.V.Y, r.V.Z, r.V.X},
[3]float64{r.D.Y, r.D.Z, r.D.X},
p.Pos.Y, p.Pos.Z, p.Pos.Z + p.Dim[2], p.Pos.X, p.Pos.X + p.Dim[0],
Vec3{0, -1, 0},
},
{
[3]float64{r.V.Y, r.V.Z, r.V.X},
[3]float64{r.D.Y, r.D.Z, r.D.X},
p.Pos.Y + p.Dim[1], p.Pos.Z, p.Pos.Z + p.Dim[2], p.Pos.X, p.Pos.X + p.Dim[0],
Vec3{0, 1, 0},
},
{
[3]float64{r.V.Z, r.V.X, r.V.Y},
[3]float64{r.D.Z, r.D.X, r.D.Y},
p.Pos.Z, p.Pos.X, p.Pos.X + p.Dim[0], p.Pos.Y, p.Pos.Y + p.Dim[1],
Vec3{0, 0, -1},
},
{
[3]float64{r.V.Z, r.V.X, r.V.Y},
[3]float64{r.D.Z, r.D.X, r.D.Y},
p.Pos.Z + p.Dim[2], p.Pos.X, p.Pos.X + p.Dim[0], p.Pos.Y, p.Pos.Y + p.Dim[1],
Vec3{0, 0, 1},
},
}
nearest := math.MaxFloat64
found := false
var normal Vec3
for _, q := range queries {
d, ok := rprismIntersects(q)
if ok {
if d > minDistance && d < nearest {
found = true
nearest = d
normal = q.normal
}
}
}
if !found {
return 0, nil, Vec3{}, Vec3{}, false
}
pt := r.At(nearest)
return nearest, p.Mat, pt, normal, found
}