Example #1
0
///  A mesh must have an array of positions and connectivity.  It may also have per-vertex normals
///  If per-vertex normals are omitted, then per-face normals are used instead.
///
///   The mesh does NOT delete the input arrays
///
/// \param rPositions
///    Array of vertex positions
/// \param pFaceNormals
///    Array of face normals.  Must be same length as nTriangleCount
/// \param nTriangleCount
///     Number of triangles
/// \param pIndices
///     An array of 3*nTriangleCount vertex indices
JRTMesh* JRTMesh::CreateMesh(const Vec3f* pPositions,
                             const Vec3f* pNormals,
                             UINT nVertices,
                             UINT nTriangleCount,
                             const UINT* pIndices)
{
    JRTMesh* pMesh = new JRTMesh();

    pMesh->m_nVertexCount = nVertices;
    pMesh->m_nTriangleCount = nTriangleCount;

    // copy vertex positions
    pMesh->m_Positions.assign (pPositions, pPositions + nVertices);

    // copy normals
    pMesh->m_FaceNormals.assign (pNormals, pNormals + nTriangleCount);

    // create triangles
    pMesh->m_Triangles.resize (nTriangleCount);

    for (UINT i = 0; i < nTriangleCount; i++)
    {
        pMesh->m_Triangles[i].m_pMesh = pMesh;

        JRT_ASSERT(pIndices[0] < nVertices);
        JRT_ASSERT(pIndices[1] < nVertices);
        JRT_ASSERT(pIndices[2] < nVertices);

        pMesh->m_Triangles[i].m_pV1 = reinterpret_cast<const float*> (&pMesh->m_Positions[ pIndices[0] ]);
        pMesh->m_Triangles[i].m_pV2 = reinterpret_cast<const float*> (&pMesh->m_Positions[ pIndices[1] ]);
        pMesh->m_Triangles[i].m_pV3 = reinterpret_cast<const float*> (&pMesh->m_Positions[ pIndices[2] ]);

        pIndices += 3;
    }

    return pMesh;

}
Example #2
0
JRTKDTree* JRTKDTreeBuilder::BuildTree(const std::vector<JRTMesh*>& rMeshes)
{
    JRTKDTree* pTree = NULL;
    std::vector<JRTKDNode> nodes;
    std::vector<UINT> indices;
    std::vector<const JRTTriangle*> triArray;
    JRTBoundingBox scene_bounds(Vec3f(FLT_MAX), Vec3f(-FLT_MAX));

    // build an array over all of the triangles
    for (UINT i = 0; i < rMeshes.size();  i++)
    {
        const JRTTriangle* pTri = rMeshes[i]->GetTriangles();

        for (UINT j = 0; j < rMeshes[i]->GetTriangleCount(); j++)
        {
            triArray.push_back(pTri);
            pTri++;
        }
    }

    JRT_ASSERT(triArray.size() > 0);

    // compute scene bounding box
    for (UINT i = 0; i < triArray.size(); i++)
    {
        // this will cause the fourth component of each point
        // passed to Expand() to be junk.  This is ok

        scene_bounds.Expand(triArray[i]->GetV1());
        scene_bounds.Expand(triArray[i]->GetV2());
        scene_bounds.Expand(triArray[i]->GetV3());
    }

    // slight hack:  Expand bounding box a little bit.  This fixes some precision issues with flat polygons on the edge of the model
    scene_bounds.GetMin() += Vec3f(-0.001f, -0.001f, -0.001f);
    scene_bounds.GetMax() += Vec3f(0.001f, 0.001f, 0.001f);

    // construct the tree
    BuildTreeImpl(scene_bounds, triArray, nodes, indices);

    pTree = new JRTKDTree;
    pTree->m_pIndexArray = new UINT[ indices.size() ];
    pTree->m_pMailboxes = new UINT[ triArray.size() ];
    pTree->m_bBackFacing = new bool [ triArray.size() ];

    // initialize the tree structure
    pTree->m_pNodeArray = (JRTKDNode*)_aligned_malloc(sizeof(JRTKDNode) * nodes.size(), 16);
    pTree->m_pTriArray = (JRTCoreTriangle*)_aligned_malloc(sizeof(JRTCoreTriangle) * triArray.size(), 16);

    if (!pTree->m_pNodeArray || !pTree->m_pTriArray)
    {
        JRT_SAFE_DELETE(pTree);
        return NULL;
    }

    pTree->m_nNodeCount = (UINT)nodes.size();
    pTree->m_nTriangleCount = (UINT)triArray.size();
    pTree->m_nIndexCount = (UINT) indices.size();
    pTree->m_treeBounds = scene_bounds;

    // copy node array
    for (UINT i = 0; i < nodes.size(); i++)
    {
        pTree->m_pNodeArray[i] = nodes[i];
    }

    // create index array
    for (UINT i = 0; i < indices.size(); i++)
    {
        pTree->m_pIndexArray[i] = indices[i];
    }

    // create mailbox array
    memset(pTree->m_pMailboxes, 0, sizeof(UINT)*triArray.size());
    pTree->m_nNextRayID = 1;

    // preprocess triangles and copy to array
    for (UINT i = 0; i < triArray.size(); i++)
    {
        PreprocessTri(triArray[i]->GetV1(), triArray[i]->GetV2(), triArray[i]->GetV3(), &pTree->m_pTriArray[i]);
        pTree->m_pTriArray[i].pMesh = triArray[i]->GetMesh();
        pTree->m_pTriArray[i].nTriIndex = triArray[i]->GetIndexInMesh();
    }

    // initialize array of flags telling whether or not a triangle is backfacing (TOOTLE SPECIFIC)
    for (UINT i = 0; i < triArray.size(); i++)
    {
        pTree->m_bBackFacing[i] = false;
    }

    return pTree;
}
void JRTHeuristicKDTreeBuilder::BuildTreeRecursive(UINT nDepthLimit,
                                                   const JRTBoundingBox& rNodeBounds,
                                                   SplitVec splits[3],
                                                   std::vector<TriangleBB*>& rBBsThisNode,
                                                   JRTKDNode* pNode,
                                                   std::vector<JRTKDNode>& rNodesOut,
                                                   std::vector<UINT>& rTrisOut)
{
    UINT nTriCount = (UINT)rBBsThisNode.size();

    JRT_ASSERT(splits[0].size() == splits[1].size() && splits[1].size() == splits[2].size());

    // initialize best cost to cost of not splitting
    float fBestCost = INTERSECT_COST * nTriCount;

    // find the optimal split
    bool bSplit = false;
    float fSplitValue;
    UINT eSplitAxis = X_AXIS;


    float fNodeBBInvArea = 1.0f / Max(0.0000001f, rNodeBounds.GetSurfaceArea());
    float fNodeVolume = rNodeBounds.GetVolume();

    for (UINT axis = X_AXIS; axis <= Z_AXIS; axis++)
    {
        LocateBestSplit(fNodeBBInvArea, rNodeBounds, splits, axis, nTriCount, fBestCost, bSplit, eSplitAxis, fSplitValue);
    }


    if (!bSplit || nDepthLimit == 0)
    {
        // we either don't want to split, or can't split, so make a leaf
        pNode->leaf.is_leaf = true;
        pNode->leaf.triangle_count = 0;
        pNode->leaf.triangle_start = (UINT)rTrisOut.size();

        for (UINT i = 0; i < rBBsThisNode.size(); i++)
        {
            // do robust tri-box clipping at the leaves
            const Vec3f* pV1 = &rBBsThisNode[i]->pTri->GetV1();
            const Vec3f* pV2 = &rBBsThisNode[i]->pTri->GetV2();
            const Vec3f* pV3 = &rBBsThisNode[i]->pTri->GetV3();

            if (rNodeBounds.TriangleIntersect(pV1, pV2, pV3))
            {
                rTrisOut.push_back(rBBsThisNode[i]->nIndex);
                pNode->leaf.triangle_count++;
            }
        }
    }
    else
    {
        // make a non-leaf

        pNode->inner.axis = eSplitAxis;
        pNode->inner.is_leaf = false;
        pNode->inner.position = fSplitValue;

        JRT_ASSERT(fSplitValue > rNodeBounds.GetMin()[eSplitAxis] &&
                   fSplitValue < rNodeBounds.GetMax()[eSplitAxis]);

        ClassifyBBs(splits[0], m_bbs, eSplitAxis, fSplitValue);

        // partition the splits
        SplitVec frontSplits[3];
        SplitVec backSplits[3];

        for (int j = X_AXIS; j <= Z_AXIS; j++)
        {
            PartitionSplits(fSplitValue, eSplitAxis, splits[j], m_bbs, backSplits[j], frontSplits[j]);

            // clear old split vec to save memory
            splits[j].clear();
        }

        // partition BBs
        std::vector<TriangleBB*> backBBs;
        std::vector<TriangleBB*> frontBBs;
        PartitionBBs(eSplitAxis, fSplitValue, rBBsThisNode, backBBs, frontBBs);

        rBBsThisNode.clear(); // save memory

        // create new nodes
        // by convention, always create front child right before back child
        pNode->inner.front_offset = (UINT)rNodesOut.size();
        UINT nFront = (UINT)rNodesOut.size();
        UINT nBack = nFront + 1;
        rNodesOut.push_back(JRTKDNode());
        rNodesOut.push_back(JRTKDNode());

        // split the node bounding box
        JRTBoundingBox front_bounds, back_bounds;
        rNodeBounds.Split(eSplitAxis, fSplitValue, front_bounds, back_bounds);

        // recursively build the subtrees
        BuildTreeRecursive(nDepthLimit - 1, front_bounds, frontSplits, frontBBs,  &rNodesOut[ nFront ], rNodesOut, rTrisOut);
        BuildTreeRecursive(nDepthLimit - 1, back_bounds,  backSplits,  backBBs, &rNodesOut[ nBack ], rNodesOut, rTrisOut);
    }
}
void JRTHeuristicKDTreeBuilder::LocateBestSplit(float fNodeBBInvArea, const JRTBoundingBox& rNodeBounds, const SplitVec splits[3], UINT axis, UINT nTriCount, float& fBestCost, bool& bSplit, UINT& eSplitAxis, float& fSplitValue)
{
    JRTBoundingBox frontBounds = rNodeBounds, backBounds = rNodeBounds;

    // std::ofstream foo("foo.csv");

    const SplitVec& rSplitVec = splits[axis];
    float fNodeMin = rNodeBounds.GetMin()[axis];
    float fNodeMax = rNodeBounds.GetMax()[axis];
    UINT nSplits = (UINT)rSplitVec.size();

    if (nSplits == 0)
    {
        bSplit = false;
        eSplitAxis = X_AXIS;
        return;
    }

    // find the best split along the current axis
    // to do this, we sweep the bounding box planes in left-to-right order
    // - for each 'opening' plane, we compute the cost, and then increment the # of triangles
    //     on the 'behind' side of the plane (since the triangle will be behind all subsequent planes)
    //  For each 'closing' plane, we decrement the number of tris in front before computing the cost
    UINT nTrisBehind = 0;
    UINT nTrisInFront = nTriCount;
    UINT i = 0;

    Split* split = rSplitVec[i];

    // advance forwards past any splits which are before the bounding box start
    i = 0;

    while (i < nSplits && rSplitVec[i]->value <= fNodeMin)
    {
        if (rSplitVec[i]->bMaxSplit)
        {
            nTrisInFront--;
        }
        else
        {
            nTrisBehind++;
        }

        i++;
    }

    // figure out which split to stop at, start at the end and go backwards
    while (nSplits > 0 && rSplitVec[nSplits - 1]->value >= fNodeMax)
    {
        nSplits--;
    }

    // precompute some stuff that we'll use to compute surface areas
    Vec3f bbSize = rNodeBounds.GetMax() - rNodeBounds.GetMin();

    float bbSizeU = UCOMP(bbSize, axis);
    float bbSizeV = VCOMP(bbSize, axis);

    float sa_const = bbSizeU * bbSizeV;

    // surface area of root bounding box.  we omit the multiplies by two since they cancel out
    float farea = bbSizeU * bbSizeV + bbSize[axis] * bbSizeU + bbSize[axis] * bbSizeV;

    // pre-compute the divide
    if (farea == 0.0f)
    {
        farea = 0.00000001f;
    }

    farea = 1.0f / farea;

    bool bHaveSplit = false;

    // iterate over all of the splits that lie inside the node bounding box
    for (i = i; i < nSplits; i++)
    {
        Split* split = rSplitVec[i];

        nTrisInFront -= (split->bMaxSplit) ? 1 : 0;
        nTrisBehind += (split->bMaxSplit) ? 0 : 1;

        // evaluate the cost of this split using the surface area heuristic

        //backBounds.GetMax()[axis] = split->value;
        //frontBounds.GetMin()[axis] = split->value;
        //float back_area = backBounds.GetSurfaceArea();
        //float front_area = frontBounds.GetSurfaceArea();

        // compute surface area for each side of the box.  Note that we deliberately leave
        // off the multiply by two since it cancels out
        float back_area = sa_const + (bbSizeU + bbSizeV) * (split->value - fNodeMin);
        float front_area = sa_const + (bbSizeU + bbSizeV) * (fNodeMax - split->value);
        float cost = 1.0f + INTERSECT_COST * farea * ((back_area * nTrisBehind) + (front_area * nTrisInFront));

        //foo << cost <<","<<split->value << "," << nTrisInFront<< "," << nTrisBehind << std::endl;

        if (cost < fBestCost)
        {
            fBestCost = cost;
            fSplitValue = split->value;
            bHaveSplit = true;
            JRT_ASSERT(fSplitValue >= rNodeBounds.GetMin()[axis] &&
                       fSplitValue <= rNodeBounds.GetMax()[axis]);
        }

    }

    if (bHaveSplit)
    {
        bSplit = true;
        eSplitAxis = (Axis)axis;
    }

    //foo.close();
}