diff --git a/cmd/stl2gltf.go b/cmd/stl2gltf.go index bff2ceb..87a1c09 100644 --- a/cmd/stl2gltf.go +++ b/cmd/stl2gltf.go @@ -7,7 +7,14 @@ import ( func main() { stl := stl2gltf.Load("D:\\src\\go\\stl2gltf\\example\\0c448109-7ac9-4f6d-aaf6-ce6bb744032f.stl") // log.Println(stl) - stl2gltf.Convert(stl, "C:\\Users\\13040\\Downloads\\0c448109-7ac9-4f6d-aaf6-ce6bb744032f.gltf") + // stl = stl2gltf.Load("D:\\src\\go\\stl2gltf\\example\\1ec91891-c25a-4905-a79c-f3bc96045d37.stl") + // // log.Println(stl) + // stl2gltf.Convert(stl, "C:\\Users\\13040\\Downloads\\1ec91891-c25a-4905-a79c-f3bc96045d37.gltf") + + // stl = stl2gltf.Load("D:\\src\\go\\stl2gltf\\example\\3ea057e5-1520-4898-a59c-f33a6c955bd7.stl") + // // log.Println(stl) + // stl2gltf.Convert(stl, "C:\\Users\\13040\\Downloads\\3ea057e5-1520-4898-a59c-f33a6c955bd7.gltf") + } diff --git a/convert.go b/convert.go index 01aac17..235c981 100644 --- a/convert.go +++ b/convert.go @@ -41,8 +41,8 @@ func Convert(stl STL, outFile string) { normalBuf = padding(normalBuf) // 写入索引 for i := range stl.TriangleNum * 3 { - buf := make([]byte, 2) - binary.LittleEndian.PutUint16(buf, uint16(i)) + buf := make([]byte, 4) + binary.LittleEndian.PutUint32(buf, uint32(i)) indexBuf = append(indexBuf, buf...) } indexBuf = padding(indexBuf) @@ -50,70 +50,72 @@ func Convert(stl STL, outFile string) { log.Println(len(positionBuf), len(normalBuf), len(indexBuf)) allData := make([]byte, 0) - allData = append(allData, positionBuf...) - allData = append(allData, normalBuf...) allData = append(allData, indexBuf...) + // allData = append(allData, normalBuf...) + allData = append(allData, positionBuf...) base := base64.StdEncoding.EncodeToString(allData) attributes := make(map[string]int, 0) - attributes["POSITION"] = 0 - attributes["NORMAL"] = 1 + attributes["POSITION"] = 1 + // attributes["NORMAL"] = 1 gltf := GLTF{ Asset: Asset{Version: "2.0"}, Scenes: []Scene{{Nodes: []int{0}}}, Nodes: []Node{{Mesh: 0}}, - Meshes: []Mesh{{Primitives: []Primitive{{Attributes: attributes, Indices: 2, Mode: 4}}}}, + Meshes: []Mesh{{Primitives: []Primitive{{Attributes: attributes, Indices: 0, Mode: 4}}}}, Buffers: []Buffer{{Uri: fmt.Sprintf("data:application/gltf-buffer;base64,%s", base), ByteLength: len(allData)}}, BufferViews: []BufferView{ - // position - { - Buffer: 0, - ByteOffset: 0, - ByteLength: len(positionBuf), - Target: 34962, - }, - // normal - { - Buffer: 0, - ByteOffset: len(positionBuf), - ByteLength: len(normalBuf), - Target: 34962, - }, // index { Buffer: 0, - ByteOffset: len(positionBuf) + len(normalBuf), + ByteOffset: 0, ByteLength: len(indexBuf), Target: 34963, }, + // normal + // { + // Buffer: 0, + // ByteOffset: len(indexBuf), + // ByteLength: len(normalBuf), + // Target: 34962, + // }, + // position + { + Buffer: 0, + ByteOffset: len(indexBuf), + // ByteOffset: len(indexBuf) + len(normalBuf), + ByteLength: len(positionBuf), + Target: 34962, + }, }, Accessors: []Accessor{ { BufferView: 0, ByteOffset: 0, - ComponentType: 5126, - Type: "VEC3", - Count: len(positionBuf) / 12, - Max: stl.Max, - Min: stl.Min, + ComponentType: 5125, + Type: "SCALAR", + Count: stl.TriangleNum * 3, }, + + // { + // BufferView: 1, + // ByteOffset: 0, + // ComponentType: 5126, + // Type: "VEC3", + // Count: int(stl.TriangleNum), + // // Max: []float32{1, 1, 1}, + // // Min: []float32{-1, -1, -1}, + // }, { BufferView: 1, ByteOffset: 0, ComponentType: 5126, Type: "VEC3", - Count: int(stl.TriangleNum), - // Max: []float32{1, 1, 1}, - // Min: []float32{-1, -1, -1}, - }, - { - BufferView: 2, - ByteOffset: 0, - ComponentType: 5123, - Type: "SCALAR", - Count: stl.TriangleNum, + Count: len(positionBuf) / 12, + Max: stl.Max, + Min: stl.Min, }, }, } diff --git a/convert2.go b/convert2.go new file mode 100644 index 0000000..7306056 --- /dev/null +++ b/convert2.go @@ -0,0 +1,167 @@ +package stl2gltf + +import ( + "encoding/base64" + "encoding/binary" + "encoding/json" + "fmt" + "log" + "os" +) + +func Convert2(stl STL2, outFile string) { + // buf + indexBuf := make([]byte, 0) + normalBuf := make([]byte, 0) + positionBuf := make([]byte, 0) + + 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 + } + + uniquePositions := make([][3]uint32, 0) + indices := make([]int16, len(stl.Positions)) + for i := 0; i < len(stl.Positions); i++ { + position := stl.Positions[i] + // if in uniqt + found := false + for u, ut := range uniquePositions { + if ut[0] == position[0] && ut[1] == position[1] && ut[2] == position[2] { + // exist + indices[i] = int16(u) + found = true + } + } + if !found { + uniquePositions = append(uniquePositions, position) + indices[i] = int16(len(uniquePositions) - 1) + } + } + + // 写入顶点 + for _, ip := range uniquePositions { + buf := make([]byte, 0) + buf = binary.LittleEndian.AppendUint32(buf, uint32(ip[0])) + buf = binary.LittleEndian.AppendUint32(buf, uint32(ip[1])) + buf = binary.LittleEndian.AppendUint32(buf, uint32(ip[2])) + positionBuf = append(positionBuf, buf...) + } + positionBuf = padding(positionBuf) + + // 写入法向量 + // for i := range stl.TriangleNum { + // t := stl.Triangles[i] + // normalBuf = append(normalBuf, t.Normal...) + // normalBuf = append(normalBuf, t.Normal...) + // normalBuf = append(normalBuf, t.Normal...) + // } + normalBuf = padding(normalBuf) + // 写入索引 + for i := range indices { + buf := make([]byte, 2) + binary.LittleEndian.PutUint16(buf, uint16(i)) + indexBuf = append(indexBuf, buf...) + } + indexBuf = padding(indexBuf) + + log.Println(len(positionBuf), len(normalBuf), len(indexBuf)) + + allData := make([]byte, 0) + allData = append(allData, positionBuf...) + allData = append(allData, indexBuf...) + // allData = append(allData, normalBuf...) + + base := base64.StdEncoding.EncodeToString(allData) + + attributes := make(map[string]int, 0) + attributes["POSITION"] = 0 + // attributes["NORMAL"] = 1 + + gltf := GLTF{ + Asset: Asset{Version: "2.0"}, + Scenes: []Scene{{Nodes: []int{0}}}, + Nodes: []Node{{Mesh: 0}}, + Meshes: []Mesh{{Primitives: []Primitive{{Attributes: attributes, Indices: 1, Mode: 4}}}}, + Buffers: []Buffer{{Uri: fmt.Sprintf("data:application/gltf-buffer;base64,%s", base), ByteLength: len(allData)}}, + BufferViews: []BufferView{ + // index + + // normal + // { + // Buffer: 0, + // ByteOffset: len(indexBuf), + // ByteLength: len(normalBuf), + // Target: 34962, + // }, + // position + { + Buffer: 0, + ByteOffset: 0, + // ByteOffset: len(indexBuf) + len(normalBuf), + ByteLength: len(positionBuf), + Target: 34962, + }, + { + Buffer: 0, + ByteOffset: len(positionBuf), + ByteLength: len(indexBuf), + Target: 34963, + }, + }, + Accessors: []Accessor{ + + // { + // BufferView: 1, + // ByteOffset: 0, + // ComponentType: 5126, + // Type: "VEC3", + // Count: int(stl.TriangleNum), + // // Max: []float32{1, 1, 1}, + // // Min: []float32{-1, -1, -1}, + // }, + { + BufferView: 0, + ByteOffset: 0, + ComponentType: 5126, + Type: "VEC3", + Count: len(positionBuf) / 12, + Max: f2i(stl.Max), + Min: f2i(stl.Min), + }, + { + BufferView: 1, + ByteOffset: 0, + ComponentType: 5123, + Type: "SCALAR", + Count: stl.TriangleNum, + }, + }, + } + + data, err := json.MarshalIndent(gltf, " ", " ") + if err != nil { + log.Println("marshal error: ", err.Error()) + return + } + 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) +} + +func f2i(p []uint32) []float32 { + a := make([]float32, len(p)) + for i := 0; i < len(p); i++ { + a[i] = float32(p[i]) + } + return a +} diff --git a/data.go b/data.go index 5ee0fae..5bee5a0 100644 --- a/data.go +++ b/data.go @@ -1,10 +1,26 @@ package stl2gltf +import "encoding/binary" + // Triangle 50B type Triangle struct { Normal []byte // 4 * 3 12 Position []byte // 4 * 3 * 3 36 Attribute []byte // 2 02 + + XYZ []float32 +} + +func (t Triangle) EuqalsPosition(position []byte) bool { + x1 := binary.LittleEndian.Uint32(position[:4]) + y1 := binary.LittleEndian.Uint32(position[4:8]) + z1 := binary.LittleEndian.Uint32(position[8:12]) + + x2 := binary.LittleEndian.Uint32(t.Position[:4]) + y2 := binary.LittleEndian.Uint32(t.Position[4:8]) + z2 := binary.LittleEndian.Uint32(t.Position[8:12]) + + return x1 == x2 && y1 == y2 && z1 == z2 } type STL struct { @@ -16,6 +32,23 @@ type STL struct { Min []float32 } +type Triangle2 struct { + Normal []byte // 4 * 3 12 + Position [][3]float32 // 4 * 3 * 3 36 + Attribute []byte // 2 02 + + XYZ []float32 +} + +type STL2 struct { + Header string + TriangleNum int + Positions [][3]uint32 + + Max []uint32 + Min []uint32 +} + type Asset struct { Version string `json:"version"` } diff --git a/example/1ec91891-c25a-4905-a79c-f3bc96045d37.stl b/example/1ec91891-c25a-4905-a79c-f3bc96045d37.stl new file mode 100644 index 0000000..2644822 Binary files /dev/null and b/example/1ec91891-c25a-4905-a79c-f3bc96045d37.stl differ diff --git a/example/3ea057e5-1520-4898-a59c-f33a6c955bd7.stl b/example/3ea057e5-1520-4898-a59c-f33a6c955bd7.stl new file mode 100644 index 0000000..e70c4a4 Binary files /dev/null and b/example/3ea057e5-1520-4898-a59c-f33a6c955bd7.stl differ diff --git a/load.go b/load.go index ab1aa1f..ff6e84f 100644 --- a/load.go +++ b/load.go @@ -70,3 +70,71 @@ func Load(filePath string) (stl STL) { return stl } + +func Load2(filePath string) (stl STL2) { + file, err := os.OpenFile(filePath, os.O_RDONLY, os.ModePerm) + if err != nil { + log.Panic("open file " + filePath + " error: " + err.Error()) + } + headerBuf := make([]byte, 80) + file.Read(headerBuf) + triangleNumBuf := make([]byte, 4) + file.Read(triangleNumBuf) + + stl.Header = string(headerBuf) + stl.TriangleNum = int(binary.LittleEndian.Uint32(triangleNumBuf)) + + stl.Positions = make([][3]uint32, 0) + + max := []uint32{0, 0, 0} + min := []uint32{0, 0, 0} + for range stl.TriangleNum { + normalBuf := make([]byte, 4*3) + file.Read(normalBuf) + + positionBuf := make([]byte, 4*3*3) + file.Read(positionBuf) + x := uint32(0) + y := uint32(0) + z := uint32(0) + for r := range 3 { + x1 := math.Float32frombits(binary.LittleEndian.Uint32(positionBuf[:4+(r*4)])) + y1 := math.Float32frombits(binary.LittleEndian.Uint32(positionBuf[(r * 4) : (r*4)+4])) + z1 := math.Float32frombits(binary.LittleEndian.Uint32(positionBuf[(r*4)+4 : (r*4)+8])) + + // x = uint32(x1) * 10000 / 10000 + // y = uint32(y1) * 10000 / 10000 + // z = uint32(z1) * 10000 / 10000 + x = uint32(x1) * 1 / 1 + y = uint32(y1) * 1 / 1 + z = uint32(z1) * 1 / 1 + + if max[0] < x { + max[0] = x + } + if max[1] < y { + max[1] = y + } + if max[2] < z { + max[2] = z + } + if min[0] > x { + min[0] = x + } + if min[1] > y { + min[1] = y + } + if min[2] > z { + min[2] = z + } + stl.Positions = append(stl.Positions, [3]uint32{x, y, z}) + } + attributeBuf := make([]byte, 2) + file.Read(attributeBuf) + } + + stl.Max = max + stl.Min = min + + return stl +}