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
|
// vim: shiftwidth=4 tabstop=4 noexpandtab
/*
Wikipedia about STL file format: https://en.wikipedia.org/wiki/STL_(file_format)
*/
package main
import (
"io/ioutil"
"log"
"encoding/binary"
"math"
)
// StlModel is the Go-internal representation of a STL model.
type StlModel struct {
header []byte
numberOfTriangles uint32
surface Surface
}
// Vector3 is a three-dimensional vector based on 32 bit floats.
type Vector3 [3]float32
// ReadBinaryStlFile reads and parses a given binary STL file.
func ReadBinaryStlFile(filePath string) (StlModel, error) {
fileContent, err := ioutil.ReadFile(filePath)
if err != nil {
return StlModel{}, err
}
model := StlModel{}
model.surface = Surface{}
model.header = fileContent[0:80]
log.Printf("STL header: '%s'\n", string(model.header))
model.numberOfTriangles = binary.LittleEndian.Uint32(fileContent[80:84])
log.Printf("STL model has %d triangles\n", model.numberOfTriangles)
var i uint32
for i = 0; i < model.numberOfTriangles; i++ { // for each expected triangle
start := 84 + i*50 // 50 bytes is length of one triangle
end := 84 + (i+1)*50
model.surface.triangles = append(model.surface.triangles,
ParseBinaryStlTriangle(fileContent[start:end]))
}
return model,nil
}
// ParseBinaryStlTriangle parses the 50 bytes of the STL file representing a
// triangle (surface normal is ignored).
func ParseBinaryStlTriangle(data []byte) *Triangle {
// FIXME: This function should only accept 50 byte slices/arrays
// allocate a new triangle and three corner points on the heap
triangle := new(Triangle)
triangle.points[0] = new(Point)
triangle.points[1] = new(Point)
triangle.points[2] = new(Point)
// parse x, y and z coordinate for corner point a
triangle.points[0].scalars[0] = math.Float32frombits(
binary.LittleEndian.Uint32(data[12:16]))
triangle.points[0].scalars[1] = math.Float32frombits(
binary.LittleEndian.Uint32(data[16:20]))
triangle.points[0].scalars[2] = math.Float32frombits(
binary.LittleEndian.Uint32(data[20:24]))
// parse x, y and z coordinate for corner point b
triangle.points[1].scalars[0] = math.Float32frombits(
binary.LittleEndian.Uint32(data[24:28]))
triangle.points[1].scalars[1] = math.Float32frombits(
binary.LittleEndian.Uint32(data[28:32]))
triangle.points[1].scalars[2] = math.Float32frombits(
binary.LittleEndian.Uint32(data[32:36]))
// parse x, y and z coordinate for corner point c
triangle.points[2].scalars[0] = math.Float32frombits(
binary.LittleEndian.Uint32(data[36:40]))
triangle.points[2].scalars[1] = math.Float32frombits(
binary.LittleEndian.Uint32(data[40:44]))
triangle.points[2].scalars[2] = math.Float32frombits(
binary.LittleEndian.Uint32(data[44:48]))
return triangle
}
// toVertices converts a STL model into vertices suitable for OpenGL rendering.
func (stl StlModel) toVertices() (vertex_position []float32,
vertex_normal []float32) {
vertex_position = make([]float32, stl.numberOfTriangles * 9)
vertex_normal = make([]float32, stl.numberOfTriangles * 9)
var point0,point1,point2,edge0,edge1,normal Vector3;
for triangleIndex,triangle := range(stl.surface.triangles) {
for pointIndex,point := range(triangle.points) {
for scalarIndex,scalar := range(point.scalars) {
i := triangleIndex * 9 + pointIndex * 3 + scalarIndex
vertex_position[i] = scalar
}
}
// calculate normal
point0 = Vector3{triangle.points[0].scalars[0],
triangle.points[0].scalars[1], triangle.points[0].scalars[2]}
point1 = Vector3{triangle.points[1].scalars[0],
triangle.points[1].scalars[1], triangle.points[1].scalars[2]}
point2 = Vector3{triangle.points[2].scalars[0],
triangle.points[2].scalars[1], triangle.points[2].scalars[2]}
edge0 = point1.subtract(point0)
edge1 = point2.subtract(point1)
normal = edge0.crossProduct(edge1)
normal.divideScalar(2.0) // length of normal vector corresponds to
// triangle area
// save normal to each vertex of the triangle
for i := 0; i<3; i++ {
vertex_normal[triangleIndex*9+i*3+0] = normal[0]
vertex_normal[triangleIndex*9+i*3+1] = normal[1]
vertex_normal[triangleIndex*9+i*3+2] = normal[2]
}
}
return vertex_position, vertex_normal
}
// divideScalar divides a three-dimensional vector by a scalar.
func (vector *Vector3) divideScalar(scalar float32) {
vector[0] = vector[0] / scalar
vector[1] = vector[1] / scalar
vector[2] = vector[2] / scalar
}
// subtract subtracts vectorB from vectorA.
func (vectorA Vector3) subtract(vectorB Vector3) (vectorC Vector3) {
vectorC[0] = vectorA[0] - vectorB[0]
vectorC[1] = vectorA[1] - vectorB[1]
vectorC[2] = vectorA[2] - vectorB[2]
return vectorC
}
// crossProduct returns the cross product vectorA x vectorB.
func (vectorA Vector3) crossProduct(vectorB Vector3) (vectorC Vector3) {
vectorC[0] = vectorA[1] * vectorB[2] - vectorA[2] * vectorB[1]
vectorC[1] = vectorA[2] * vectorB[0] - vectorA[0] * vectorB[2]
vectorC[2] = vectorA[0] * vectorB[1] - vectorA[1] * vectorB[0]
return vectorC
}
|