int TesselateSphereTri (tOOF_triangle *pDest, tOOF_triangle *pSrc, int nFaces) { int i; for (i = 0; i < nFaces; i++, pDest += 6, pSrc++) SplitTriangle (pDest, pSrc); return 1; }
void Terrain::ForceSplit(Node* T) { // Recursion Call to force split bases if(T->BaseNeighbour != NULL) { if(T->BaseNeighbour->BaseNeighbour != T) { ForceSplit(T->BaseNeighbour); } SplitTriangle(T); SplitTriangle(T->BaseNeighbour); T->LeftChild->RightNeighbour = T->BaseNeighbour->RightChild; T->RightChild->LeftNeighbour = T->BaseNeighbour->LeftChild; T->BaseNeighbour->LeftChild->RightNeighbour = T->RightChild; T->BaseNeighbour->RightChild->LeftNeighbour = T->LeftChild; } else { SplitTriangle(T); T->LeftChild->RightNeighbour = NULL; T->RightChild->LeftNeighbour = NULL; } }
_Use_decl_annotations_ bool KdTreeCompiler2::FindBestSplit(uint32_t trianglesRoot, uint32_t numTriangles, uint32_t* splitAxis, float* splitValue, _In_ const float min[3], _In_ const float max[3], uint32_t* frontRoot, uint32_t* frontCount, float frontMin[3], float frontMax[3], uint32_t* backRoot, uint32_t* backCount, float backMin[3], float backMax[3]) { if (numTriangles < 10) { return false; } uint32_t axis; float bestSplit = 0.0f; uint32_t bestAxis = 0; int32_t bestScore = INT32_MAX; // Find best split for (axis = 0; axis < 3; ++axis) { int32_t score = 0; float value = (min[axis] + max[axis]) * 0.5f; uint32_t tri = trianglesRoot; while (tri != UINT32_MAX) { KdCompilerTriangle* t = &_compilerTriangles[tri]; score += (t->Centroid[axis] >= value) ? 1 : -1; tri = t->Next; } score = abs(score); if (score < bestScore) { bestScore = score; bestSplit = value; bestAxis = axis; } } if (bestScore == (int32_t)numTriangles) { return false; } *splitAxis = bestAxis; *splitValue = bestSplit; uint32_t front = UINT32_MAX, back = UINT32_MAX; uint32_t nFront = 0, nBack = 0; // Now actually split the triangles uint32_t tri = trianglesRoot; while (tri != UINT32_MAX) { KdCompilerTriangle* t = &_compilerTriangles[tri]; uint32_t nextTri = t->Next; // Remove node t->Next = UINT32_MAX; if (t->Min[bestAxis] > bestSplit) { // insert in front if (front == UINT32_MAX) { front = tri; memcpy_s(frontMin, sizeof(t->Min), t->Min, sizeof(t->Min)); memcpy_s(frontMax, sizeof(t->Max), t->Max, sizeof(t->Max)); } else { t->Next = front; front = tri; for (axis = 0; axis < 3; ++axis) { frontMin[axis] = std::min(frontMin[axis], t->Min[axis]); frontMax[axis] = std::max(frontMax[axis], t->Max[axis]); } } ++nFront; } else if (t->Max[bestAxis] < bestSplit) { // insert in back if (back == UINT32_MAX) { back = tri; memcpy_s(backMin, sizeof(t->Min), t->Min, sizeof(t->Min)); memcpy_s(backMax, sizeof(t->Max), t->Max, sizeof(t->Max)); } else { t->Next = back; back = tri; for (axis = 0; axis < 3; ++axis) { backMin[axis] = std::min(backMin[axis], t->Min[axis]); backMax[axis] = std::max(backMax[axis], t->Max[axis]); } } ++nBack; } else { // TODO: split triangle SplitTriangle(tri, bestAxis, bestSplit, &front, &back); } tri = nextTri; } *frontRoot = front; *frontCount = nFront; *backRoot = back; *backCount = nBack; return true; }
_Use_decl_annotations_ bool BspCompiler::FindBestSplit(Triangle** triangles, uint32_t count, const Triangle** splitTriangle, Triangle** front, uint32_t* frontCount, Triangle** back, uint32_t* backCount) { const Triangle* candidate = nullptr; int32_t score = 0; #if defined(PARALLEL_BSP) if (count > 1000) { // Divide up workload and spin up multiple SplitTriangleTasks uint32_t numGroups = std::min((int)(count / 1000), 16); std::vector<std::shared_ptr<FindBestSplitPacket>> packets(numGroups + 1); std::vector<HANDLE> threads(numGroups + 1); for (uint32_t i = 0; i < packets.size(); ++i) { packets[i].reset(new FindBestSplitPacket); } uint32_t countPerGroup = count / numGroups; uint32_t countDispatched = 0; const Triangle* start = *triangles; for (uint32_t i = 0; i < numGroups; ++i) { auto& packet = packets[i]; packet->start = start; packet->head = *triangles; packet->count = count; packet->numTriangles = countPerGroup; threads[i] = CreateThread(nullptr, 0, FindBestSplitTaskThreadProc, packet.get(), 0, nullptr); for (uint32_t j = 0; j < countPerGroup; ++j) { start = start->Next; } countDispatched += countPerGroup; } if (countDispatched < count) { auto& packet = packets[numGroups]; packet->start = start; packet->head = *triangles; packet->count = count; packet->numTriangles = count - countDispatched; threads[numGroups] = CreateThread(nullptr, 0, FindBestSplitTaskThreadProc, packet.get(), 0, nullptr); ++numGroups; } WaitForMultipleObjects(numGroups, threads.data(), TRUE, INFINITE); for (uint32_t i = 0; i < numGroups; ++i) { CloseHandle(threads[i]); } // aggregate best const Triangle* best = nullptr; int32_t bestScore = 0; for (uint32_t i = 0; i < numGroups; ++i) { if (packets[i]->result) { if (best == nullptr || packets[i]->candidateScore < bestScore) { best = packets[i]->candidateTriangle; bestScore = packets[i]->candidateScore; } } } if (best == nullptr) { return false; } candidate = best; score = bestScore; } else #endif { FindBestSplitPacket packet; packet.head = *triangles; packet.count = count; packet.start = *triangles; packet.numTriangles = count; FindBestSplitTask(&packet); if (!packet.result) { return false; } candidate = packet.candidateTriangle; score = packet.candidateScore; } *splitTriangle = candidate; const XMFLOAT4& plane = candidate->Plane; (*frontCount) = 0; (*backCount) = 0; Triangle* t = *triangles; while (t != nullptr) { Triangle* next = t->Next; if (t == candidate) { // self. insert in front PUSH(front, t); ++(*frontCount); } else { float d0 = DistToPlane(plane, t->v0.Position); float d1 = DistToPlane(plane, t->v1.Position); float d2 = DistToPlane(plane, t->v2.Position); if (ABS(d0) < epsilon && ABS(d1) < epsilon && ABS(d2) < epsilon) { XMVECTOR tplane = XMLoadFloat4(&t->Plane); // coplanar case if (XMVectorGetX(XMVector3Dot(XMLoadFloat4(&plane), tplane)) > 0) { // same facing direction PUSH(front, t); ++(*frontCount); } else { PUSH(back, t); ++(*backCount); } } else if (d0 > -epsilon && d1 > -epsilon && d2 > -epsilon) { // front of plane PUSH(front, t); ++(*frontCount); } else if (d0 < epsilon && d1 < epsilon && d2 < epsilon) { // fully behind plane PUSH(back, t); ++(*backCount); } else { // intersect SplitTriangle(t, plane, front, back); } } t = next; } return true; }
cBSPTree::cBSPTree(std::vector<cTriangle>& tris, std::string name, eBSPHeuristic heuristic) :mName(name) ,mHeuristic(heuristic) ,mBoundingBox(cCoord3(0.0f)) { if(!tris.empty()){ mBoundingBox.Reset(tris.back().GetVertex(0)); } //SplitTriangle(cTriangle(cCoord3(0.0f, 2.0f, 0.0f),cCoord3(0.0f, 0.0f, 0.0f),cCoord3(2.0f, 0.0f, 0.0f),cCoord3(0.0f, 0.0f, 1.0f)), cPlane3(cCoord3::XAxis(), 1.0f)); for (const cTriangle& t: tris) { mTris.push_back(t); mBoundingBox.ExpandToFit(t.GetVertex(0)); mBoundingBox.ExpandToFit(t.GetVertex(1)); mBoundingBox.ExpandToFit(t.GetVertex(2)); } mBoundingBox.ExpandToFit(mBoundingBox.LowerCorner()-0.1f*cCoord3::Ones()); mBoundingBox.ExpandToFit(mBoundingBox.UpperCorner()+0.1f*cCoord3::Ones()); std::vector<cTriNodeIndexPair> in_progress; std::vector<int> index_stack; mNodeVec.reserve(mTris.size()); in_progress.reserve(mTris.size()); index_stack.reserve(static_cast<size_t>(std::log(mTris.size())+1)); std::sort(mTris.begin(), mTris.end(), cTrianglePerimeterComparator()); int count = 0; cCoord3 com; for (const cTriangle& tri: mTris) { com += tri.GetVertex(0); com += tri.GetVertex(1); com += tri.GetVertex(2); in_progress.emplace_back(count, 0); count++; } com /= static_cast<float>(count*3); index_stack.push_back(0); mNodeVec.push_back(cBSPNode(mTris.back(),-1, 0)); in_progress.back().mParentNode--; int total_splits = 0; int max_depth = 0; while (index_stack.size()>0) { int parent_node = index_stack.back(); index_stack.pop_back(); int front_index = static_cast<int>(mNodeVec.size()); int back_index = front_index + 1; float best_front_metric = 0.0f; float best_back_metric = 0.0f; int best_front_index = -1; int best_back_index = -1; int num_front_nodes = 0; int num_back_nodes = 0; int num_split = 0; int num_coplanar = 0; cCoord3 center; float avg_over = 0.0f; cCoord3 orientation; //PreProcessNodes(mTris,in_progress,parent_node); for (int i = 0 ; i < static_cast<int>(in_progress.size()); ++i) { if(in_progress.at(i).mParentNode != parent_node) continue; const int tri_index = in_progress.at(i).mTriIndex; cBSPNode::eClassification in_out = mNodeVec.at(parent_node).ClassifyTriangle(mTris.at(tri_index)); const float area = mTris.at(tri_index).Area(); if(area < 0.0001f) continue; float metric = 0.0; const float normal_dot = mTris.at(tri_index).GetNormal().dot(mNodeVec.at(parent_node).GetNormal()); if(mHeuristic == BSP_COPLANAR){ metric = std::abs(normal_dot); } else if(mHeuristic == BSP_ORTHOGONAL){ metric = normal_dot; } else if(mHeuristic == BSP_AREA){ metric = area* (1 + std::abs(normal_dot)); } //const cCoord3 mean_loc = mTris.at(tri_index).MeanLocation(); //float axis_align = 1.0f - std::abs(mTris.at(tri_index).GetNormal().x); //axis_align = GreaterOf(axis_align, 1.0f - std::abs(mTris.at(tri_index).GetNormal().y)); //axis_align = GreaterOf(axis_align, 1.0f - std::abs(mTris.at(tri_index).GetNormal().z)); //const float dist = -0.0f*(mean_pos/3.0f - center).length(); if(in_out == cBSPNode::BSP_COPLANAR){ if(mTris.at(tri_index).GetNormal().dot(mNodeVec.at(parent_node).GetNormal()) <= 0.0){ in_out = cBSPNode::BSP_BACK; float perim1 = std::sqrt(mTris.at(tri_index).Area()); float normal_length = mNodeVec.at(parent_node).GetNormal().length(); float ratio = mTris.at(tri_index).GetNormal().dot(mNodeVec.at(parent_node).GetNormal()); p4::Log::Warn("coplanar triangles with different normals. area = %f, normal_len = %f, ratio: %f",perim1,normal_length, ratio); mNodeVec.at(parent_node).ClassifyTriangle(mTris.at(tri_index)); //mNodeVec.at(parent_node).AddTriangle(mTris.at(tri_index)); //num_coplanar++; } else{ mNodeVec.at(parent_node).AddTriangle(mTris.at(tri_index)); num_coplanar++; } //p4::Log::Debug("passing over coplanar tri %d", tri_index); } if(in_out == cBSPNode::BSP_FRONT){ num_front_nodes++; //p4::Log::Debug("found front tri"); if( metric > best_front_metric ){ best_front_metric = metric; best_front_index = i; } in_progress.at(i).mParentNode = front_index; } else if(in_out == cBSPNode::BSP_BACK){ num_back_nodes++; //p4::Log::Debug("found back tri"); if( metric > best_back_metric ){ best_back_metric = metric; best_back_index = i; } in_progress.at(i).mParentNode = back_index; } else if(in_out == cBSPNode::BSP_SPLIT){ //p4::Log::Debug("splitting tri %d", tri_index); std::vector<cTriangle> split = SplitTriangle(mTris.at(tri_index), mNodeVec.at(parent_node).GetDividingPlane()); // replace our current triangle mTris.at(tri_index) = split.at(0); // and then add two more if(split.size() > 1){ mTris.emplace_back(split.at(1)); in_progress.emplace_back(static_cast<int>(mTris.size()-1), parent_node); } if(split.size() > 2){ mTris.emplace_back(split.at(2)); in_progress.emplace_back(static_cast<int>(mTris.size()-1), parent_node); } //rewind the index to process this one again i--; num_split ++; } } p4::Log::TMI("Processed for node %d, depth %d, front: %d, back: %d, split: %d, coplanar %d", parent_node, mNodeVec.at(parent_node).GetDepth(), num_front_nodes, num_back_nodes, num_split, num_coplanar); // okay, classified all the triangles: now create 0-2 nodes if(best_front_index >= 0){ const cCoord3 nrml = mTris.at(best_front_index).GetNormal(); mNodeVec.push_back(cBSPNode(mTris.at(best_front_index),parent_node,mNodeVec.at(parent_node).GetDepth()+1)); int new_front_index = static_cast<int>(mNodeVec.size()-1); mNodeVec.at(parent_node).SetFrontNodeIndex(new_front_index); P4_ASSERT(new_front_index == front_index); //TODO:watch out here... in_progress.at(best_front_index).mParentNode = parent_node; index_stack.push_back(new_front_index); p4::Log::TMI("created new front node %d, child of %d, depth %d", new_front_index, parent_node, mNodeVec.at(new_front_index).GetDepth()); P4_ASSERT(mNodeVec.at(parent_node).GetDepth() >= 0); if(mNodeVec.at(new_front_index).GetDepth() > max_depth){ max_depth = mNodeVec.at(new_front_index).GetDepth(); } } if(best_back_index >= 0){ const cCoord3 nrml = mTris.at(best_back_index).GetNormal(); mNodeVec.push_back(cBSPNode(mTris.at(best_back_index),parent_node,mNodeVec.at(parent_node).GetDepth()+1 )); int new_back_index = static_cast<int>(mNodeVec.size()-1); mNodeVec.at(parent_node).SetBackNodeIndex(new_back_index); if(new_back_index != back_index){ for (cTriNodeIndexPair& tri_index: in_progress) { if(tri_index.mParentNode == back_index){ tri_index.mParentNode = new_back_index; } } } in_progress.at(best_back_index).mParentNode = parent_node; index_stack.push_back(new_back_index); p4::Log::TMI("created new back node %d, child of %d, depth %d", new_back_index, parent_node, mNodeVec.at(new_back_index).GetDepth()); P4_ASSERT(mNodeVec.at(parent_node).GetDepth() >= 0); if(mNodeVec.at(new_back_index).GetDepth() > max_depth){ max_depth = mNodeVec.at(new_back_index).GetDepth(); } } total_splits += num_split; } p4::Log::Info("Finished BSP creation: %d nodes (%dkB), max depth = %d, splits = %d", mNodeVec.size(),(mNodeVec.size()*sizeof(cBSPNode)-1)/1024+1, max_depth, total_splits); }