Take the 2-minute tour ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

I'm building a bounding volume hierarchy in Golang. I can tell my code works because I did some test on it and it gives the same results as the brute force implementation (brute force is to test all possibilities, in \$O(n^2)\$).

My problem is that the code is slower than the brute force implementation (not by that much but enough to make me wonder why I bothered to implement this in the first place). Maybe that's normal, but I would like the opinion of my peers on this.

For those who don't know Golang but would still like to take a look. a chan is a primitive to send/receive messages from other threads (we call thread, goroutine in Go). If I have c <- m that means "send this message via this channel". Both the brute force and the BVH use this primitive so thats probably not the reason for the slowdown.

package tornago

// this line makes sure that *BVHNode is a valid Broadphase
var _ Broadphase = (*BVHNode)(nil)

// BVHNode represents a single node in a bounding volume hierarchy.
type BVHNode struct {
    children [2]*BVHNode
    parent   *BVHNode
    BoundingSphere
    body *RigidBody
}

// NewBVHNode returns a new bvh node. Use this to start a bvh tree.
func NewBVHNode(b *RigidBody, volume *BoundingSphere) BVHNode {
    return BVHNode{
        body:           b,
        BoundingSphere: *volume,
    }
}

// IsLeaf returns true if this node has no children.
func (n *BVHNode) IsLeaf() bool {
    return n.body != nil
}

// GeneratePotentialContacts sends all colliding bounding sphere to the narrow
// phase detector.
func (n *BVHNode) GeneratePotentialContacts(narrowPhaseDetector chan<- PotentialContact) {
    if n.IsLeaf() {
        return
    }
    n.children[0].GeneratePotentialContactsWith(n.children[1], narrowPhaseDetector)
}

// GeneratePotentialContactsWith accepts a second node with a bounding volume to
// test against.
func (n *BVHNode) GeneratePotentialContactsWith(o *BVHNode, narrowPhaseDetector chan<- PotentialContact) {
    //df("inspecting %p,%p\n", n, o)
    // If they don't overlap then we are done.
    if !n.Overlaps(&o.BoundingSphere) {
        return
    }

    // If they're both leaves, then we have a potential contact.
    if n.IsLeaf() && o.IsLeaf() {
        narrowPhaseDetector <- PotentialContact{
        [2]*RigidBody{n.body, o.body},
        }
        return
    }

    // Determine which node to descend into. If either is a leaf, then we
    // descend the other. If both are branches, then we use the one with the
    // largest size.
    if n.IsLeaf() {
        n.GeneratePotentialContactsWith(o.children[0], narrowPhaseDetector)
        n.GeneratePotentialContactsWith(o.children[1], narrowPhaseDetector)
        o.GeneratePotentialContacts(narrowPhaseDetector)
        return
    }

    if o.IsLeaf() {
        o.GeneratePotentialContactsWith(n.children[0], narrowPhaseDetector)
        o.GeneratePotentialContactsWith(n.children[1], narrowPhaseDetector)
        n.GeneratePotentialContacts(narrowPhaseDetector)
        return
    }

    // If they're both branches then descent into the biggest.
    if n.GetSize() < o.GetSize() {
        n.GeneratePotentialContactsWith(o.children[0], narrowPhaseDetector)
        n.GeneratePotentialContactsWith(o.children[1], narrowPhaseDetector)
        n.GeneratePotentialContacts(narrowPhaseDetector)
    } else {
        n.children[0].GeneratePotentialContactsWith(o, narrowPhaseDetector)
        n.children[1].GeneratePotentialContactsWith(o, narrowPhaseDetector)
        o.GeneratePotentialContacts(narrowPhaseDetector)
    }

    // and then like do them separatelly because yknow thats what things.
}

// RecalculateBoundingVolume recalculates the bounding sphere of this node.
func (n *BVHNode) RecalculateBoundingVolume() {
    if n.IsLeaf() {
        return
    }
    n.BoundingSphere = NewBoundingSphereFromSpheres(&n.children[0].BoundingSphere, &n.children[1].BoundingSphere)
    if n.parent != nil {
        n.parent.RecalculateBoundingVolume()
    }
}

// Insert inserts this rigid body in this node of one of it's childs.
func (n *BVHNode) Insert(b *RigidBody, volume *BoundingSphere) {
    if n.IsLeaf() {
        n.children[0] = &BVHNode{
            BoundingSphere: n.BoundingSphere,
            body:           n.body,
            parent:         n,
        }
        n.children[1] = &BVHNode{
            BoundingSphere: *volume,
            body:           b,
            parent:         n,
        }
        n.body = nil
        n.RecalculateBoundingVolume()
        return
    }

    if n.children[0].GetGrowth(volume) < n.children[1].GetGrowth(volume) {
        n.children[0].Insert(b, volume)
    } else {
        n.children[1].Insert(b, volume)
    }
}

Is there something obvious in my implementation that would make this super slow? Am I doing something twice or anything that could add a lot more work to achieve the same result?

share|improve this question

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Browse other questions tagged or ask your own question.