├── .gitignore ├── LICENSE.txt ├── README.md ├── bundle.go ├── bundle.wasm ├── color ├── color.go ├── gradient.go ├── gradient_test.go └── interpolation.go ├── gltypes ├── arrays.go └── gltypes.go ├── go.mod ├── go.sum ├── index.html ├── main.go ├── makefile ├── models ├── model.go └── stl.go ├── renderer └── renderer.go └── wasm_exec.js /.gitignore: -------------------------------------------------------------------------------- 1 | wasm-rotating-cube 2 | .idea 3 | *.stl 4 | wasm-stl-viewer 5 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Eric Suedmeier 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WASM STL Viewer 2 | 3 | This project is built on my wasm-rotating-cube project, Check it out for a more basic example. Instead of drawing only a 3D cube, this project draws arbitrary models imported with from STLs. 4 | 5 | ## Demo 6 | 7 | To see this project running check out this link: [https://bobcob7.github.io/wasm-stl-viewer/](https://bobcob7.github.io/wasm-stl-viewer/) 8 | 9 | ## Running 10 | 11 | To run you can use the makefile with `make run` 12 | -------------------------------------------------------------------------------- /bundle.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/base64" 5 | "errors" 6 | "fmt" 7 | "math" 8 | "strings" 9 | "syscall/js" 10 | 11 | "github.com/bobcob7/wasm-stl-viewer/gltypes" 12 | "github.com/bobcob7/wasm-stl-viewer/models" 13 | "github.com/bobcob7/wasm-stl-viewer/renderer" 14 | ) 15 | 16 | var ( 17 | gl js.Value 18 | glTypes gltypes.GLTypes 19 | ) 20 | 21 | //// BUFFERS + SHADERS //// 22 | // Shamelessly copied from https://www.tutorialspoint.com/webgl/webgl_cube_rotation.htm // 23 | var verticesNative = []float32{ 24 | -1, -1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 25 | -1, -1, 1, 1, -1, 1, 1, 1, 1, -1, 1, 1, 26 | -1, -1, -1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 27 | 1, -1, -1, 1, 1, -1, 1, 1, 1, 1, -1, 1, 28 | -1, -1, -1, -1, -1, 1, 1, -1, 1, 1, -1, -1, 29 | -1, 1, -1, -1, 1, 1, 1, 1, 1, 1, 1, -1, 30 | } 31 | var colorsNative = []float32{ 32 | 5, 3, 7, 5, 3, 7, 5, 3, 7, 5, 3, 7, 33 | 1, 1, 3, 1, 1, 3, 1, 1, 3, 1, 1, 3, 34 | 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 35 | 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 36 | 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 37 | 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 38 | } 39 | var indicesNative = []uint32{ 40 | 0, 1, 2, 0, 2, 3, 4, 5, 6, 4, 6, 7, 41 | 8, 9, 10, 8, 10, 11, 12, 13, 14, 12, 14, 15, 42 | 16, 17, 18, 16, 18, 19, 20, 21, 22, 20, 22, 23, 43 | } 44 | 45 | const vertShaderCode = ` 46 | attribute vec3 position; 47 | uniform mat4 Pmatrix; 48 | uniform mat4 Vmatrix; 49 | uniform mat4 Mmatrix; 50 | attribute vec3 color; 51 | varying vec3 vColor; 52 | 53 | void main(void) { 54 | gl_Position = Pmatrix*Vmatrix*Mmatrix*vec4(position, 1.); 55 | vColor = color; 56 | } 57 | ` 58 | const fragShaderCode = ` 59 | precision mediump float; 60 | varying vec3 vColor; 61 | void main(void) { 62 | gl_FragColor = vec4(vColor, 1.); 63 | } 64 | ` 65 | 66 | var reader js.Value 67 | var render renderer.Renderer 68 | var speedSliderXValue js.Value 69 | var speedSliderYValue js.Value 70 | var speedSliderZValue js.Value 71 | var canvasElement js.Value 72 | var currentZoom float32 = 3 73 | 74 | func uploading(this js.Value, args []js.Value) interface{} { 75 | files := this.Get("files") 76 | file := files.Index(0) 77 | currentFileName := file.Get("name").String() 78 | fmt.Println("Uploading", currentFileName) 79 | reader.Call("readAsDataURL", file) 80 | return nil 81 | } 82 | 83 | func parseBase64File(input string) (output []byte, err error) { 84 | searchString := "base64," 85 | index := strings.Index(input, searchString) 86 | if index < 0 { 87 | err = errors.New("Error opening file") 88 | return 89 | } 90 | sBuffer := input[index+len(searchString):] 91 | return base64.StdEncoding.DecodeString(sBuffer) 92 | } 93 | 94 | func uploaded(this js.Value, args []js.Value) interface{} { 95 | fmt.Println("Finished uploading") 96 | result := args[0].Get("target").Get("result").String() 97 | func() { 98 | defer func() { 99 | if r := recover(); r != nil { 100 | fmt.Println("Recovered in upload", r) 101 | js.Global().Call("alert", "Failed to parse file") 102 | } 103 | }() 104 | uploadedFile, err := parseBase64File(result) 105 | if err != nil { 106 | panic(err) 107 | } 108 | stlSolid, err := models.NewSTL(uploadedFile) 109 | if err != nil { 110 | js.Global().Call("alert", "Could not parse file") 111 | } 112 | vert, colors, indices := stlSolid.GetModel() 113 | modelSize := getMaxScalar(vert) 114 | currentZoom = modelSize * 3 115 | render.SetZoom(currentZoom) 116 | render.SetModel(colors, vert, indices) 117 | }() 118 | return nil 119 | } 120 | 121 | func getMaxScalar(vertices []float32) float32 { 122 | var max float32 123 | for baseIndex := 0; baseIndex < len(vertices); baseIndex += 3 { 124 | testScale := scalar(vertices[baseIndex], vertices[baseIndex], vertices[baseIndex]) 125 | if testScale > max { 126 | max = testScale 127 | } 128 | } 129 | return max 130 | } 131 | 132 | func scalar(x float32, y float32, z float32) float32 { 133 | xy := math.Sqrt(float64(x*x + y*y)) 134 | return float32(math.Sqrt(xy*xy + float64(z*z))) 135 | } 136 | 137 | func uploadError(this js.Value, args []js.Value) interface{} { 138 | fmt.Println("Uploading Error") 139 | return nil 140 | } 141 | 142 | func uploadAborted(this js.Value, args []js.Value) interface{} { 143 | fmt.Println("Upload Aborted") 144 | return nil 145 | } 146 | 147 | func main() { 148 | fmt.Println("Returned normally from f.") 149 | 150 | // Init Canvas stuff 151 | doc := js.Global().Get("document") 152 | 153 | canvasResizeCallback := js.FuncOf(canvasResize) 154 | canvasElement = doc.Call("getElementById", "gocanvas") 155 | js.Global().Get("window").Call("addEventListener", "resize", canvasResizeCallback) 156 | 157 | width := canvasElement.Get("clientWidth").Int() 158 | height := canvasElement.Get("clientHeight").Int() 159 | canvasElement.Set("width", width) 160 | canvasElement.Set("height", height) 161 | upload := doc.Call("getElementById", "upload") 162 | newReader := js.Global().Get("FileReader") 163 | reader = newReader.New() 164 | 165 | sliderSpeedXCallback := js.FuncOf(sliderChangeX) 166 | speedSliderX := doc.Call("getElementById", "speedSliderX") 167 | speedSliderX.Call("addEventListener", "input", sliderSpeedXCallback) 168 | speedSliderXValue = doc.Call("getElementById", "speedSliderXValue") 169 | 170 | sliderSpeedYCallback := js.FuncOf(sliderChangeY) 171 | speedSliderY := doc.Call("getElementById", "speedSliderY") 172 | speedSliderY.Call("addEventListener", "input", sliderSpeedYCallback) 173 | speedSliderYValue = doc.Call("getElementById", "speedSliderYValue") 174 | 175 | sliderSpeedZCallback := js.FuncOf(sliderChangeZ) 176 | speedSliderZ := doc.Call("getElementById", "speedSliderZ") 177 | speedSliderZ.Call("addEventListener", "input", sliderSpeedZCallback) 178 | speedSliderZValue = doc.Call("getElementById", "speedSliderZValue") 179 | 180 | zoomChangeCallback := js.FuncOf(zoomChange) 181 | js.Global().Get("window").Call("addEventListener", "wheel", zoomChangeCallback) 182 | 183 | uploadCallback := js.FuncOf(uploading) 184 | uploadedCallback := js.FuncOf(uploaded) 185 | errorUploadCallback := js.FuncOf(uploadError) 186 | uploadAbortedCallback := js.FuncOf(uploadAborted) 187 | defer uploadCallback.Release() 188 | defer uploadedCallback.Release() 189 | defer errorUploadCallback.Release() 190 | defer uploadAbortedCallback.Release() 191 | reader.Call("addEventListener", "load", uploadedCallback) 192 | reader.Call("addEventListener", "error", errorUploadCallback) 193 | reader.Call("addEventListener", "abort", uploadAbortedCallback) 194 | upload.Call("addEventListener", "change", uploadCallback) 195 | 196 | gl = canvasElement.Call("getContext", "webgl") 197 | if gl.IsUndefined() { 198 | gl = canvasElement.Call("getContext", "experimental-webgl") 199 | } 200 | if gl.IsUndefined() { 201 | js.Global().Call("alert", "browser might not support webgl") 202 | return 203 | } 204 | 205 | config := renderer.InitialConfig{ 206 | Width: width, 207 | Height: height, 208 | SpeedX: 0.5, 209 | SpeedY: 0.3, 210 | SpeedZ: 0.2, 211 | Colors: colorsNative, 212 | Vertices: verticesNative, 213 | Indices: indicesNative, 214 | FragmentShaderCode: fragShaderCode, 215 | VertexShaderCode: vertShaderCode, 216 | } 217 | var err error 218 | render, err = renderer.NewRenderer(gl, config) 219 | if err != nil { 220 | js.Global().Call("alert", fmt.Sprintf("Cannot load webgl %v", err)) 221 | return 222 | } 223 | render.SetZoom(currentZoom) 224 | defer render.Release() 225 | 226 | x, y, z := render.GetSpeed() 227 | speedSliderX.Set("value", fmt.Sprint(x)) 228 | speedSliderXValue.Set("innerHTML", fmt.Sprint(x)) 229 | speedSliderY.Set("value", fmt.Sprint(y)) 230 | speedSliderYValue.Set("innerHTML", fmt.Sprint(y)) 231 | speedSliderZ.Set("value", fmt.Sprint(z)) 232 | speedSliderZValue.Set("innerHTML", fmt.Sprint(z)) 233 | 234 | var renderFrame js.Func 235 | renderFrame = js.FuncOf(func(this js.Value, args []js.Value) interface{} { 236 | render.Render(this, args) 237 | js.Global().Call("requestAnimationFrame", renderFrame) 238 | return nil 239 | }) 240 | js.Global().Call("requestAnimationFrame", renderFrame) 241 | 242 | done := make(chan struct{}, 0) 243 | <-done 244 | } 245 | 246 | func canvasResize(this js.Value, args []js.Value) interface{} { 247 | width := canvasElement.Get("clientWidth").Int() 248 | height := canvasElement.Get("clientHeight").Int() 249 | canvasElement.Set("width", width) 250 | canvasElement.Set("height", height) 251 | render.SetSize(height, width) 252 | return nil 253 | } 254 | 255 | func sliderChangeX(this js.Value, args []js.Value) interface{} { 256 | var speed float32 257 | sSpeed := this.Get("value").String() 258 | fmt.Sscan(sSpeed, &speed) 259 | render.SetSpeedX(speed) 260 | speedSliderXValue.Set("innerHTML", sSpeed) 261 | return nil 262 | } 263 | 264 | func sliderChangeY(this js.Value, args []js.Value) interface{} { 265 | var speed float32 266 | sSpeed := this.Get("value").String() 267 | fmt.Sscan(sSpeed, &speed) 268 | render.SetSpeedY(speed) 269 | speedSliderYValue.Set("innerHTML", sSpeed) 270 | return nil 271 | } 272 | 273 | func sliderChangeZ(this js.Value, args []js.Value) interface{} { 274 | var speed float32 275 | sSpeed := this.Get("value").String() 276 | fmt.Sscan(sSpeed, &speed) 277 | render.SetSpeedZ(speed) 278 | speedSliderZValue.Set("innerHTML", sSpeed) 279 | return nil 280 | } 281 | 282 | func zoomChange(this js.Value, args []js.Value) interface{} { 283 | deltaY := args[0].Get("deltaY").Float() 284 | deltaScale := 1 - (float32(deltaY) * 0.001) 285 | currentZoom *= deltaScale 286 | render.SetZoom(currentZoom) 287 | return nil 288 | } 289 | -------------------------------------------------------------------------------- /bundle.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bobcob7/wasm-stl-viewer/77289c9d58dade8be6b7d0ab8248ab70b0f644a4/bundle.wasm -------------------------------------------------------------------------------- /color/color.go: -------------------------------------------------------------------------------- 1 | package color 2 | 3 | import ( 4 | "math/rand" 5 | ) 6 | 7 | type Color struct { 8 | Red float32 9 | Green float32 10 | Blue float32 11 | } 12 | 13 | func NewRandomColor() Color { 14 | return Color{ 15 | rand.Float32(), 16 | rand.Float32(), 17 | rand.Float32(), 18 | } 19 | } 20 | 21 | func (c Color) Subtract(d Color) Color { 22 | return Color{ 23 | c.Red - d.Red, 24 | c.Green - d.Green, 25 | c.Blue - d.Blue, 26 | } 27 | } 28 | 29 | func (c Color) Add(d Color) Color { 30 | return Color{ 31 | c.Red + d.Red, 32 | c.Green + d.Green, 33 | c.Blue + d.Blue, 34 | } 35 | } 36 | 37 | func (c Color) MultiplyFloat(x float32) Color { 38 | return Color{ 39 | c.Red * x, 40 | c.Green * x, 41 | c.Blue * x, 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /color/gradient.go: -------------------------------------------------------------------------------- 1 | package color 2 | 3 | import ( 4 | "math" 5 | ) 6 | 7 | func GenerateGradient(numColors int, steps int) []Color { 8 | // Evenly space colors 9 | distribution := distributeColors(numColors, steps) 10 | colors := make([]Color, numColors) 11 | for i := 0; i < numColors; i++ { 12 | colors[i] = NewRandomColor() 13 | } 14 | // Interpolate between colors 15 | outputBuffer := make([]Color, 0, steps) 16 | for index := 0; index < numColors; index++ { 17 | if index >= numColors-1 { 18 | size := steps - distribution[index] 19 | interpolation := NewColorInterpolation(colors[index-1], colors[index]) 20 | buffer := generateSingleGradient(interpolation, size) 21 | outputBuffer = append(outputBuffer, buffer...) 22 | break 23 | } 24 | currentStep := distribution[index] 25 | nextStep := distribution[index+1] 26 | size := nextStep - currentStep 27 | interpolation := NewColorInterpolation(colors[index], colors[index+1]) 28 | buffer := generateSingleGradient(interpolation, size) 29 | outputBuffer = append(outputBuffer, buffer...) 30 | } 31 | // Create output 32 | return outputBuffer 33 | } 34 | 35 | func distributeColors(numColors int, steps int) []int { 36 | diff := int(math.Ceil(float64(steps) / float64(numColors))) 37 | output := make([]int, numColors) 38 | for i := 0; i < numColors; i++ { 39 | output[i] = diff * i 40 | } 41 | return output 42 | } 43 | 44 | func generateSingleGradient(c ColorInterpolation, numSteps int) []Color { 45 | output := make([]Color, numSteps) 46 | for i := 0; i < numSteps; i++ { 47 | percent := float32(i) / float32(numSteps) 48 | output[i] = c.Interpolate(percent) 49 | } 50 | return output 51 | } 52 | -------------------------------------------------------------------------------- /color/gradient_test.go: -------------------------------------------------------------------------------- 1 | package color 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | func Test_distributeColors(t *testing.T) { 9 | type args struct { 10 | numColors int 11 | steps int 12 | } 13 | tests := []struct { 14 | name string 15 | args args 16 | want []int 17 | }{ 18 | { 19 | name: "3 Colors", 20 | args: args{ 21 | numColors: 3, 22 | steps: 5, 23 | }, 24 | want: []int{0, 2, 4}, 25 | }, 26 | { 27 | name: "Extra 1 Color", 28 | args: args{ 29 | numColors: 3, 30 | steps: 7, 31 | }, 32 | want: []int{0, 3, 6}, 33 | }, 34 | { 35 | name: "Extra 2 Colors", 36 | args: args{ 37 | numColors: 3, 38 | steps: 8, 39 | }, 40 | want: []int{0, 3, 6}, 41 | }, 42 | { 43 | name: "Extra 3 Colors", 44 | args: args{ 45 | numColors: 3, 46 | steps: 9, 47 | }, 48 | want: []int{0, 3, 6}, 49 | }, 50 | { 51 | name: "Extra 1 Color 10", 52 | args: args{ 53 | numColors: 3, 54 | steps: 10, 55 | }, 56 | want: []int{0, 4, 8}, 57 | }, 58 | } 59 | for _, tt := range tests { 60 | t.Run(tt.name, func(t *testing.T) { 61 | if got := distributeColors(tt.args.numColors, tt.args.steps); !reflect.DeepEqual(got, tt.want) { 62 | t.Errorf("distributeColors() = %v, want %v", got, tt.want) 63 | } 64 | }) 65 | } 66 | } 67 | 68 | func TestNewRandomGradient(t *testing.T) { 69 | type args struct { 70 | numColors int 71 | steps int 72 | } 73 | tests := []struct { 74 | name string 75 | args args 76 | wantLen int 77 | want Gradient 78 | }{ 79 | { 80 | name: "3 Colors", 81 | args: args{ 82 | numColors: 3, 83 | steps: 5, 84 | }, 85 | want: Gradient{[]Color{}}, 86 | }, 87 | { 88 | name: "Extra 2 Colors", 89 | args: args{ 90 | numColors: 3, 91 | steps: 8, 92 | }, 93 | want: Gradient{[]Color{}}, 94 | }, 95 | } 96 | for _, tt := range tests { 97 | t.Run(tt.name, func(t *testing.T) { 98 | got := NewRandomGradient(tt.args.numColors, tt.args.steps) 99 | if len(got.colors) != tt.args.steps { 100 | t.Errorf("NewRandomGradient().Len = %d, want %d", len(got.colors), tt.args.steps) 101 | } 102 | }) 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /color/interpolation.go: -------------------------------------------------------------------------------- 1 | package color 2 | 3 | func NewColorInterpolation(a Color, b Color) ColorInterpolation { 4 | return ColorInterpolation{ 5 | a, 6 | b, 7 | a.Subtract(b), 8 | } 9 | } 10 | 11 | type ColorInterpolation struct { 12 | startColor Color 13 | endColor Color 14 | deltaColor Color 15 | } 16 | 17 | func (c ColorInterpolation) Interpolate(percent float32) Color { 18 | scaled := c.deltaColor.MultiplyFloat(percent) 19 | return c.startColor.Add(scaled) 20 | } 21 | -------------------------------------------------------------------------------- /gltypes/arrays.go: -------------------------------------------------------------------------------- 1 | package gltypes 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | "runtime" 7 | "syscall/js" 8 | "unsafe" 9 | ) 10 | 11 | // Shamelessly stolen from: https://github.com/golang/go/issues/32402 12 | // Thanks hajimehoshi 13 | func sliceToByteSlice(s interface{}) []byte { 14 | switch s := s.(type) { 15 | case []int8: 16 | h := (*reflect.SliceHeader)(unsafe.Pointer(&s)) 17 | return *(*[]byte)(unsafe.Pointer(h)) 18 | case []int16: 19 | h := (*reflect.SliceHeader)(unsafe.Pointer(&s)) 20 | h.Len *= 2 21 | h.Cap *= 2 22 | return *(*[]byte)(unsafe.Pointer(h)) 23 | case []int32: 24 | h := (*reflect.SliceHeader)(unsafe.Pointer(&s)) 25 | h.Len *= 4 26 | h.Cap *= 4 27 | return *(*[]byte)(unsafe.Pointer(h)) 28 | case []int64: 29 | h := (*reflect.SliceHeader)(unsafe.Pointer(&s)) 30 | h.Len *= 8 31 | h.Cap *= 8 32 | return *(*[]byte)(unsafe.Pointer(h)) 33 | case []uint8: 34 | return s 35 | case []uint16: 36 | h := (*reflect.SliceHeader)(unsafe.Pointer(&s)) 37 | h.Len *= 2 38 | h.Cap *= 2 39 | return *(*[]byte)(unsafe.Pointer(h)) 40 | case []uint32: 41 | h := (*reflect.SliceHeader)(unsafe.Pointer(&s)) 42 | h.Len *= 4 43 | h.Cap *= 4 44 | return *(*[]byte)(unsafe.Pointer(h)) 45 | case []uint64: 46 | h := (*reflect.SliceHeader)(unsafe.Pointer(&s)) 47 | h.Len *= 8 48 | h.Cap *= 8 49 | return *(*[]byte)(unsafe.Pointer(h)) 50 | case []float32: 51 | h := (*reflect.SliceHeader)(unsafe.Pointer(&s)) 52 | h.Len *= 4 53 | h.Cap *= 4 54 | return *(*[]byte)(unsafe.Pointer(h)) 55 | case []float64: 56 | h := (*reflect.SliceHeader)(unsafe.Pointer(&s)) 57 | h.Len *= 8 58 | h.Cap *= 8 59 | return *(*[]byte)(unsafe.Pointer(h)) 60 | default: 61 | panic(fmt.Sprintf("jsutil: unexpected value at sliceToBytesSlice: %T", s)) 62 | } 63 | } 64 | func SliceToTypedArray(s interface{}) js.Value { 65 | switch s := s.(type) { 66 | case []int8: 67 | a := js.Global().Get("Uint8Array").New(len(s)) 68 | js.CopyBytesToJS(a, sliceToByteSlice(s)) 69 | runtime.KeepAlive(s) 70 | buf := a.Get("buffer") 71 | return js.Global().Get("Int8Array").New(buf, a.Get("byteOffset"), a.Get("byteLength")) 72 | case []int16: 73 | a := js.Global().Get("Uint8Array").New(len(s) * 2) 74 | js.CopyBytesToJS(a, sliceToByteSlice(s)) 75 | runtime.KeepAlive(s) 76 | buf := a.Get("buffer") 77 | return js.Global().Get("Int16Array").New(buf, a.Get("byteOffset"), a.Get("byteLength").Int()/2) 78 | case []int32: 79 | a := js.Global().Get("Uint8Array").New(len(s) * 4) 80 | js.CopyBytesToJS(a, sliceToByteSlice(s)) 81 | runtime.KeepAlive(s) 82 | buf := a.Get("buffer") 83 | return js.Global().Get("Int32Array").New(buf, a.Get("byteOffset"), a.Get("byteLength").Int()/4) 84 | case []uint8: 85 | a := js.Global().Get("Uint8Array").New(len(s)) 86 | js.CopyBytesToJS(a, s) 87 | runtime.KeepAlive(s) 88 | return a 89 | case []uint16: 90 | a := js.Global().Get("Uint8Array").New(len(s) * 2) 91 | js.CopyBytesToJS(a, sliceToByteSlice(s)) 92 | runtime.KeepAlive(s) 93 | buf := a.Get("buffer") 94 | return js.Global().Get("Uint16Array").New(buf, a.Get("byteOffset"), a.Get("byteLength").Int()/2) 95 | case []uint32: 96 | a := js.Global().Get("Uint8Array").New(len(s) * 4) 97 | js.CopyBytesToJS(a, sliceToByteSlice(s)) 98 | runtime.KeepAlive(s) 99 | buf := a.Get("buffer") 100 | return js.Global().Get("Uint32Array").New(buf, a.Get("byteOffset"), a.Get("byteLength").Int()/4) 101 | case []float32: 102 | a := js.Global().Get("Uint8Array").New(len(s) * 4) 103 | js.CopyBytesToJS(a, sliceToByteSlice(s)) 104 | runtime.KeepAlive(s) 105 | buf := a.Get("buffer") 106 | return js.Global().Get("Float32Array").New(buf, a.Get("byteOffset"), a.Get("byteLength").Int()/4) 107 | case []float64: 108 | a := js.Global().Get("Uint8Array").New(len(s) * 8) 109 | js.CopyBytesToJS(a, sliceToByteSlice(s)) 110 | runtime.KeepAlive(s) 111 | buf := a.Get("buffer") 112 | return js.Global().Get("Float64Array").New(buf, a.Get("byteOffset"), a.Get("byteLength").Int()/8) 113 | default: 114 | panic(fmt.Sprintf("jsutil: unexpected value at SliceToTypedArray: %T", s)) 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /gltypes/gltypes.go: -------------------------------------------------------------------------------- 1 | package gltypes 2 | 3 | import ( 4 | "errors" 5 | "syscall/js" 6 | ) 7 | 8 | // GLTypes provides WebGL bindings. 9 | type GLTypes struct { 10 | StaticDraw js.Value 11 | ArrayBuffer js.Value 12 | ElementArrayBuffer js.Value 13 | VertexShader js.Value 14 | FragmentShader js.Value 15 | Float js.Value 16 | DepthTest js.Value 17 | ColorBufferBit js.Value 18 | DepthBufferBit js.Value 19 | Triangles js.Value 20 | UnsignedShort js.Value 21 | UnsignedInt js.Value 22 | LEqual js.Value 23 | LineLoop js.Value 24 | } 25 | 26 | // New grabs the WebGL bindings from a GL context. 27 | func (types *GLTypes) New(gl js.Value) error { 28 | types.StaticDraw = gl.Get("STATIC_DRAW") 29 | types.ArrayBuffer = gl.Get("ARRAY_BUFFER") 30 | types.ElementArrayBuffer = gl.Get("ELEMENT_ARRAY_BUFFER") 31 | types.VertexShader = gl.Get("VERTEX_SHADER") 32 | types.FragmentShader = gl.Get("FRAGMENT_SHADER") 33 | types.Float = gl.Get("FLOAT") 34 | types.DepthTest = gl.Get("DEPTH_TEST") 35 | types.ColorBufferBit = gl.Get("COLOR_BUFFER_BIT") 36 | types.Triangles = gl.Get("TRIANGLES") 37 | types.UnsignedShort = gl.Get("UNSIGNED_SHORT") 38 | types.LEqual = gl.Get("LEQUAL") 39 | types.DepthBufferBit = gl.Get("DEPTH_BUFFER_BIT") 40 | types.LineLoop = gl.Get("LINE_LOOP") 41 | enabled := gl.Call("getExtension", "OES_element_index_uint") 42 | if !enabled.Truthy() { 43 | return errors.New("missing extension: OES_element_index_uint") 44 | } 45 | types.UnsignedInt = gl.Get("UNSIGNED_INT") 46 | return nil 47 | } 48 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bobcob7/wasm-stl-viewer 2 | 3 | go 1.13 4 | 5 | require ( 6 | github.com/go-gl/mathgl v0.0.0-20180804195959-cdf14b6b8f8a 7 | gitlab.com/russoj88/stl v1.0.2 8 | golang.org/x/image v0.0.0-20180926015637-991ec62608f3 // indirect 9 | ) 10 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/go-gl/mathgl v0.0.0-20180804195959-cdf14b6b8f8a h1:2n5w2v3knlspzjJWyQPC0j88Mwvq0SZV0Jdws34GJwc= 2 | github.com/go-gl/mathgl v0.0.0-20180804195959-cdf14b6b8f8a/go.mod h1:dvrdneKbyWbK2skTda0nM4B9zSlS2GZSnjX7itr/skQ= 3 | gitlab.com/russoj88/stl v1.0.2 h1:jByJjAbp5G+Dn4yiSozUXmIDi5Jdm8tzWBkk6QsoZHk= 4 | gitlab.com/russoj88/stl v1.0.2/go.mod h1:+F6mWiD6X/9Z37sZdN2EUaQHNasZdsNX23f2t8iM1HY= 5 | golang.org/x/image v0.0.0-20180926015637-991ec62608f3 h1:5IfA9fqItkh2alJW94tvQk+6+RF9MW2q9DzwE8DBddQ= 6 | golang.org/x/image v0.0.0-20180926015637-991ec62608f3/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= 7 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
39 | WASM STL Viewer40 | |
41 |
42 | Rotation X 43 | 44 | |
46 |
47 | Rotation Y 48 | 49 | |
51 |
52 | Rotation Z 53 | 54 | |
56 |
57 | Local STL File 58 | 59 | |
60 |