2024-06-05 01:43:08 +08:00
|
|
|
package stl2gltf
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/base64"
|
|
|
|
"encoding/binary"
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"log"
|
|
|
|
"os"
|
|
|
|
)
|
|
|
|
|
|
|
|
func Convert(stl STL, outFile string) {
|
|
|
|
// buf
|
|
|
|
indexBuf := make([]byte, 0)
|
|
|
|
normalBuf := make([]byte, 0)
|
|
|
|
positionBuf := make([]byte, 0)
|
|
|
|
|
2024-06-06 14:55:35 +08:00
|
|
|
// padding each buffer part
|
2024-06-05 01:43:08 +08:00
|
|
|
padding := func(buf []byte) []byte {
|
|
|
|
log.Println("padding: ", len(buf)%4)
|
|
|
|
for len(buf)%4 != 0 {
|
|
|
|
log.Println("padding")
|
|
|
|
buf = append(buf, 0x00)
|
|
|
|
}
|
|
|
|
return buf
|
|
|
|
}
|
|
|
|
|
2024-06-06 14:55:35 +08:00
|
|
|
// positions to byte
|
2024-06-05 01:43:08 +08:00
|
|
|
for i := range stl.TriangleNum {
|
|
|
|
t := stl.Triangles[i]
|
|
|
|
positionBuf = append(positionBuf, t.Position...)
|
|
|
|
}
|
|
|
|
positionBuf = padding(positionBuf)
|
|
|
|
|
2024-06-06 14:55:35 +08:00
|
|
|
// normals to byte
|
2024-06-05 01:43:08 +08:00
|
|
|
for i := range stl.TriangleNum {
|
|
|
|
t := stl.Triangles[i]
|
2024-06-06 14:55:35 +08:00
|
|
|
// each position, write 3 times
|
2024-06-05 01:43:08 +08:00
|
|
|
normalBuf = append(normalBuf, t.Normal...)
|
|
|
|
normalBuf = append(normalBuf, t.Normal...)
|
|
|
|
normalBuf = append(normalBuf, t.Normal...)
|
|
|
|
}
|
|
|
|
normalBuf = padding(normalBuf)
|
2024-06-06 14:55:35 +08:00
|
|
|
// indices to byte
|
2024-06-05 01:43:08 +08:00
|
|
|
for i := range stl.TriangleNum * 3 {
|
2024-06-06 02:17:06 +08:00
|
|
|
buf := make([]byte, 4)
|
|
|
|
binary.LittleEndian.PutUint32(buf, uint32(i))
|
2024-06-05 01:43:08 +08:00
|
|
|
indexBuf = append(indexBuf, buf...)
|
|
|
|
}
|
|
|
|
indexBuf = padding(indexBuf)
|
|
|
|
|
2024-06-07 00:50:09 +08:00
|
|
|
log.Println("positions: ", len(positionBuf), " normals: ", len(normalBuf), " indices: ", len(indexBuf))
|
2024-06-05 01:43:08 +08:00
|
|
|
|
2024-06-06 14:55:35 +08:00
|
|
|
// concat
|
2024-06-05 01:43:08 +08:00
|
|
|
allData := make([]byte, 0)
|
2024-06-06 14:55:35 +08:00
|
|
|
allData = append(allData, indexBuf...) // Indices: 0
|
|
|
|
allData = append(allData, normalBuf...) // attributes["NORMAL"] = 1
|
|
|
|
allData = append(allData, positionBuf...) // attributes["POSITION"] = 2
|
2024-06-07 00:50:09 +08:00
|
|
|
|
2024-06-05 01:43:08 +08:00
|
|
|
base := base64.StdEncoding.EncodeToString(allData)
|
|
|
|
|
|
|
|
attributes := make(map[string]int, 0)
|
2024-06-06 02:26:04 +08:00
|
|
|
attributes["NORMAL"] = 1
|
2024-06-06 14:55:35 +08:00
|
|
|
attributes["POSITION"] = 2
|
2024-06-05 01:43:08 +08:00
|
|
|
|
|
|
|
gltf := GLTF{
|
2024-06-07 00:50:09 +08:00
|
|
|
Asset: Asset{Version: "2.0"},
|
|
|
|
Scenes: []Scene{{Nodes: []int{0}}},
|
|
|
|
Nodes: []Node{{Mesh: 0}},
|
|
|
|
Meshes: []Mesh{{Primitives: []Primitive{{Attributes: attributes, Indices: 0, Mode: 4}}}},
|
2024-06-06 14:55:35 +08:00
|
|
|
// ByteLength not length after base64
|
2024-06-05 01:43:08 +08:00
|
|
|
Buffers: []Buffer{{Uri: fmt.Sprintf("data:application/gltf-buffer;base64,%s", base), ByteLength: len(allData)}},
|
|
|
|
BufferViews: []BufferView{
|
2024-06-06 02:17:06 +08:00
|
|
|
// index
|
2024-06-05 01:43:08 +08:00
|
|
|
{
|
|
|
|
Buffer: 0,
|
|
|
|
ByteOffset: 0,
|
2024-06-06 02:17:06 +08:00
|
|
|
ByteLength: len(indexBuf),
|
2024-06-06 14:55:35 +08:00
|
|
|
Target: 34963, // ELEMENT_ARRAY_BUFFER float indices
|
2024-06-05 01:43:08 +08:00
|
|
|
},
|
|
|
|
// normal
|
|
|
|
{
|
|
|
|
Buffer: 0,
|
2024-06-06 02:17:06 +08:00
|
|
|
ByteOffset: len(indexBuf),
|
2024-06-06 02:26:04 +08:00
|
|
|
ByteLength: len(normalBuf),
|
2024-06-07 00:50:09 +08:00
|
|
|
Target: 34962, // ARRAY_BUFFER int vertex
|
2024-06-06 02:26:04 +08:00
|
|
|
},
|
|
|
|
// position
|
|
|
|
{
|
2024-06-07 00:50:09 +08:00
|
|
|
Buffer: 0,
|
2024-06-06 02:26:04 +08:00
|
|
|
ByteOffset: len(indexBuf) + len(normalBuf),
|
2024-06-06 02:17:06 +08:00
|
|
|
ByteLength: len(positionBuf),
|
2024-06-07 00:50:09 +08:00
|
|
|
Target: 34962, // ARRAY_BUFFER int vertex
|
2024-06-05 01:43:08 +08:00
|
|
|
},
|
|
|
|
},
|
|
|
|
Accessors: []Accessor{
|
|
|
|
{
|
|
|
|
BufferView: 0,
|
|
|
|
ByteOffset: 0,
|
2024-06-07 00:50:09 +08:00
|
|
|
ComponentType: 5125, // uint ; 5124 int will loss model parts
|
2024-06-06 14:55:35 +08:00
|
|
|
Type: "SCALAR", // index
|
|
|
|
Count: stl.TriangleNum * 3, // 1 index : 1 position
|
2024-06-05 01:43:08 +08:00
|
|
|
},
|
|
|
|
{
|
|
|
|
BufferView: 1,
|
|
|
|
ByteOffset: 0,
|
2024-06-07 00:50:09 +08:00
|
|
|
ComponentType: 5126, // float
|
2024-06-05 01:43:08 +08:00
|
|
|
Type: "VEC3",
|
2024-06-07 00:50:09 +08:00
|
|
|
Count: stl.TriangleNum * 3, // 1 normal : 1 position
|
|
|
|
Max: []float32{1, 1, 1}, // or cal from stl data
|
2024-06-06 02:26:04 +08:00
|
|
|
Min: []float32{-1, -1, -1},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
BufferView: 2,
|
|
|
|
ByteOffset: 0,
|
2024-06-07 00:50:09 +08:00
|
|
|
ComponentType: 5126, // float
|
2024-06-06 02:26:04 +08:00
|
|
|
Type: "VEC3",
|
2024-06-07 00:50:09 +08:00
|
|
|
Count: len(positionBuf) / 12, // xyz = 4x3; or stl.TriangleNum * 3
|
|
|
|
Max: stl.Max, // cal from stl data
|
2024-06-06 02:17:06 +08:00
|
|
|
Min: stl.Min,
|
2024-06-05 01:43:08 +08:00
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
2024-06-06 14:55:35 +08:00
|
|
|
// or not indent
|
2024-06-05 01:43:08 +08:00
|
|
|
data, err := json.MarshalIndent(gltf, " ", " ")
|
|
|
|
if err != nil {
|
|
|
|
log.Println("marshal error: ", err.Error())
|
|
|
|
return
|
|
|
|
}
|
2024-06-06 14:55:35 +08:00
|
|
|
// output
|
2024-06-05 01:43:08 +08:00
|
|
|
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(data)
|
|
|
|
}
|