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)
}