forked from g3n/engine
/
box.go
160 lines (134 loc) · 5.04 KB
/
box.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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
// Copyright 2016 The G3N Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package geometry
import (
"engine/gls"
"engine/math32"
)
// Box represents the geometry of a rectangular cuboid.
// See https://en.wikipedia.org/wiki/Cuboid#Rectangular_cuboid for more details.
// A Box geometry is defined by its width, height, and length and also by the number
// of segments in each dimension.
type Box struct {
Geometry
Width float32
Height float32
Length float32
WidthSegments int // > 0
HeightSegments int // > 0
LengthSegments int // > 0
}
// NewCube creates a new cube geometry of the specified size.
func NewCube(size float32) *Box {
return NewSegmentedBox(size, size, size, 1, 1, 1)
}
// NewSegmentedCube creates a cube geometry of the specified size and number of segments.
func NewSegmentedCube(size float32, segments int) *Box {
return NewSegmentedBox(size, size, size, segments, segments, segments)
}
// NewBox creates a box geometry of the specified width, height, and length.
func NewBox(width, height, length float32) *Box {
return NewSegmentedBox(width, height, length, 1, 1, 1)
}
// NewSegmentedBox creates a box geometry of the specified size and with the specified number
// of segments in each dimension. This is the Box constructor with most tunable parameters.
func NewSegmentedBox(width, height, length float32, widthSegments, heightSegments, lengthSegments int) *Box {
box := new(Box)
box.Geometry.Init()
// Validate arguments
if widthSegments <= 0 || heightSegments <= 0 || lengthSegments <= 0 {
panic("Invalid argument(s). All segment quantities should be greater than zero.")
}
box.Width = width
box.Height = height
box.Length = length
box.WidthSegments = widthSegments
box.HeightSegments = heightSegments
box.LengthSegments = lengthSegments
// Create buffers
positions := math32.NewArrayF32(0, 16)
normals := math32.NewArrayF32(0, 16)
uvs := math32.NewArrayF32(0, 16)
indices := math32.NewArrayU32(0, 16)
// Internal function to build each of the six box planes
buildPlane := func(u, v string, udir, vdir int, width, height, length float32, materialIndex uint) {
offset := positions.Len() / 3
gridX := box.WidthSegments
gridY := box.HeightSegments
var w string
if (u == "x" && v == "y") || (u == "y" && v == "x") {
w = "z"
} else if (u == "x" && v == "z") || (u == "z" && v == "x") {
w = "y"
gridY = box.LengthSegments
} else if (u == "z" && v == "y") || (u == "y" && v == "z") {
w = "x"
gridX = box.LengthSegments
}
var normal math32.Vector3
if length > 0 {
normal.SetByName(w, 1)
} else {
normal.SetByName(w, -1)
}
wHalf := width / 2
hHalf := height / 2
gridX1 := gridX + 1
gridY1 := gridY + 1
segmentWidth := width / float32(gridX)
segmentHeight := height / float32(gridY)
// Generate the plane vertices, normals, and uv coordinates
for iy := 0; iy < gridY1; iy++ {
for ix := 0; ix < gridX1; ix++ {
var vector math32.Vector3
vector.SetByName(u, (float32(ix)*segmentWidth-wHalf)*float32(udir))
vector.SetByName(v, (float32(iy)*segmentHeight-hHalf)*float32(vdir))
vector.SetByName(w, length)
positions.AppendVector3(&vector)
normals.AppendVector3(&normal)
uvs.Append(float32(ix)/float32(gridX), float32(1)-(float32(iy)/float32(gridY)))
}
}
// Generate the indices for the vertices, normals and uv coordinates
gstart := indices.Size()
for iy := 0; iy < gridY; iy++ {
for ix := 0; ix < gridX; ix++ {
a := ix + gridX1*iy
b := ix + gridX1*(iy+1)
c := (ix + 1) + gridX1*(iy+1)
d := (ix + 1) + gridX1*iy
indices.Append(uint32(a+offset), uint32(b+offset), uint32(d+offset), uint32(b+offset), uint32(c+offset), uint32(d+offset))
}
}
gcount := indices.Size() - gstart
box.AddGroup(gstart, gcount, int(materialIndex))
}
wHalf := box.Width / 2
hHalf := box.Height / 2
lHalf := box.Length / 2
buildPlane("z", "y", -1, -1, box.Length, box.Height, wHalf, 0) // px
buildPlane("z", "y", 1, -1, box.Length, box.Height, -wHalf, 1) // nx
buildPlane("x", "z", 1, 1, box.Width, box.Length, hHalf, 2) // py
buildPlane("x", "z", 1, -1, box.Width, box.Length, -hHalf, 3) // ny
buildPlane("x", "y", 1, -1, box.Width, box.Height, lHalf, 4) // pz
buildPlane("x", "y", -1, -1, box.Width, box.Height, -lHalf, 5) // nz
box.SetIndices(indices)
box.AddVBO(gls.NewVBO(positions).AddAttrib(gls.VertexPosition))
box.AddVBO(gls.NewVBO(normals).AddAttrib(gls.VertexNormal))
box.AddVBO(gls.NewVBO(uvs).AddAttrib(gls.VertexTexcoord))
// Update bounding box
box.boundingBox.Min = math32.Vector3{-wHalf, -hHalf, -lHalf}
box.boundingBox.Max = math32.Vector3{wHalf, hHalf, lHalf}
box.boundingBoxValid = true
// Update bounding sphere
box.boundingSphere.Radius = math32.Sqrt(math32.Pow(width/2,2) + math32.Pow(height/2,2) + math32.Pow(length/2,2))
box.boundingSphereValid = true
// Update area
box.area = 2*width + 2*height + 2*length
box.areaValid = true
// Update volume
box.volume = width * height * length
box.volumeValid = true
return box
}