func NewKdTreeBuilder(mesh *TriangleMesh, buildParams BuildParams) *KdTreeBuilder { // max count is chosen such that maxTrianglesCount * 2 is still // an int32, this simplifies implementation. const maxTrianglesCount = 0x3fffffff // max ~ 1 billion triangles if mesh.GetTrianglesCount() > maxTrianglesCount { common.RuntimeError(fmt.Sprintf( "exceeded the maximum number of mesh triangles: %d", maxTrianglesCount)) } if buildParams.MaxDepth <= 0 { trianglesCountLog := math.Floor(math.Log2(float64(mesh.GetTrianglesCount()))) buildParams.MaxDepth = int(math.Floor(0.5 + 8.0 + 1.3*trianglesCountLog)) } if buildParams.MaxDepth > maxTraversalDepth { buildParams.MaxDepth = maxTraversalDepth } builder := &KdTreeBuilder{ mesh: mesh, buildParams: buildParams, } if buildParams.CollectStats { builder.buildStats.enabled = true } return builder }
func LoadTriangleMesh(fileName string) *TriangleMesh { const ( headerSize = 80 facetSize = 50 maxVerticesCount = math.MaxInt32 maxTrianglesCount = math.MaxInt32 ) file, err := os.Open(fileName) common.Check(err) defer file.Close() // get file size stat, err := file.Stat() common.Check(err) fileSize := stat.Size() // read file content fileContent := make([]byte, fileSize) bytesRead, err := file.Read(fileContent) common.Check(err) if int64(bytesRead) != fileSize { common.RuntimeError(fmt.Sprintf("failed to read %d bytes from file %s", fileSize, fileName)) } // validate file content asciiStlHeader := []byte{0x73, 0x6f, 0x6c, 0x69, 0x64} if bytes.Equal(fileContent[0:5], asciiStlHeader) { common.RuntimeError("ascii stl files are not supported: " + fileName) } if fileSize < headerSize+4 { common.RuntimeError("invalid binary stl file: " + fileName) } buffer := bytes.NewBuffer(fileContent[headerSize:]) var trianglesCount int32 err = binary.Read(buffer, binary.LittleEndian, &trianglesCount) common.Check(err) if trianglesCount > maxTrianglesCount { common.RuntimeError("triangles limit exceeded: " + fileName) } expectedSize := int64(headerSize + 4 + trianglesCount*facetSize) if fileSize != expectedSize { common.RuntimeError("invalid size of binary stl file: " + fileName) } // read mesh data mesh := new(TriangleMesh) mesh.normals = make([]Vector32, trianglesCount) mesh.triangles = make([][3]int32, trianglesCount) uniqueVertices := make(map[Vector32]int32) for i := 0; i < int(trianglesCount); i++ { binary.Read(buffer, binary.LittleEndian, &mesh.normals[i]) for k := 0; k < 3; k++ { var v Vector32 binary.Read(buffer, binary.LittleEndian, &v) vertexIndex, found := uniqueVertices[v] if !found { if len(mesh.vertices) > maxVerticesCount { common.RuntimeError("vertices limit exceeded: " + fileName) } vertexIndex = int32(len(mesh.vertices)) uniqueVertices[v] = vertexIndex mesh.vertices = append(mesh.vertices, v) } mesh.triangles[i][k] = vertexIndex } var attribsCount uint16 binary.Read(buffer, binary.LittleEndian, &attribsCount) } return mesh }
func (builder *KdTreeBuilder) buildNode(nodeBounds BBox32, nodeTriangles []int32, depth int, offset0 int, offset1 int) { if len(builder.nodes) >= maxNodesCount { common.RuntimeError(fmt.Sprintf( "maximum number of KdTree nodes has been reached: %d", maxNodesCount)) } // check if leaf node should be created if len(nodeTriangles) <= builder.buildParams.LeafTrianglesLimit || depth == 0 { builder.createLeaf(nodeTriangles) builder.buildStats.newLeaf(len(nodeTriangles), builder.buildParams.MaxDepth-depth) return } // select split position split := builder.selectSplit(nodeBounds, nodeTriangles) if split.edge == -1 { builder.createLeaf(nodeTriangles) builder.buildStats.newLeaf(len(nodeTriangles), builder.buildParams.MaxDepth-depth) return } splitPosition := builder.edgesBuffer[split.edge].positionOnAxis // classify triangles with respect to split n0 := 0 for i := int32(0); i < split.edge; i++ { if builder.edgesBuffer[i].isStart() { builder.trianglesBuffer[offset0+n0] = builder.edgesBuffer[i].triangleIndex() n0++ } } n1 := 0 for i := split.edge + 1; i < int32(2*len(nodeTriangles)); i++ { if builder.edgesBuffer[i].isEnd() { builder.trianglesBuffer[offset1+n1] = builder.edgesBuffer[i].triangleIndex() n1++ } } // add interior node and recursively create children nodes thisNodeIndex := len(builder.nodes) builder.nodes = append(builder.nodes, node{}) bounds0 := nodeBounds bounds0.maxPoint[split.axis] = splitPosition builder.buildNode(bounds0, builder.trianglesBuffer[0:n0], depth-1, 0, offset1+n1) aboveChild := int32(len(builder.nodes)) builder.nodes[thisNodeIndex].initInteriorNode(split.axis, aboveChild, splitPosition) bounds1 := nodeBounds bounds1.minPoint[split.axis] = splitPosition builder.buildNode(bounds1, builder.trianglesBuffer[offset1:offset1+n1], depth-1, 0, offset1) }