From d56044f05c90f98d3217e2e08e3dead6e9d21a66 Mon Sep 17 00:00:00 2001 From: dqn Date: Fri, 7 Jun 2024 00:50:09 +0800 Subject: [PATCH] glb --- .gitignore | 3 +- cmd/stl2gltf.go | 3 + convert.go | 32 +++++----- convert_glb.go | 155 ++++++++++++++++++++++++++++++++++++++++++++++++ data.go | 5 +- 5 files changed, 177 insertions(+), 21 deletions(-) create mode 100644 convert_glb.go diff --git a/.gitignore b/.gitignore index 54c8b8a..1074905 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -*.gltf \ No newline at end of file +*.gltf +*.glb \ No newline at end of file diff --git a/cmd/stl2gltf.go b/cmd/stl2gltf.go index 9692669..456762c 100644 --- a/cmd/stl2gltf.go +++ b/cmd/stl2gltf.go @@ -7,10 +7,13 @@ import ( func main() { stl := stl2gltf.Load("./example/0c448109-7ac9-4f6d-aaf6-ce6bb744032f.stl") stl2gltf.Convert(stl, "./example/0c448109-7ac9-4f6d-aaf6-ce6bb744032f.gltf") + stl2gltf.ConvertGLB(stl, "./example/0c448109-7ac9-4f6d-aaf6-ce6bb744032f.glb") stl = stl2gltf.Load("./example/1ec91891-c25a-4905-a79c-f3bc96045d37.stl") stl2gltf.Convert(stl, "./example/1ec91891-c25a-4905-a79c-f3bc96045d37.gltf") + stl2gltf.ConvertGLB(stl, "./example/1ec91891-c25a-4905-a79c-f3bc96045d37.glb") stl = stl2gltf.Load("./example/3ea057e5-1520-4898-a59c-f33a6c955bd7.stl") stl2gltf.Convert(stl, "./example/3ea057e5-1520-4898-a59c-f33a6c955bd7.gltf") + stl2gltf.ConvertGLB(stl, "./example/3ea057e5-1520-4898-a59c-f33a6c955bd7.glb") } diff --git a/convert.go b/convert.go index bf2b603..1a2ef09 100644 --- a/convert.go +++ b/convert.go @@ -49,14 +49,14 @@ func Convert(stl STL, outFile string) { } indexBuf = padding(indexBuf) - log.Println(len(positionBuf), len(normalBuf), len(indexBuf)) + log.Println("positions: ", len(positionBuf), " normals: ", len(normalBuf), " indices: ", len(indexBuf)) // concat allData := make([]byte, 0) allData = append(allData, indexBuf...) // Indices: 0 allData = append(allData, normalBuf...) // attributes["NORMAL"] = 1 allData = append(allData, positionBuf...) // attributes["POSITION"] = 2 - + base := base64.StdEncoding.EncodeToString(allData) attributes := make(map[string]int, 0) @@ -64,10 +64,10 @@ func Convert(stl STL, outFile string) { attributes["POSITION"] = 2 gltf := GLTF{ - Asset: Asset{Version: "2.0"}, - Scenes: []Scene{{Nodes: []int{0}}}, - Nodes: []Node{{Mesh: 0}}, - Meshes: []Mesh{{Primitives: []Primitive{{Attributes: attributes, Indices: 0, Mode: 4}}}}, + Asset: Asset{Version: "2.0"}, + Scenes: []Scene{{Nodes: []int{0}}}, + Nodes: []Node{{Mesh: 0}}, + Meshes: []Mesh{{Primitives: []Primitive{{Attributes: attributes, Indices: 0, Mode: 4}}}}, // ByteLength not length after base64 Buffers: []Buffer{{Uri: fmt.Sprintf("data:application/gltf-buffer;base64,%s", base), ByteLength: len(allData)}}, BufferViews: []BufferView{ @@ -83,40 +83,40 @@ func Convert(stl STL, outFile string) { Buffer: 0, ByteOffset: len(indexBuf), ByteLength: len(normalBuf), - Target: 34962, // ARRAY_BUFFER int vertex + Target: 34962, // ARRAY_BUFFER int vertex }, // position { - Buffer: 0, + Buffer: 0, ByteOffset: len(indexBuf) + len(normalBuf), ByteLength: len(positionBuf), - Target: 34962, // ARRAY_BUFFER int vertex + Target: 34962, // ARRAY_BUFFER int vertex }, }, Accessors: []Accessor{ { BufferView: 0, ByteOffset: 0, - ComponentType: 5125, // uint ; 5124 int will loss model parts + ComponentType: 5125, // uint ; 5124 int will loss model parts Type: "SCALAR", // index Count: stl.TriangleNum * 3, // 1 index : 1 position }, { BufferView: 1, ByteOffset: 0, - ComponentType: 5126, // float + ComponentType: 5126, // float Type: "VEC3", - Count: stl.TriangleNum * 3, // 1 normal : 1 position - Max: []float32{1, 1, 1}, // or cal from stl data + Count: stl.TriangleNum * 3, // 1 normal : 1 position + Max: []float32{1, 1, 1}, // or cal from stl data Min: []float32{-1, -1, -1}, }, { BufferView: 2, ByteOffset: 0, - ComponentType: 5126, // float + ComponentType: 5126, // float Type: "VEC3", - Count: len(positionBuf) / 12,// xyz = 4x3; or stl.TriangleNum * 3 - Max: stl.Max, // cal from stl data + Count: len(positionBuf) / 12, // xyz = 4x3; or stl.TriangleNum * 3 + Max: stl.Max, // cal from stl data Min: stl.Min, }, }, diff --git a/convert_glb.go b/convert_glb.go new file mode 100644 index 0000000..0fe4c53 --- /dev/null +++ b/convert_glb.go @@ -0,0 +1,155 @@ +package stl2gltf + +import ( + "encoding/binary" + "encoding/json" + "log" + "os" +) + +func ConvertGLB(stl STL, outFile string) { + // buf + indexBuf := make([]byte, 0) + normalBuf := make([]byte, 0) + positionBuf := make([]byte, 0) + + // padding each buffer part + padding := func(buf []byte) []byte { + for len(buf)%4 != 0 { + buf = append(buf, 0x00) + } + return buf + } + + // positions to byte + for i := range stl.TriangleNum { + t := stl.Triangles[i] + positionBuf = append(positionBuf, t.Position...) + } + positionBuf = padding(positionBuf) + + // normals to byte + for i := range stl.TriangleNum { + t := stl.Triangles[i] + // each position, write 3 times + normalBuf = append(normalBuf, t.Normal...) + normalBuf = append(normalBuf, t.Normal...) + normalBuf = append(normalBuf, t.Normal...) + } + normalBuf = padding(normalBuf) + // indices to byte + for i := range stl.TriangleNum * 3 { + buf := make([]byte, 4) + binary.LittleEndian.PutUint32(buf, uint32(i)) + indexBuf = append(indexBuf, buf...) + } + indexBuf = padding(indexBuf) + + log.Println("positions: ", len(positionBuf), " normals: ", len(normalBuf), " indices: ", len(indexBuf)) + + // concat + allData := make([]byte, 0) + allData = append(allData, indexBuf...) // Indices: 0 + allData = append(allData, normalBuf...) // attributes["NORMAL"] = 1 + allData = append(allData, positionBuf...) // attributes["POSITION"] = 2 + + // base := base64.StdEncoding.EncodeToString(allData) + attributes := make(map[string]int, 0) + attributes["NORMAL"] = 1 + attributes["POSITION"] = 2 + + gltf := GLTF{ + Asset: Asset{Version: "2.0"}, + Scenes: []Scene{{Nodes: []int{0}}}, + Nodes: []Node{{Mesh: 0}}, + Meshes: []Mesh{{Primitives: []Primitive{{Attributes: attributes, Indices: 0, Mode: 4}}}}, + // ByteLength not length after base64 + Buffers: []Buffer{{ByteLength: len(allData)}}, + BufferViews: []BufferView{ + // index + { + Buffer: 0, + ByteOffset: 0, + ByteLength: len(indexBuf), + Target: 34963, // ELEMENT_ARRAY_BUFFER float indices + }, + // normal + { + Buffer: 0, + ByteOffset: len(indexBuf), + ByteLength: len(normalBuf), + Target: 34962, // ARRAY_BUFFER int vertex + }, + // position + { + Buffer: 0, + ByteOffset: len(indexBuf) + len(normalBuf), + ByteLength: len(positionBuf), + Target: 34962, // ARRAY_BUFFER int vertex + }, + }, + Accessors: []Accessor{ + { + BufferView: 0, + ByteOffset: 0, + ComponentType: 5125, // uint ; 5124 int will loss model parts + Type: "SCALAR", // index + Count: stl.TriangleNum * 3, // 1 index : 1 position + }, + { + BufferView: 1, + ByteOffset: 0, + ComponentType: 5126, // float + Type: "VEC3", + Count: stl.TriangleNum * 3, // 1 normal : 1 position + Max: []float32{1, 1, 1}, // or cal from stl data + Min: []float32{-1, -1, -1}, + }, + { + BufferView: 2, + ByteOffset: 0, + ComponentType: 5126, // float + Type: "VEC3", + Count: len(positionBuf) / 12, // xyz = 4x3; or stl.TriangleNum * 3 + Max: stl.Max, // cal from stl data + Min: stl.Min, + }, + }, + } + + // 生成glb + outBuf := make([]byte, 0) + + jsonChunkBuf := make([]byte, 0) + + jsonData, err := json.Marshal(gltf) + if err != nil { + log.Println("marshal error: ", err.Error()) + return + } + jsonChunkBuf = binary.LittleEndian.AppendUint32(jsonChunkBuf, uint32(len(jsonData))) + jsonChunkBuf = binary.LittleEndian.AppendUint32(jsonChunkBuf, uint32(0x4E4F534A)) + jsonChunkBuf = append(jsonChunkBuf, jsonData...) + + binChunkBuf := make([]byte, 0) + binChunkBuf = binary.LittleEndian.AppendUint32(binChunkBuf, uint32(len(allData))) + binChunkBuf = binary.LittleEndian.AppendUint32(binChunkBuf, uint32(0x004E4942)) + binChunkBuf = append(binChunkBuf, allData...) + + outBuf = binary.LittleEndian.AppendUint32(outBuf, uint32(0x46546C67)) + outBuf = binary.LittleEndian.AppendUint32(outBuf, uint32(2)) + outBuf = binary.LittleEndian.AppendUint32(outBuf, uint32(12+len(jsonChunkBuf)+len(binChunkBuf))) + + outBuf = append(outBuf, jsonChunkBuf...) + outBuf = append(outBuf, binChunkBuf...) + + // output + os.Remove(outFile) + f, err := os.OpenFile(outFile, os.O_CREATE, os.ModePerm) + if err != nil { + log.Println("write file error: ", err.Error()) + return + } + defer f.Close() + f.Write(outBuf) +} diff --git a/data.go b/data.go index 9bac750..e0d2af4 100644 --- a/data.go +++ b/data.go @@ -1,7 +1,5 @@ package stl2gltf -import "encoding/binary" - // Triangle 50B type Triangle struct { Normal []byte // 4 * 3 12 @@ -11,14 +9,13 @@ type Triangle struct { XYZ []float32 } - // // header num // |----80B------|-4B-|-----triangle1--------|--------triangle2-------|-----------... // Normal(xyz) 4 x 3 position1 position2 position3 attribute // each triangle: |--------12--------|--------12--------|--------12--------|--------12--------|---2-------| -// +// // // //