Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'intern/cycles/bvh/bvh_build.cpp')
-rw-r--r--intern/cycles/bvh/bvh_build.cpp545
1 files changed, 545 insertions, 0 deletions
diff --git a/intern/cycles/bvh/bvh_build.cpp b/intern/cycles/bvh/bvh_build.cpp
new file mode 100644
index 00000000000..6a9cc915f01
--- /dev/null
+++ b/intern/cycles/bvh/bvh_build.cpp
@@ -0,0 +1,545 @@
+/*
+ * Adapted from code copyright 2009-2010 NVIDIA Corporation
+ * Modifications Copyright 2011, Blender Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "bvh_build.h"
+#include "bvh_node.h"
+#include "bvh_params.h"
+#include "bvh_sort.h"
+
+#include "mesh.h"
+#include "object.h"
+#include "scene.h"
+
+#include "util_algorithm.h"
+#include "util_foreach.h"
+#include "util_progress.h"
+#include "util_time.h"
+
+CCL_NAMESPACE_BEGIN
+
+/* Constructor / Destructor */
+
+BVHBuild::BVHBuild(const vector<Object*>& objects_,
+ vector<int>& prim_index_, vector<int>& prim_object_,
+ const BVHParams& params_, Progress& progress_)
+: objects(objects_),
+ prim_index(prim_index_),
+ prim_object(prim_object_),
+ params(params_),
+ progress(progress_),
+ progress_start_time(0.0)
+{
+ spatial_min_overlap = 0.0f;
+ progress_num_duplicates = 0;
+}
+
+BVHBuild::~BVHBuild()
+{
+}
+
+/* Adding References */
+
+void BVHBuild::add_reference_mesh(NodeSpec& root, Mesh *mesh, int i)
+{
+ for(uint j = 0; j < mesh->triangles.size(); j++) {
+ Mesh::Triangle t = mesh->triangles[j];
+ Reference ref;
+
+ ref.prim_index = j;
+ ref.prim_object = i;
+
+ for(int k = 0; k < 3; k++) {
+ float3 pt = mesh->verts[t.v[k]];
+ ref.bounds.grow(pt);
+ }
+
+ references.push_back(ref);
+ root.bounds.grow(ref.bounds);
+ }
+}
+
+void BVHBuild::add_reference_object(NodeSpec& root, Object *ob, int i)
+{
+ Reference ref;
+
+ ref.prim_index = -1;
+ ref.prim_object = i;
+ ref.bounds = ob->bounds;
+
+ references.push_back(ref);
+ root.bounds.grow(ref.bounds);
+}
+
+void BVHBuild::add_references(NodeSpec& root)
+{
+ /* init root spec */
+ root.num = 0;
+ root.bounds = BoundBox();
+
+ /* add objects */
+ int i = 0;
+
+ foreach(Object *ob, objects) {
+ if(params.top_level) {
+ if(ob->mesh->transform_applied)
+ add_reference_mesh(root, ob->mesh, i);
+ else
+ add_reference_object(root, ob, i);
+ }
+ else
+ add_reference_mesh(root, ob->mesh, i);
+
+ i++;
+
+ if(progress.get_cancel()) return;
+ }
+
+ /* happens mostly on empty meshes */
+ if(!root.bounds.valid())
+ root.bounds.grow(make_float3(0.0f, 0.0f, 0.0f));
+
+ root.num = references.size();
+}
+
+/* Build */
+
+BVHNode* BVHBuild::run()
+{
+ NodeSpec root;
+
+ /* add references */
+ add_references(root);
+
+ if(progress.get_cancel()) return NULL;
+
+ /* init spatial splits */
+ if(params.top_level) /* todo: get rid of this */
+ params.use_spatial_split = false;
+
+ spatial_min_overlap = root.bounds.area() * params.spatial_split_alpha;
+ spatial_right_bounds.clear();
+ spatial_right_bounds.resize(max(root.num, (int)BVHParams::NUM_SPATIAL_BINS) - 1);
+
+ /* init progress updates */
+ progress_num_duplicates = 0;
+ progress_start_time = time_dt();
+
+ /* build recursively */
+ return build_node(root, 0, 0.0f, 1.0f);
+}
+
+void BVHBuild::progress_update(float progress_start, float progress_end)
+{
+ if(time_dt() - progress_start_time < 0.25f)
+ return;
+
+ float duplicates = (float)progress_num_duplicates/(float)references.size();
+ string msg = string_printf("Building BVH %.0f%%, duplicates %.0f%%",
+ progress_start*100.0f, duplicates*100.0f);
+
+ progress.set_substatus(msg);
+ progress_start_time = time_dt();
+}
+
+BVHNode* BVHBuild::build_node(const NodeSpec& spec, int level, float progress_start, float progress_end)
+{
+ /* progress update */
+ progress_update(progress_start, progress_end);
+ if(progress.get_cancel()) return NULL;
+
+ /* small enough or too deep => create leaf. */
+ if(spec.num <= params.min_leaf_size || level >= BVHParams::MAX_DEPTH)
+ return create_leaf_node(spec);
+
+ /* find split candidates. */
+ float area = spec.bounds.area();
+ float leafSAH = area * params.triangle_cost(spec.num);
+ float nodeSAH = area * params.node_cost(2);
+ ObjectSplit object = find_object_split(spec, nodeSAH);
+ SpatialSplit spatial;
+
+ if(params.use_spatial_split && level < BVHParams::MAX_SPATIAL_DEPTH) {
+ BoundBox overlap = object.left_bounds;
+ overlap.intersect(object.right_bounds);
+
+ if(overlap.area() >= spatial_min_overlap)
+ spatial = find_spatial_split(spec, nodeSAH);
+ }
+
+ /* leaf SAH is the lowest => create leaf. */
+ float minSAH = min(min(leafSAH, object.sah), spatial.sah);
+
+ if(minSAH == leafSAH && spec.num <= params.max_leaf_size)
+ return create_leaf_node(spec);
+
+ /* perform split. */
+ NodeSpec left, right;
+
+ if(params.use_spatial_split && minSAH == spatial.sah)
+ do_spatial_split(left, right, spec, spatial);
+ if(!left.num || !right.num)
+ do_object_split(left, right, spec, object);
+
+ /* create inner node. */
+ progress_num_duplicates += left.num + right.num - spec.num;
+
+ float progress_mid = lerp(progress_start, progress_end, (float)right.num / (float)(left.num + right.num));
+
+ BVHNode* rightNode = build_node(right, level + 1, progress_start, progress_mid);
+ if(progress.get_cancel()) {
+ if(rightNode) rightNode->deleteSubtree();
+ return NULL;
+ }
+
+ BVHNode* leftNode = build_node(left, level + 1, progress_mid, progress_end);
+ if(progress.get_cancel()) {
+ if(leftNode) leftNode->deleteSubtree();
+ return NULL;
+ }
+
+ return new InnerNode(spec.bounds, leftNode, rightNode);
+}
+
+BVHNode *BVHBuild::create_object_leaf_nodes(const Reference *ref, int num)
+{
+ if(num == 0) {
+ BoundBox bounds;
+ return new LeafNode(bounds, 0, 0);
+ }
+ else if(num == 1) {
+ prim_index.push_back(ref[0].prim_index);
+ prim_object.push_back(ref[0].prim_object);
+ return new LeafNode(ref[0].bounds, prim_index.size()-1, prim_index.size());
+ }
+ else {
+ int mid = num/2;
+ BVHNode *leaf0 = create_object_leaf_nodes(ref, mid);
+ BVHNode *leaf1 = create_object_leaf_nodes(ref+mid, num-mid);
+
+ BoundBox bounds;
+ bounds.grow(leaf0->m_bounds);
+ bounds.grow(leaf1->m_bounds);
+
+ return new InnerNode(bounds, leaf0, leaf1);
+ }
+}
+
+BVHNode* BVHBuild::create_leaf_node(const NodeSpec& spec)
+{
+ vector<int>& p_index = prim_index;
+ vector<int>& p_object = prim_object;
+ BoundBox bounds;
+ int num = 0;
+
+ for(int i = 0; i < spec.num; i++) {
+ if(references.back().prim_index != -1) {
+ p_index.push_back(references.back().prim_index);
+ p_object.push_back(references.back().prim_object);
+ bounds.grow(references.back().bounds);
+ references.pop_back();
+ num++;
+ }
+ }
+
+ BVHNode *leaf = NULL;
+
+ if(num > 0) {
+ leaf = new LeafNode(bounds, p_index.size() - num, p_index.size());
+
+ if(num == spec.num)
+ return leaf;
+ }
+
+ /* while there may be multiple triangles in a leaf, for object primitives
+ * we want them to be the only one, so we */
+ int ob_num = spec.num - num;
+ BVHNode *oleaf = create_object_leaf_nodes(&references.back() - (ob_num - 1), ob_num);
+ for(int i = 0; i < ob_num; i++)
+ references.pop_back();
+
+ if(leaf)
+ return new InnerNode(spec.bounds, leaf, oleaf);
+ else
+ return oleaf;
+}
+
+/* Object Split */
+
+BVHBuild::ObjectSplit BVHBuild::find_object_split(const NodeSpec& spec, float nodeSAH)
+{
+ ObjectSplit split;
+ const Reference *ref_ptr = &references[references.size() - spec.num];
+
+ for(int dim = 0; dim < 3; dim++) {
+ /* sort references */
+ bvh_reference_sort(references.size() - spec.num, references.size(), &references[0], dim);
+
+ /* sweep right to left and determine bounds. */
+ BoundBox right_bounds;
+
+ for(int i = spec.num - 1; i > 0; i--) {
+ right_bounds.grow(ref_ptr[i].bounds);
+ spatial_right_bounds[i - 1] = right_bounds;
+ }
+
+ /* sweep left to right and select lowest SAH. */
+ BoundBox left_bounds;
+
+ for(int i = 1; i < spec.num; i++) {
+ left_bounds.grow(ref_ptr[i - 1].bounds);
+ right_bounds = spatial_right_bounds[i - 1];
+
+ float sah = nodeSAH +
+ left_bounds.area() * params.triangle_cost(i) +
+ right_bounds.area() * params.triangle_cost(spec.num - i);
+
+ if(sah < split.sah) {
+ split.sah = sah;
+ split.dim = dim;
+ split.num_left = i;
+ split.left_bounds = left_bounds;
+ split.right_bounds = right_bounds;
+ }
+ }
+ }
+
+ return split;
+}
+
+void BVHBuild::do_object_split(NodeSpec& left, NodeSpec& right, const NodeSpec& spec, const ObjectSplit& split)
+{
+ /* sort references according to split */
+ int start = references.size() - spec.num;
+ int end = references.size(); /* todo: is this right? */
+
+ bvh_reference_sort(start, end, &references[0], split.dim);
+
+ /* split node specs */
+ left.num = split.num_left;
+ left.bounds = split.left_bounds;
+ right.num = spec.num - split.num_left;
+ right.bounds = split.right_bounds;
+}
+
+/* Spatial Split */
+
+BVHBuild::SpatialSplit BVHBuild::find_spatial_split(const NodeSpec& spec, float nodeSAH)
+{
+ /* initialize bins. */
+ float3 origin = spec.bounds.min;
+ float3 binSize = (spec.bounds.max - origin) * (1.0f / (float)BVHParams::NUM_SPATIAL_BINS);
+ float3 invBinSize = 1.0f / binSize;
+
+ for(int dim = 0; dim < 3; dim++) {
+ for(int i = 0; i < BVHParams::NUM_SPATIAL_BINS; i++) {
+ SpatialBin& bin = spatial_bins[dim][i];
+
+ bin.bounds = BoundBox();
+ bin.enter = 0;
+ bin.exit = 0;
+ }
+ }
+
+ /* chop references into bins. */
+ for(unsigned int refIdx = references.size() - spec.num; refIdx < references.size(); refIdx++) {
+ const Reference& ref = references[refIdx];
+ float3 firstBinf = (ref.bounds.min - origin) * invBinSize;
+ float3 lastBinf = (ref.bounds.max - origin) * invBinSize;
+ int3 firstBin = make_int3(firstBinf.x, firstBinf.y, firstBinf.z);
+ int3 lastBin = make_int3(lastBinf.x, lastBinf.y, lastBinf.z);
+
+ firstBin = clamp(firstBin, 0, BVHParams::NUM_SPATIAL_BINS - 1);
+ lastBin = clamp(lastBin, firstBin, BVHParams::NUM_SPATIAL_BINS - 1);
+
+ for(int dim = 0; dim < 3; dim++) {
+ Reference currRef = ref;
+
+ for(int i = firstBin[dim]; i < lastBin[dim]; i++) {
+ Reference leftRef, rightRef;
+
+ split_reference(leftRef, rightRef, currRef, dim, origin[dim] + binSize[dim] * (float)(i + 1));
+ spatial_bins[dim][i].bounds.grow(leftRef.bounds);
+ currRef = rightRef;
+ }
+
+ spatial_bins[dim][lastBin[dim]].bounds.grow(currRef.bounds);
+ spatial_bins[dim][firstBin[dim]].enter++;
+ spatial_bins[dim][lastBin[dim]].exit++;
+ }
+ }
+
+ /* select best split plane. */
+ SpatialSplit split;
+
+ for(int dim = 0; dim < 3; dim++) {
+ /* sweep right to left and determine bounds. */
+ BoundBox right_bounds;
+
+ for(int i = BVHParams::NUM_SPATIAL_BINS - 1; i > 0; i--) {
+ right_bounds.grow(spatial_bins[dim][i].bounds);
+ spatial_right_bounds[i - 1] = right_bounds;
+ }
+
+ /* sweep left to right and select lowest SAH. */
+ BoundBox left_bounds;
+ int leftNum = 0;
+ int rightNum = spec.num;
+
+ for(int i = 1; i < BVHParams::NUM_SPATIAL_BINS; i++) {
+ left_bounds.grow(spatial_bins[dim][i - 1].bounds);
+ leftNum += spatial_bins[dim][i - 1].enter;
+ rightNum -= spatial_bins[dim][i - 1].exit;
+
+ float sah = nodeSAH +
+ left_bounds.area() * params.triangle_cost(leftNum) +
+ spatial_right_bounds[i - 1].area() * params.triangle_cost(rightNum);
+
+ if(sah < split.sah) {
+ split.sah = sah;
+ split.dim = dim;
+ split.pos = origin[dim] + binSize[dim] * (float)i;
+ }
+ }
+ }
+
+ return split;
+}
+
+void BVHBuild::do_spatial_split(NodeSpec& left, NodeSpec& right, const NodeSpec& spec, const SpatialSplit& split)
+{
+ /* Categorize references and compute bounds.
+ *
+ * Left-hand side: [left_start, left_end[
+ * Uncategorized/split: [left_end, right_start[
+ * Right-hand side: [right_start, refs.size()[ */
+
+ vector<Reference>& refs = references;
+ int left_start = refs.size() - spec.num;
+ int left_end = left_start;
+ int right_start = refs.size();
+
+ left.bounds = right.bounds = BoundBox();
+
+ for(int i = left_end; i < right_start; i++) {
+ if(refs[i].bounds.max[split.dim] <= split.pos) {
+ /* entirely on the left-hand side */
+ left.bounds.grow(refs[i].bounds);
+ swap(refs[i], refs[left_end++]);
+ }
+ else if(refs[i].bounds.min[split.dim] >= split.pos) {
+ /* entirely on the right-hand side */
+ right.bounds.grow(refs[i].bounds);
+ swap(refs[i--], refs[--right_start]);
+ }
+ }
+
+ /* duplicate or unsplit references intersecting both sides. */
+ while(left_end < right_start) {
+ /* split reference. */
+ Reference lref, rref;
+
+ split_reference(lref, rref, refs[left_end], split.dim, split.pos);
+
+ /* compute SAH for duplicate/unsplit candidates. */
+ BoundBox lub = left.bounds; // Unsplit to left: new left-hand bounds.
+ BoundBox rub = right.bounds; // Unsplit to right: new right-hand bounds.
+ BoundBox ldb = left.bounds; // Duplicate: new left-hand bounds.
+ BoundBox rdb = right.bounds; // Duplicate: new right-hand bounds.
+
+ lub.grow(refs[left_end].bounds);
+ rub.grow(refs[left_end].bounds);
+ ldb.grow(lref.bounds);
+ rdb.grow(rref.bounds);
+
+ float lac = params.triangle_cost(left_end - left_start);
+ float rac = params.triangle_cost(refs.size() - right_start);
+ float lbc = params.triangle_cost(left_end - left_start + 1);
+ float rbc = params.triangle_cost(refs.size() - right_start + 1);
+
+ float unsplitLeftSAH = lub.area() * lbc + right.bounds.area() * rac;
+ float unsplitRightSAH = left.bounds.area() * lac + rub.area() * rbc;
+ float duplicateSAH = ldb.area() * lbc + rdb.area() * rbc;
+ float minSAH = min(min(unsplitLeftSAH, unsplitRightSAH), duplicateSAH);
+
+ if(minSAH == unsplitLeftSAH) {
+ /* unsplit to left */
+ left.bounds = lub;
+ left_end++;
+ }
+ else if(minSAH == unsplitRightSAH) {
+ /* unsplit to right */
+ right.bounds = rub;
+ swap(refs[left_end], refs[--right_start]);
+ }
+ else {
+ /* duplicate */
+ left.bounds = ldb;
+ right.bounds = rdb;
+ refs[left_end++] = lref;
+ refs.push_back(rref);
+ }
+ }
+
+ left.num = left_end - left_start;
+ right.num = refs.size() - right_start;
+}
+
+void BVHBuild::split_reference(Reference& left, Reference& right, const Reference& ref, int dim, float pos)
+{
+ /* initialize references. */
+ left.prim_index = right.prim_index = ref.prim_index;
+ left.prim_object = right.prim_object = ref.prim_object;
+ left.bounds = right.bounds = BoundBox();
+
+ /* loop over vertices/edges. */
+ Object *ob = objects[ref.prim_object];
+ const Mesh *mesh = ob->mesh;
+ const int *inds = mesh->triangles[ref.prim_index].v;
+ const float3 *verts = &mesh->verts[0];
+ const float3* v1 = &verts[inds[2]];
+
+ for(int i = 0; i < 3; i++) {
+ const float3* v0 = v1;
+ int vindex = inds[i];
+ v1 = &verts[vindex];
+ float v0p = (*v0)[dim];
+ float v1p = (*v1)[dim];
+
+ /* insert vertex to the boxes it belongs to. */
+ if(v0p <= pos)
+ left.bounds.grow(*v0);
+
+ if(v0p >= pos)
+ right.bounds.grow(*v0);
+
+ /* edge intersects the plane => insert intersection to both boxes. */
+ if((v0p < pos && v1p > pos) || (v0p > pos && v1p < pos)) {
+ float3 t = lerp(*v0, *v1, clamp((pos - v0p) / (v1p - v0p), 0.0f, 1.0f));
+ left.bounds.grow(t);
+ right.bounds.grow(t);
+ }
+ }
+
+ /* intersect with original bounds. */
+ left.bounds.max[dim] = pos;
+ right.bounds.min[dim] = pos;
+ left.bounds.intersect(ref.bounds);
+ right.bounds.intersect(ref.bounds);
+}
+
+CCL_NAMESPACE_END
+