summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore4
-rw-r--r--README.md2
-rw-r--r--geometry.go20
-rw-r--r--go.mod8
-rw-r--r--go.sum4
-rw-r--r--graphics.go151
-rw-r--r--main.go50
-rw-r--r--stl.go66
8 files changed, 304 insertions, 1 deletions
diff --git a/.gitignore b/.gitignore
index f1d43c6..bb7e2b9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,5 @@
archive
+vendor
+stlscope
+*.FCStd
+*.stl
diff --git a/README.md b/README.md
index da38359..e5542a6 100644
--- a/README.md
+++ b/README.md
@@ -11,6 +11,6 @@ This little program will allow you to view STL files from your terminal.
## Expected Usage
```
-$ stlscope model.stl
+$ stlscope -file model.stl
```
diff --git a/geometry.go b/geometry.go
new file mode 100644
index 0000000..c9453a9
--- /dev/null
+++ b/geometry.go
@@ -0,0 +1,20 @@
+// vim: shiftwidth=4 tabstop=4 noexpandtab
+
+package main
+
+type Point struct {
+ x float32
+ y float32
+ z float32
+}
+
+type Triangle struct {
+ a *Point
+ b *Point
+ c *Point
+}
+
+type Surface struct {
+ triangles []*Triangle
+}
+
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..f966925
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,8 @@
+module src.xengineering.eu/xengineering/stlscope
+
+go 1.16
+
+require (
+ github.com/go-gl/gl v0.0.0-20210315015930-ae072cafe09d
+ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20210311203641-62640a716d48
+)
diff --git a/go.sum b/go.sum
new file mode 100644
index 0000000..b443e1b
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,4 @@
+github.com/go-gl/gl v0.0.0-20210315015930-ae072cafe09d h1:o81yRlBATU4PRn97lydmsq8hTRNXI4wlR/VvUQhFRVY=
+github.com/go-gl/gl v0.0.0-20210315015930-ae072cafe09d/go.mod h1:482civXOzJJCPzJ4ZOX/pwvXBWSnzD4OKMdH4ClKGbk=
+github.com/go-gl/glfw/v3.3/glfw v0.0.0-20210311203641-62640a716d48 h1:QrUfZrT8n72FUuiABt4tbu8PwDnOPAbnj3Mql1UhdRI=
+github.com/go-gl/glfw/v3.3/glfw v0.0.0-20210311203641-62640a716d48/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
diff --git a/graphics.go b/graphics.go
new file mode 100644
index 0000000..812be3a
--- /dev/null
+++ b/graphics.go
@@ -0,0 +1,151 @@
+// vim: shiftwidth=4 tabstop=4 noexpandtab
+
+package main
+
+import (
+ "fmt"
+ "log"
+ "strings"
+
+ "github.com/go-gl/gl/v4.6-core/gl"
+ "github.com/go-gl/glfw/v3.3/glfw"
+)
+
+var (
+ // a single triangle
+ triangle = []float32{
+ 0.0, 0.5, 0.0,
+ -0.5, -0.5, 0.0,
+ 0.5, -0.5, 0.0,
+ }
+)
+
+const (
+ WINDOW_WIDTH = 500
+ WINDOW_HEIGHT = 500
+ WINDOW_TITLE = "stlscope"
+
+ // vertex shader to draw points
+ VERTEX_SHADER = `
+ #version 410
+ in vec3 vp;
+ void main() {
+ gl_Position = vec4(vp, 1.0);
+ }
+ ` + "\x00"
+
+ // fragment shader to draw surfaces
+ FRAGMENT_SHADER = `
+ #version 410
+ out vec4 frag_colour;
+ void main() {
+ frag_colour = vec4(0, 0, 1, 1); // RGBA color format
+ }
+ ` + "\x00"
+)
+
+func initGlfw() *glfw.Window {
+ log.Println("GLFW init")
+
+ if err := glfw.Init(); err != nil {
+ panic(err)
+ }
+
+ glfw.WindowHint(glfw.Resizable, glfw.False)
+ glfw.WindowHint(glfw.ContextVersionMajor, 4)
+ glfw.WindowHint(glfw.ContextVersionMinor, 6)
+ glfw.WindowHint(glfw.OpenGLProfile, glfw.OpenGLCoreProfile)
+ glfw.WindowHint(glfw.OpenGLForwardCompatible, glfw.True)
+ glfw.WindowHint(glfw.Samples, 16) // anti-aliasing
+
+ window, err := glfw.CreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, WINDOW_TITLE, nil, nil)
+ if err != nil {
+ panic(err)
+ }
+ window.MakeContextCurrent()
+
+ return window
+}
+
+// initOpenGL initializes OpenGL and returns an intiialized program
+func initOpenGL() uint32 {
+ log.Println("OpenGL init")
+
+ if err := gl.Init(); err != nil {
+ panic(err)
+ }
+ version := gl.GoStr(gl.GetString(gl.VERSION))
+ log.Println("OpenGL version", version)
+
+ vertexShader, err := compileShader(VERTEX_SHADER, gl.VERTEX_SHADER)
+ if err != nil {
+ panic(err)
+ }
+ fragmentShader, err := compileShader(FRAGMENT_SHADER, gl.FRAGMENT_SHADER)
+ if err != nil {
+ panic(err)
+ }
+
+ prog := gl.CreateProgram()
+ gl.AttachShader(prog, vertexShader)
+ gl.AttachShader(prog, fragmentShader)
+ gl.LinkProgram(prog)
+ return prog
+}
+
+func draw(vao uint32, window *glfw.Window, program uint32) {
+ gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
+ gl.UseProgram(program)
+
+ gl.BindVertexArray(vao)
+ gl.DrawArrays(gl.TRIANGLES, 0, int32(len(triangle)/3))
+
+ glfw.PollEvents()
+ window.SwapBuffers()
+}
+
+// makeVao initializes and returns a vertex array from the points provided.
+func makeVao(points []float32) uint32 {
+ log.Println("Creating VAO")
+
+ // start with vertex buffer object (VBO):
+ var vbo uint32 // VBO ID
+ gl.GenBuffers(1, &vbo)
+ gl.BindBuffer(gl.ARRAY_BUFFER, vbo)
+ gl.BufferData(gl.ARRAY_BUFFER, 4*len(points), gl.Ptr(points), gl.STATIC_DRAW)
+
+ // vertex array objects (VAO) are a feature of newer GL implementations:
+ var vao uint32 // VAO ID
+ gl.GenVertexArrays(1, &vao)
+ gl.BindVertexArray(vao)
+ gl.EnableVertexAttribArray(0)
+ gl.BindBuffer(gl.ARRAY_BUFFER, vbo)
+ gl.VertexAttribPointer(0, 3, gl.FLOAT, false, 0, nil) // tell GL to use 3D float vectors
+
+ return vao
+}
+
+func compileShader(source string, shaderType uint32) (uint32, error) {
+ log.Println("Compiling shader")
+
+ shader := gl.CreateShader(shaderType)
+
+ csources, free := gl.Strs(source)
+ gl.ShaderSource(shader, 1, csources, nil)
+ free()
+ gl.CompileShader(shader)
+
+ var status int32
+ gl.GetShaderiv(shader, gl.COMPILE_STATUS, &status)
+ if status == gl.FALSE {
+ var logLength int32
+ gl.GetShaderiv(shader, gl.INFO_LOG_LENGTH, &logLength)
+
+ log := strings.Repeat("\x00", int(logLength+1))
+ gl.GetShaderInfoLog(shader, logLength, nil, gl.Str(log))
+
+ return 0, fmt.Errorf("failed to compile %v: %v", source, log)
+ }
+
+ return shader, nil
+}
diff --git a/main.go b/main.go
new file mode 100644
index 0000000..35a4704
--- /dev/null
+++ b/main.go
@@ -0,0 +1,50 @@
+// vim: shiftwidth=4 tabstop=4 noexpandtab
+
+package main
+
+import (
+ "runtime"
+ "log"
+ "flag"
+
+ "github.com/go-gl/glfw/v3.3/glfw"
+)
+
+var (
+ stlFilePath string
+)
+
+func main() {
+
+ // read command line arguments
+ parseFlags()
+
+ // parse STL file
+ _, err := ReadBinaryStlFile(stlFilePath)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ // lock this program to one OS thread (details: https://golang.org/pkg/runtime/#LockOSThread)
+ log.Println("Locking OS thread")
+ runtime.LockOSThread()
+
+ // init GLFW and assert termination at end of main
+ window := initGlfw()
+ defer glfw.Terminate()
+
+ // init OpenGL
+ program := initOpenGL()
+
+ vao := makeVao(triangle)
+
+ // main loop
+ for !window.ShouldClose() {
+ draw(vao, window, program)
+ }
+}
+
+func parseFlags() {
+ flag.StringVar(&stlFilePath, "file", "myfile.stl", "path to the binary STL file")
+ flag.Parse()
+}
diff --git a/stl.go b/stl.go
new file mode 100644
index 0000000..ceee2b7
--- /dev/null
+++ b/stl.go
@@ -0,0 +1,66 @@
+// vim: shiftwidth=4 tabstop=4 noexpandtab
+
+package main
+
+import (
+ "io/ioutil"
+ "log"
+ "encoding/binary"
+ "math"
+)
+
+type BinaryStl struct {
+ header []byte
+ numberOfTriangles uint32
+ surface Surface
+}
+
+func ReadBinaryStlFile(filePath string) (BinaryStl, error) {
+
+ fileContent, err := ioutil.ReadFile(filePath)
+ if err != nil {
+ return BinaryStl{}, err
+ }
+
+ model := BinaryStl{}
+ 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
+}
+
+func ParseBinaryStlTriangle(data []byte) *Triangle { // FIXME: This function should only accept 50 byte slices/arrays
+
+ triangle := new(Triangle)
+ triangle.a = new(Point)
+ triangle.b = new(Point)
+ triangle.c = new(Point)
+
+ triangle.a.x = math.Float32frombits(binary.LittleEndian.Uint32(data[12:16]))
+ triangle.a.y = math.Float32frombits(binary.LittleEndian.Uint32(data[16:20]))
+ triangle.a.z = math.Float32frombits(binary.LittleEndian.Uint32(data[20:24]))
+
+ triangle.b.x = math.Float32frombits(binary.LittleEndian.Uint32(data[24:28]))
+ triangle.b.y = math.Float32frombits(binary.LittleEndian.Uint32(data[28:32]))
+ triangle.b.z = math.Float32frombits(binary.LittleEndian.Uint32(data[32:36]))
+
+ triangle.c.x = math.Float32frombits(binary.LittleEndian.Uint32(data[36:40]))
+ triangle.c.y = math.Float32frombits(binary.LittleEndian.Uint32(data[40:44]))
+ triangle.c.z = math.Float32frombits(binary.LittleEndian.Uint32(data[44:48]))
+
+ return triangle
+}
+