From 76b3843c354b7830b637de47024c090acc3ab6b1 Mon Sep 17 00:00:00 2001 From: Valtteri Koskivuori Date: Wed, 17 Jun 2020 22:43:18 +0300 Subject: [PATCH] Implement a top-level BVH (#77) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Start work on an experimental top-level BVH + traversal. It's disabled for now, switch the implementations in getClosestIsect() Still some issues to work out, but it's something. I don't really like the meshIndex hack I had to add to the polygon struct, but I gues it's not that bad since I managed to shrink it anyway. * Maybe MSVC dislikes compound literals, let's try getting rid of those here. * Make bvh construction truly generic to limit code duplication (#78) * Make bvh construction truly generic * Spaces -> tabs * Update .xcodeproj to reflect changes in pull request 78 * Hide `struct bvhNode` and `struct bvh` and present them as opaque structs in the API instead. * Continue implementing generic BVH traversal * Rename rayIntersectsWithGenericBvh -> rayIntersectsWithBvhGeneric Fix incorrect function call in rayIntersectsWithBvh() * Rename `rayIntersectsWith*` -> `traverse*` * Fix naming Add inline qualifiers to a few functions. * Fix top-level BVH implementation (#79) * Fix top-level BVH implementation * Minor performance improvements to the BVH traversal * Remove redundant assignment * Move address offset inside indirect access Co-authored-by: Arsène Pérard-Gayot --- C-Ray.xcodeproj/project.pbxproj | 18 +-- src/accelerators/bbox.c | 99 --------------- src/accelerators/bbox.h | 39 ------ src/accelerators/bvh.c | 204 +++++++++++++++++++----------- src/accelerators/bvh.h | 27 ++-- src/accelerators/kdtree.c | 212 -------------------------------- src/accelerators/kdtree.h | 44 ------- src/datatypes/bbox.h | 36 ++++++ src/datatypes/mesh.c | 5 - src/datatypes/mesh.h | 7 +- src/datatypes/poly.h | 5 +- src/datatypes/scene.c | 29 ++--- src/datatypes/scene.h | 2 + src/includes.h | 7 ++ src/renderer/pathtrace.c | 24 ++-- src/utils/converter.c | 4 +- src/utils/converter.h | 2 +- src/utils/loaders/sceneloader.c | 4 +- 18 files changed, 220 insertions(+), 548 deletions(-) delete mode 100644 src/accelerators/bbox.c delete mode 100644 src/accelerators/bbox.h delete mode 100644 src/accelerators/kdtree.c delete mode 100644 src/accelerators/kdtree.h create mode 100644 src/datatypes/bbox.h diff --git a/C-Ray.xcodeproj/project.pbxproj b/C-Ray.xcodeproj/project.pbxproj index 9be70959..67406ec6 100644 --- a/C-Ray.xcodeproj/project.pbxproj +++ b/C-Ray.xcodeproj/project.pbxproj @@ -7,8 +7,6 @@ objects = { /* Begin PBXBuildFile section */ - 900BA12B220B4603005B8EE7 /* bbox.c in Sources */ = {isa = PBXBuildFile; fileRef = 900BA0F4220B4602005B8EE7 /* bbox.c */; }; - 900BA12C220B4603005B8EE7 /* kdtree.c in Sources */ = {isa = PBXBuildFile; fileRef = 900BA0F5220B4602005B8EE7 /* kdtree.c */; }; 900BA12D220B4603005B8EE7 /* pathtrace.c in Sources */ = {isa = PBXBuildFile; fileRef = 900BA0F7220B4602005B8EE7 /* pathtrace.c */; }; 900BA12E220B4603005B8EE7 /* renderer.c in Sources */ = {isa = PBXBuildFile; fileRef = 900BA0FA220B4602005B8EE7 /* renderer.c */; }; 900BA12F220B4603005B8EE7 /* poly.c in Sources */ = {isa = PBXBuildFile; fileRef = 900BA0FC220B4602005B8EE7 /* poly.c */; }; @@ -49,7 +47,6 @@ 905842EC236651FC009D92F1 /* objloader.c in Sources */ = {isa = PBXBuildFile; fileRef = 90CA85192252C90C00BA7702 /* objloader.c */; }; 905842EE236651FC009D92F1 /* pcg_basic.c in Sources */ = {isa = PBXBuildFile; fileRef = 90369CCA222E012F008D215B /* pcg_basic.c */; }; 905842EF236651FC009D92F1 /* gitsha1.c in Sources */ = {isa = PBXBuildFile; fileRef = 90FB15C822596E79008D6AAA /* gitsha1.c */; }; - 905842F1236651FC009D92F1 /* kdtree.c in Sources */ = {isa = PBXBuildFile; fileRef = 900BA0F5220B4602005B8EE7 /* kdtree.c */; }; 905842F2236651FC009D92F1 /* converter.c in Sources */ = {isa = PBXBuildFile; fileRef = 900BA126220B4602005B8EE7 /* converter.c */; }; 905842F3236651FC009D92F1 /* mtlloader.c in Sources */ = {isa = PBXBuildFile; fileRef = 90CA85182252C90C00BA7702 /* mtlloader.c */; }; 905842F4236651FC009D92F1 /* tile.c in Sources */ = {isa = PBXBuildFile; fileRef = 900BA107220B4602005B8EE7 /* tile.c */; }; @@ -62,7 +59,6 @@ 905842FB236651FC009D92F1 /* filehandler.c in Sources */ = {isa = PBXBuildFile; fileRef = 900BA11E220B4602005B8EE7 /* filehandler.c */; }; 905842FC236651FC009D92F1 /* transforms.c in Sources */ = {isa = PBXBuildFile; fileRef = 900BA106220B4602005B8EE7 /* transforms.c */; }; 905842FD236651FC009D92F1 /* ui.c in Sources */ = {isa = PBXBuildFile; fileRef = 900BA125220B4602005B8EE7 /* ui.c */; }; - 905842FE236651FC009D92F1 /* bbox.c in Sources */ = {isa = PBXBuildFile; fileRef = 900BA0F4220B4602005B8EE7 /* bbox.c */; }; 905842FF236651FC009D92F1 /* sceneloader.c in Sources */ = {isa = PBXBuildFile; fileRef = 90CA85202252CB3800BA7702 /* sceneloader.c */; }; 9062FA09243BBD0900420889 /* args.c in Sources */ = {isa = PBXBuildFile; fileRef = 9062FA08243BBD0900420889 /* args.c */; }; 9062FA0A243BBD0900420889 /* args.c in Sources */ = {isa = PBXBuildFile; fileRef = 9062FA08243BBD0900420889 /* args.c */; }; @@ -146,10 +142,6 @@ /* Begin PBXFileReference section */ 900BA0F0220B4602005B8EE7 /* main.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = main.h; sourceTree = ""; }; - 900BA0F2220B4602005B8EE7 /* bbox.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = bbox.h; sourceTree = ""; }; - 900BA0F3220B4602005B8EE7 /* kdtree.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = kdtree.h; sourceTree = ""; }; - 900BA0F4220B4602005B8EE7 /* bbox.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = bbox.c; sourceTree = ""; }; - 900BA0F5220B4602005B8EE7 /* kdtree.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = kdtree.c; sourceTree = ""; }; 900BA0F7220B4602005B8EE7 /* pathtrace.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pathtrace.c; sourceTree = ""; }; 900BA0F8220B4602005B8EE7 /* renderer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = renderer.h; sourceTree = ""; }; 900BA0F9220B4602005B8EE7 /* pathtrace.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = pathtrace.h; sourceTree = ""; }; @@ -214,6 +206,7 @@ 9062FA19243D23A400420889 /* encoder.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = encoder.c; sourceTree = ""; }; 906479BE24982155003772CE /* sky.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = sky.c; sourceTree = ""; }; 906479BF24982155003772CE /* sky.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = sky.h; sourceTree = ""; }; + 906479C224996499003772CE /* bbox.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = bbox.h; sourceTree = ""; }; 9076FB4B243002A40003B327 /* terminal.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = terminal.h; sourceTree = ""; }; 9076FB4C243002A40003B327 /* terminal.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = terminal.c; sourceTree = ""; }; 9076FB4F243002B00003B327 /* mutex.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = mutex.h; sourceTree = ""; }; @@ -319,10 +312,6 @@ 900BA0F1220B4602005B8EE7 /* accelerators */ = { isa = PBXGroup; children = ( - 900BA0F2220B4602005B8EE7 /* bbox.h */, - 900BA0F4220B4602005B8EE7 /* bbox.c */, - 900BA0F3220B4602005B8EE7 /* kdtree.h */, - 900BA0F5220B4602005B8EE7 /* kdtree.c */, 904CDBB7248D74380092E564 /* bvh.c */, 904CDBB6248D74380092E564 /* bvh.h */, ); @@ -347,6 +336,7 @@ isa = PBXGroup; children = ( 90C5D5512448CC8400C58643 /* image */, + 906479C224996499003772CE /* bbox.h */, 900BA108220B4602005B8EE7 /* poly.h */, 900BA0FC220B4602005B8EE7 /* poly.c */, 900BA0FD220B4602005B8EE7 /* tile.h */, @@ -648,7 +638,6 @@ 909D136E24439069006D0A86 /* string.c in Sources */, 905842EE236651FC009D92F1 /* pcg_basic.c in Sources */, 905842EF236651FC009D92F1 /* gitsha1.c in Sources */, - 905842F1236651FC009D92F1 /* kdtree.c in Sources */, 905842F2236651FC009D92F1 /* converter.c in Sources */, 905842F3236651FC009D92F1 /* mtlloader.c in Sources */, 905842F4236651FC009D92F1 /* tile.c in Sources */, @@ -670,7 +659,6 @@ 905842FB236651FC009D92F1 /* filehandler.c in Sources */, 905842FC236651FC009D92F1 /* transforms.c in Sources */, 905842FD236651FC009D92F1 /* ui.c in Sources */, - 905842FE236651FC009D92F1 /* bbox.c in Sources */, 9062FA13243D236600420889 /* png.c in Sources */, 905842FF236651FC009D92F1 /* sceneloader.c in Sources */, 9076FB4E243002A40003B327 /* terminal.c in Sources */, @@ -712,7 +700,6 @@ 909D136D24439069006D0A86 /* string.c in Sources */, 90369CCC222E012F008D215B /* pcg_basic.c in Sources */, 90FB15CB22596E79008D6AAA /* gitsha1.c in Sources */, - 900BA12C220B4603005B8EE7 /* kdtree.c in Sources */, 900BA142220B4603005B8EE7 /* converter.c in Sources */, 90CA851C2252C90C00BA7702 /* mtlloader.c in Sources */, 900BA134220B4603005B8EE7 /* tile.c in Sources */, @@ -734,7 +721,6 @@ 900BA13F220B4603005B8EE7 /* filehandler.c in Sources */, 900BA133220B4603005B8EE7 /* transforms.c in Sources */, 900BA141220B4603005B8EE7 /* ui.c in Sources */, - 900BA12B220B4603005B8EE7 /* bbox.c in Sources */, 9062FA12243D236600420889 /* png.c in Sources */, 90CA85212252CB3800BA7702 /* sceneloader.c in Sources */, 9076FB4D243002A40003B327 /* terminal.c in Sources */, diff --git a/src/accelerators/bbox.c b/src/accelerators/bbox.c deleted file mode 100644 index eac1200a..00000000 --- a/src/accelerators/bbox.c +++ /dev/null @@ -1,99 +0,0 @@ -// -// bbox.c -// C-ray -// -// Created by Valtteri Koskivuori on 28/04/2017. -// Copyright © 2015-2020 Valtteri Koskivuori. All rights reserved. -// - -#include "../includes.h" -#include "bbox.h" - -#include "../datatypes/vertexbuffer.h" -#include "../datatypes/poly.h" -#include "../utils/assert.h" - -/** - Get the longest axis of an axis-aligned bounding box - - @param bbox Bounding box to compute longest axis for - @return Longest axis as an enum - */ -enum bboxAxis getLongestAxis(const struct boundingBox *bbox) { - float x = fabsf(bbox->start.x - bbox->end.x); - float y = fabsf(bbox->start.y - bbox->end.y); - float z = fabsf(bbox->start.z - bbox->end.z); - - return x > y && x > z ? X : y > z ? Y : Z; -} - -/// Compute the surface area of a given bounding box -/// @param box Bounding box to compute surface area for -float findSurfaceArea(const struct boundingBox *box) { - float width = box->end.x - box->start.x; - float height = box->end.y - box->start.y; - float length = box->end.z - box->start.z; - return 2 * (length * width) + 2 * (length * height) + 2 * (width * height); -} - -/** - Compute the bounding box for a given array of polygons - - @param polys Indices to polygons to compute bounding box for - @param count Amount of polygons indices given - @return Axis-aligned bounding box - */ -struct boundingBox *computeBoundingBox(const int *polys, const int count) { - ASSERT(polys); - ASSERT(count > 0); - struct boundingBox *bbox = calloc(1, sizeof(*bbox)); - struct vector minPoint = vecWithPos(FLT_MAX, FLT_MAX, FLT_MAX); - struct vector maxPoint = vecWithPos(-FLT_MAX, -FLT_MAX, -FLT_MAX); - - for (int i = 0; i < count; ++i) { - for (int j = 0; j < 3; ++j) { - minPoint = vecMin(minPoint, vertexArray[polygonArray[polys[i]].vertexIndex[j]]); - maxPoint = vecMax(maxPoint, vertexArray[polygonArray[polys[i]].vertexIndex[j]]); - } - } - struct vector center = vecWithPos(0.5f * (minPoint.x + maxPoint.x), 0.5f * (minPoint.y + maxPoint.y), 0.5f * (minPoint.z + maxPoint.z)); - float maxDistance = 0.0f; - for (int i = 0; i < count; ++i) { - for (int j = 0; j < 3; ++j) { - struct vector fromCenter = vecSub(vertexArray[polygonArray[polys[i]].vertexIndex[j]], center); - maxDistance = max(maxDistance, pow(vecLength(fromCenter), 2)); - } - } - bbox->start = minPoint; - bbox->end = maxPoint; - bbox->midPoint = center; - bbox->surfaceArea = findSurfaceArea(bbox); - return bbox; -} - -bool rayIntersectsWithAABB(const struct boundingBox *box, const struct lightRay *ray) { - //If a mesh has no polygons, it won't have a root bbox either. - if (!box) return false; - - struct vector dirfrac = vecWithPos(1.0f / ray->direction.x, 1.0f / ray->direction.y, 1.0f / ray->direction.z); - - float t1 = (box->start.x - ray->start.x)*dirfrac.x; - float t2 = (box-> end.x - ray->start.x)*dirfrac.x; - float t3 = (box->start.y - ray->start.y)*dirfrac.y; - float t4 = (box-> end.y - ray->start.y)*dirfrac.y; - float t5 = (box->start.z - ray->start.z)*dirfrac.z; - float t6 = (box-> end.z - ray->start.z)*dirfrac.z; - - float tmin = max(max(min(t1, t2), min(t3, t4)), min(t5, t6)); - float tmax = min(min(max(t1, t2), max(t3, t4)), max(t5, t6)); - - // if tmax < 0, ray is intersecting AABB, but the whole AABB is behind us - if (tmax < 0) { - return false; - } - // if tmin > tmax, ray doesn't intersect AABB - if (tmin > tmax) { - return false; - } - return true; -} diff --git a/src/accelerators/bbox.h b/src/accelerators/bbox.h deleted file mode 100644 index 3f638bf8..00000000 --- a/src/accelerators/bbox.h +++ /dev/null @@ -1,39 +0,0 @@ -// -// bbox.h -// C-ray -// -// Created by Valtteri Koskivuori on 28/04/2017. -// Copyright © 2015-2020 Valtteri Koskivuori. All rights reserved. -// - -#pragma once - -#include "../datatypes/vector.h" -#include "../datatypes/lightRay.h" - -/// Current bbox split axis -enum bboxAxis { - X, - Y, - Z -}; - -/// Bounding box for a given set of primitives -struct boundingBox { - struct vector start, end, midPoint; - float surfaceArea; -}; - -/// Computes a bounding box for a given array of polygons -/// @param polys Array of polygons to process -/// @param count Amount of polygons given -struct boundingBox *computeBoundingBox(const int *polys, const int count); - -/// Compute the longest axis of a given bounding box -/// @param bbox Bounding box to process -enum bboxAxis getLongestAxis(const struct boundingBox *bbox); - -/// Checks for an intersection between a given ray and bounding box -/// @param box Bounding box to check intersection against -/// @param ray Ray to intersect -bool rayIntersectsWithAABB(const struct boundingBox *box, const struct lightRay *ray); diff --git a/src/accelerators/bvh.c b/src/accelerators/bvh.c index d2af8bcd..5938378a 100644 --- a/src/accelerators/bvh.c +++ b/src/accelerators/bvh.c @@ -14,7 +14,9 @@ #include "../datatypes/vertexbuffer.h" #include "../datatypes/poly.h" #include "../datatypes/vector.h" +#include "../datatypes/bbox.h" #include "../datatypes/lightRay.h" +#include "../datatypes/mesh.h" /* * This BVH builder is based on "On fast Construction of SAH-based Bounding Volume Hierarchies", @@ -30,20 +32,27 @@ #define TRAVERSAL_COST 1.5f // Ratio (cost of traversing a node / cost of intersecting a primitive) #define BIN_COUNT 32 // Number of bins to use to approximate the SAH -// TODO: Move this to its own file if need be -// NOTE: The existing `boundingBox` type is too big -typedef struct bBox { - vector min, max; -} bBox; +struct bvhNode { + float bounds[6]; // Node bounds (min x, max x, min y, max y, ...) + unsigned firstChildOrPrim; // Index to the first child or primitive (if the node is a leaf) + unsigned primCount : 30; + bool isLeaf : 1; +}; + +struct bvh { + struct bvhNode* nodes; + int *primIndices; + unsigned nodeCount; +}; // Bin used to approximate the SAH. typedef struct Bin { - bBox bbox; + struct boundingBox bbox; unsigned count; float cost; } Bin; -static inline void storeBBoxInNode(struct bvhNode *node, const bBox *bbox) { +static inline void storeBBoxInNode(struct bvhNode *node, const struct boundingBox *bbox) { node->bounds[0] = bbox->min.x; node->bounds[1] = bbox->max.x; node->bounds[2] = bbox->min.y; @@ -52,7 +61,7 @@ static inline void storeBBoxInNode(struct bvhNode *node, const bBox *bbox) { node->bounds[5] = bbox->max.z; } -static inline void loadBBoxFromNode(bBox *bbox, const struct bvhNode *node) { +static inline void loadBBoxFromNode(struct boundingBox *bbox, const struct bvhNode *node) { bbox->min.x = node->bounds[0]; bbox->max.x = node->bounds[1]; bbox->min.y = node->bounds[2]; @@ -61,25 +70,10 @@ static inline void loadBBoxFromNode(bBox *bbox, const struct bvhNode *node) { bbox->max.z = node->bounds[5]; } -static const bBox emptyBBox = { - .min = { FLT_MAX, FLT_MAX, FLT_MAX }, - .max = { -FLT_MAX, -FLT_MAX, -FLT_MAX } -}; - -static inline float halfArea(const bBox *bbox) { - vector extent = vecSub(bbox->max, bbox->min); - return extent.x * (extent.y + extent.z) + extent.y * extent.z; -} - -static inline void extendBBox(bBox *dst, const bBox *src) { - dst->min = vecMin(dst->min, src->min); - dst->max = vecMax(dst->max, src->max); -} - static inline float nodeArea(const struct bvhNode *node) { - bBox bbox; + struct boundingBox bbox; loadBBoxFromNode(&bbox, node); - return halfArea(&bbox); + return bboxHalfArea(&bbox); } static inline void makeLeaf(struct bvhNode* node, unsigned begin, unsigned primCount) { @@ -136,7 +130,7 @@ static inline unsigned partitionPrimitiveIndices( static void buildBvhRecursive( unsigned nodeId, struct bvh *bvh, - const bBox *bboxes, + const struct boundingBox *bboxes, const vector *centers, unsigned begin, unsigned end, unsigned depth) @@ -171,13 +165,13 @@ static void buildBvhRecursive( // Sweep from the right to the left to compute the partial SAH cost. // Recall that the SAH is the sum of two parts: SA(left) * N(left) + SA(right) * N(right). // This loop computes SA(right) * N(right) alone. - bBox curBBox = emptyBBox; + struct boundingBox curBBox = emptyBBox; unsigned curCount = 0; for (unsigned i = BIN_COUNT; i > 1; --i) { Bin *bin = &bins[axis][i - 1]; curCount += bin->count; extendBBox(&curBBox, &bin->bbox); - bin->cost = curCount * halfArea(&curBBox); + bin->cost = curCount * bboxHalfArea(&curBBox); } // Sweep from the left to the right to compute the full cost and find the minimum. @@ -187,7 +181,7 @@ static void buildBvhRecursive( Bin *bin = &bins[axis][i]; curCount += bin->count; extendBBox(&curBBox, &bin->bbox); - float cost = curCount * halfArea(&curBBox) + bins[axis][i + 1].cost; + float cost = curCount * bboxHalfArea(&curBBox) + bins[axis][i + 1].cost; if (cost < minCost[axis]) { minBin[axis] = i + 1; minCost[axis] = cost; @@ -227,8 +221,8 @@ static void buildBvhRecursive( bvh->nodeCount += 2; // Compute the bounding box of the children - bBox leftBBox = emptyBBox; - bBox rightBBox = emptyBBox; + struct boundingBox leftBBox = emptyBBox; + struct boundingBox rightBBox = emptyBBox; for (unsigned i = 0; i < minBin[minAxis]; ++i) extendBBox(&leftBBox, &bins[minAxis][i].bbox); for (unsigned i = minBin[minAxis]; i < BIN_COUNT; ++i) @@ -245,21 +239,21 @@ static void buildBvhRecursive( } } -struct bvh *buildBvh(int *polys, unsigned count) { +// Builds a BVH using the provided callback to obtain bounding boxes and centers for each primitive +static inline struct bvh *buildBvhGeneric( + void* userData, + void (*getBBoxAndCenter)(void*, unsigned, struct boundingBox*, vector*), + unsigned count) +{ vector *centers = malloc(sizeof(vector) * count); - bBox *bboxes = malloc(sizeof(bBox) * count); + struct boundingBox *bboxes = malloc(sizeof(struct boundingBox) * count); int *primIndices = malloc(sizeof(int) * count); - bBox rootBBox = emptyBBox; + struct boundingBox rootBBox = emptyBBox; // Precompute bboxes and centers for (unsigned i = 0; i < count; ++i) { - vector v0 = vertexArray[polygonArray[polys[i]].vertexIndex[0]]; - vector v1 = vertexArray[polygonArray[polys[i]].vertexIndex[1]]; - vector v2 = vertexArray[polygonArray[polys[i]].vertexIndex[2]]; - centers[i] = getMidPoint(v0, v1, v2); - bboxes[i].min = vecMin(v0, vecMin(v1, v2)); - bboxes[i].max = vecMax(v0, vecMax(v1, v2)); + getBBoxAndCenter(userData, i, &bboxes[i], ¢ers[i]); primIndices[i] = i; rootBBox.min = vecMin(rootBBox.min, bboxes[i].min); rootBBox.max = vecMax(rootBBox.max, bboxes[i].max); @@ -278,25 +272,36 @@ struct bvh *buildBvh(int *polys, unsigned count) { // Shrink array of nodes (since some leaves may contain more than 1 primitive) bvh->nodes = realloc(bvh->nodes, sizeof(struct bvhNode) * bvh->nodeCount); - for (unsigned i = 0; i < count; ++i) - primIndices[i] = polys[primIndices[i]]; free(centers); free(bboxes); return bvh; } -static inline bool rayIntersectsWithBvhLeaf(const struct bvh *bvh, const struct bvhNode *leaf, const struct lightRay *ray, struct hitRecord *isect) { - bool found = false; - for (int i = 0; i < leaf->primCount; ++i) { - struct poly p = polygonArray[bvh->primIndices[leaf->firstChildOrPrim + i]]; - if (rayIntersectsWithPolygon(ray, &p, &isect->distance, &isect->surfaceNormal, &isect->uv)) { - isect->didIntersect = true; - isect->type = hitTypePolygon; - isect->polyIndex = p.polyIndex; - found = true; - } - } - return found; +static void getPolyBBoxAndCenter(void *userData, unsigned i, struct boundingBox *bbox, vector *center) { + int* polys = userData; + vector v0 = vertexArray[polygonArray[polys[i]].vertexIndex[0]]; + vector v1 = vertexArray[polygonArray[polys[i]].vertexIndex[1]]; + vector v2 = vertexArray[polygonArray[polys[i]].vertexIndex[2]]; + *center = getMidPoint(v0, v1, v2); + bbox->min = vecMin(v0, vecMin(v1, v2)); + bbox->max = vecMax(v0, vecMax(v1, v2)); +} + +struct bvh *buildBottomLevelBvh(int *polys, unsigned count) { + struct bvh *bvh = buildBvhGeneric(polys, getPolyBBoxAndCenter, count); + for (unsigned i = 0; i < count; ++i) + bvh->primIndices[i] = polys[bvh->primIndices[i]]; + return bvh; +} + +static void getMeshBBoxAndCenter(void *userData, unsigned i, struct boundingBox *bbox, vector *center) { + struct mesh *meshes = userData; + loadBBoxFromNode(bbox, &meshes[i].bvh->nodes[0]); + *center = bboxCenter(bbox); +} + +struct bvh *buildTopLevelBvh(struct mesh *meshes, unsigned meshCount) { + return buildBvhGeneric(meshes, getMeshBBoxAndCenter, meshCount); } static inline float fastMultiplyAdd(float a, float b, float c) { @@ -307,7 +312,7 @@ static inline float fastMultiplyAdd(float a, float b, float c) { #endif } -static inline bool rayIntersectsWithBvhNode( +static inline bool intersectNode( const struct bvhNode *node, const vector *invDir, const vector *scaledStart, @@ -315,12 +320,12 @@ static inline bool rayIntersectsWithBvhNode( float maxDist, float* tEntry) { - float tMinX = fastMultiplyAdd(node->bounds[0 * 2 + octant[0]], invDir->x, scaledStart->x); - float tMaxX = fastMultiplyAdd(node->bounds[0 * 2 + 1 - octant[0]], invDir->x, scaledStart->x); - float tMinY = fastMultiplyAdd(node->bounds[1 * 2 + octant[1]], invDir->y, scaledStart->y); - float tMaxY = fastMultiplyAdd(node->bounds[1 * 2 + 1 - octant[1]], invDir->y, scaledStart->y); - float tMinZ = fastMultiplyAdd(node->bounds[2 * 2 + octant[2]], invDir->z, scaledStart->z); - float tMaxZ = fastMultiplyAdd(node->bounds[2 * 2 + 1 - octant[2]], invDir->z, scaledStart->z); + float tMinX = fastMultiplyAdd(node->bounds[0 + octant[0]], invDir->x, scaledStart->x); + float tMaxX = fastMultiplyAdd(node->bounds[0 + 1 - octant[0]], invDir->x, scaledStart->x); + float tMinY = fastMultiplyAdd(node->bounds[2 + octant[1]], invDir->y, scaledStart->y); + float tMaxY = fastMultiplyAdd(node->bounds[2 + 1 - octant[1]], invDir->y, scaledStart->y); + float tMinZ = fastMultiplyAdd(node->bounds[4 + octant[2]], invDir->z, scaledStart->z); + float tMaxZ = fastMultiplyAdd(node->bounds[4 + 1 - octant[2]], invDir->z, scaledStart->z); // Note the order here is important. // Because the comparisons are of the form x < y ? x : y, they // are guaranteed not to produce NaNs if the right hand side is not a NaN. @@ -335,7 +340,12 @@ static inline bool rayIntersectsWithBvhNode( return tMin <= tMax; } -bool rayIntersectsWithBvh(const struct bvh *bvh, const struct lightRay *ray, struct hitRecord *isect) { +static inline bool traverseBvhGeneric( + void* userData, + const struct bvh *bvh, + bool (*intersectLeaf)(void*, const struct bvh*, const struct bvhNode*, const struct lightRay*, struct hitRecord*), + const struct lightRay *ray, + struct hitRecord *isect) { const struct bvhNode *stack[MAX_BVH_DEPTH + 1]; int stackSize = 0; @@ -352,8 +362,8 @@ bool rayIntersectsWithBvh(const struct bvh *bvh, const struct lightRay *ray, str // Special case when the BVH is just a single leaf if (bvh->nodeCount == 1) { float tEntry; - if (rayIntersectsWithBvhNode(bvh->nodes, &invDir, &scaledStart, octant, maxDist, &tEntry)) - return rayIntersectsWithBvhLeaf(bvh, bvh->nodes, ray, isect); + if (intersectNode(bvh->nodes, &invDir, &scaledStart, octant, maxDist, &tEntry)) + return intersectLeaf(userData, bvh, bvh->nodes, ray, isect); return false; } @@ -365,12 +375,12 @@ bool rayIntersectsWithBvh(const struct bvh *bvh, const struct lightRay *ray, str const struct bvhNode *rightNode = &bvh->nodes[firstChild + 1]; float tEntryLeft, tEntryRight; - bool hitLeft = rayIntersectsWithBvhNode(leftNode, &invDir, &scaledStart, octant, maxDist, &tEntryLeft); - bool hitRight = rayIntersectsWithBvhNode(rightNode, &invDir, &scaledStart, octant, maxDist, &tEntryRight); + bool hitLeft = intersectNode(leftNode, &invDir, &scaledStart, octant, maxDist, &tEntryLeft); + bool hitRight = intersectNode(rightNode, &invDir, &scaledStart, octant, maxDist, &tEntryRight); if (hitLeft) { - if (leftNode->isLeaf) { - if (rayIntersectsWithBvhLeaf(bvh, leftNode, ray, isect)) { + if (unlikely(leftNode->isLeaf)) { + if (intersectLeaf(userData, bvh, leftNode, ray, isect)) { maxDist = isect->distance; hasHit = true; } @@ -380,8 +390,8 @@ bool rayIntersectsWithBvh(const struct bvh *bvh, const struct lightRay *ray, str leftNode = NULL; if (hitRight) { - if (rightNode->isLeaf) { - if (rayIntersectsWithBvhLeaf(bvh, rightNode, ray, isect)) { + if (unlikely(rightNode->isLeaf)) { + if (intersectLeaf(userData, bvh, rightNode, ray, isect)) { maxDist = isect->distance; hasHit = true; } @@ -411,6 +421,60 @@ bool rayIntersectsWithBvh(const struct bvh *bvh, const struct lightRay *ray, str return hasHit; } +static inline bool intersectBottomLevelLeaf( + void *userData, + const struct bvh *bvh, + const struct bvhNode *leaf, + const struct lightRay *ray, + struct hitRecord *isect) +{ + const struct poly *polygons = userData; + bool found = false; + for (int i = 0; i < leaf->primCount; ++i) { + const struct poly *p = &polygons[bvh->primIndices[leaf->firstChildOrPrim + i]]; + if (rayIntersectsWithPolygon(ray, p, &isect->distance, &isect->surfaceNormal, &isect->uv)) { + isect->didIntersect = true; + isect->type = hitTypePolygon; + isect->polyIndex = p->polyIndex; + found = true; + } + } + return found; +} + +static inline bool traverseBottomLevelBvh(const struct bvh *bvh, const struct lightRay *ray, struct hitRecord *isect) { + // Note: polygonArray is a global variable, so we *could* avoid passing it as user data. + // However, it is good practice to do so, since it might be the case in the future that + // it is turned into private scene data. + return traverseBvhGeneric(polygonArray, bvh, intersectBottomLevelLeaf, ray, isect); +} + +static inline bool intersectTopLevelLeaf( + void *userData, + const struct bvh *bvh, + const struct bvhNode *leaf, + const struct lightRay *ray, + struct hitRecord *isect) +{ + const struct mesh *meshes = userData; + bool found = false; + for (int i = 0; i < leaf->primCount; ++i) { + const struct mesh *m = &meshes[bvh->primIndices[leaf->firstChildOrPrim + i]]; + if (traverseBottomLevelBvh(m->bvh, ray, isect)) + found = true; + } + return found; +} + +bool traverseTopLevelBvh( + const struct mesh *meshes, + const struct bvh *bvh, + const struct lightRay *ray, + struct hitRecord *isect) +{ + return traverseBvhGeneric((void*)meshes, bvh, intersectTopLevelLeaf, ray, isect); +} + void destroyBvh(struct bvh *bvh) { if (bvh) { free(bvh->nodes); diff --git a/src/accelerators/bvh.h b/src/accelerators/bvh.h index feb9c128..01b7c4c1 100644 --- a/src/accelerators/bvh.h +++ b/src/accelerators/bvh.h @@ -12,27 +12,22 @@ struct lightRay; struct hitRecord; +struct mesh; -struct bvhNode { - float bounds[6]; // Node bounds (min x, max x, min y, max y, ...) - unsigned firstChildOrPrim; // Index to the first child or primitive (if the node is a leaf) - unsigned primCount : 30; - bool isLeaf : 1; -}; +struct bvh; -struct bvh { - struct bvhNode* nodes; - int *primIndices; - unsigned nodeCount; -}; - -/// Builds a BVH +/// Builds a BVH for a given set of polygons /// @param polygons Array of polygons to process /// @param count Amount of polygons given -struct bvh *buildBvh(int *polygons, unsigned count); +struct bvh *buildBottomLevelBvh(int *polygons, unsigned count); + +/// Builds a top-level BVH for a given set of meshes +/// @param meshes Meshes to build a top-level BVH for +/// @param meshCount Amount of meshes +struct bvh *buildTopLevelBvh(struct mesh *meshes, unsigned meshCount); -/// Intersects a ray with the given BVH -bool rayIntersectsWithBvh(const struct bvh *bvh, const struct lightRay *ray, struct hitRecord *isect); +/// Intersect a ray with a scene top-level BVH +bool traverseTopLevelBvh(const struct mesh *meshes, const struct bvh *bvh, const struct lightRay *ray, struct hitRecord *isect); /// Frees the memory allocated by the given BVH void destroyBvh(struct bvh *); diff --git a/src/accelerators/kdtree.c b/src/accelerators/kdtree.c deleted file mode 100644 index 0b4b4b50..00000000 --- a/src/accelerators/kdtree.c +++ /dev/null @@ -1,212 +0,0 @@ -// -// kdtree.c -// C-ray -// -// Created by Valtteri Koskivuori on 28/04/2017. -// Copyright © 2015-2020 Valtteri Koskivuori. All rights reserved. -// - -#include "../includes.h" -#include "kdtree.h" -#include "bbox.h" - -#include "../renderer/pathtrace.h" -#include "../datatypes/vertexbuffer.h" -#include "../datatypes/poly.h" - -//Tree funcs - -/* - Nodes are built per-object - Start at root node that contains all tris, and a bounding box for the mesh - At each level, split on a different axis in order X,Y,Z,X,Y,Z OR by longest axis - For each level: - 1. Find the midpoint of all tris in the node (calculated in a bounding box already) - 2. Find the longest axis of the bounding box for that node - 3. For each tri in the node, check if for the current axis, it is less than or greater than the overall midpoint - If less, push to left child - if greater, push to right child - */ - -struct Array { - int *array; - size_t used; - size_t size; -}; - -void initArray(struct Array *a, size_t initialSize) { - a->array = malloc(initialSize * sizeof(*a->array)); - a->used = 0; - a->size = initialSize; -} - -void insertArray(struct Array *a, int element) { - // a->used is the number of used entries, because a->array[a->used++] updates a->used only *after* the array has been accessed. - // Therefore a->used can go up to a->size - if (a->used == a->size) { - a->size *= 2; - a->array = realloc(a->array, a->size * sizeof(a->array)); - } - a->array[a->used++] = element; -} - -void freeArray(struct Array *a) { - free(a->array); - a->array = NULL; - a->used = a->size = 0; -} - -struct kdTreeNode *getNewNode() { - struct kdTreeNode *node = calloc(1, sizeof(*node)); - node->bbox = NULL; - node->left = NULL; - node->right = NULL; - node->polygons = NULL; - node->polyCount = 0; - return node; -} - -struct kdTreeNode *buildTree(int *polygons, const int count) { - struct kdTreeNode *node = getNewNode(); - node->polygons = polygons; - node->polyCount = count; - - if (count == 0) - return node; - if (count == 1) { - node->bbox = computeBoundingBox(&node->polygons[0], 1); - node->left = NULL; - node->right = NULL; - return node; - } - - node->bbox = computeBoundingBox(node->polygons, node->polyCount); - float currentSAHCost = (float)node->polyCount * node->bbox->surfaceArea; - - struct vector midPoint = node->bbox->midPoint; - - struct Array leftPolys; - initArray(&leftPolys, 5); - struct Array rightPolys; - initArray(&rightPolys, 5); - - enum bboxAxis axis = getLongestAxis(node->bbox); - - for (int i = 0; i < node->polyCount; ++i) { - struct vector polyMidPoint = getMidPoint(vertexArray[polygonArray[node->polygons[i]].vertexIndex[0]], - vertexArray[polygonArray[node->polygons[i]].vertexIndex[1]], - vertexArray[polygonArray[node->polygons[i]].vertexIndex[2]]); - - if (((axis == X) && (midPoint.x >= polyMidPoint.x)) || - ((axis == Y) && (midPoint.y >= polyMidPoint.y)) || - ((axis == Z) && (midPoint.z >= polyMidPoint.z))) { - insertArray(&rightPolys, node->polygons[i]); - } else { - insertArray(&leftPolys, node->polygons[i]); - } - } - - bool rightFreed = false; - bool leftFreed = false; - if (leftPolys.used == 0 && rightPolys.used > 0) { - freeArray(&leftPolys); - leftPolys = rightPolys; - leftFreed = true; - } - if (rightPolys.used == 0 && leftPolys.used > 0) { - freeArray(&rightPolys); - rightPolys = leftPolys; - rightFreed = true; - } - - struct boundingBox *leftBBox = computeBoundingBox(leftPolys.array, (int)leftPolys.used); - struct boundingBox *rightBBox = computeBoundingBox(rightPolys.array, (int)rightPolys.used); - - float leftSAHCost = leftPolys.used * leftBBox->surfaceArea; - float rightSAHCost = rightPolys.used * rightBBox->surfaceArea; - - free(leftBBox); - free(rightBBox); - - if ((leftSAHCost + rightSAHCost) > currentSAHCost) { - //Stop here - node->left = NULL; - node->right = NULL; - if (leftPolys.used > 0 && !leftFreed) freeArray(&leftPolys); - if (rightPolys.used > 0 && !rightFreed) freeArray(&rightPolys); - } else { - //Keep going - node->left = buildTree(leftPolys.array, (int)leftPolys.used); - node->right = buildTree(rightPolys.array, (int)rightPolys.used); - } - - return node; -} - -//Recurse through tree and count orphan nodes with no polygons -int checkTree(const struct kdTreeNode *node) { - int orphans = 0; - if (node) { - if (node->polyCount == 0) { - orphans += 1; - } - if (node->left) { - orphans += checkTree(node->left); - } - if (node->right) { - orphans += checkTree(node->right); - } - } - return orphans; -} - -int countNodes(const struct kdTreeNode *node) { - int nodes = 0; - if (node) { - if (node->left) { - nodes += countNodes(node->left); - } - if (node->right) { - nodes += countNodes(node->right); - } - nodes += 1; - } - return nodes; -} - -bool rayIntersectsWithNode(const struct kdTreeNode *node, const struct lightRay *ray, struct hitRecord *isect) { - if (!node) return false; - if (!rayIntersectsWithAABB(node->bbox, ray)) return false; - if (node->left || node->right) { - //Recurse down both sides - bool hitLeft = rayIntersectsWithNode(node->left, ray, isect); - bool hitRight = rayIntersectsWithNode(node->right, ray, isect); - return hitLeft || hitRight; - } else { - bool hasHit = false; - //This is a leaf, so check all polys - for (int i = 0; i < node->polyCount; ++i) { - struct poly p = polygonArray[node->polygons[i]]; - if (rayIntersectsWithPolygon(ray, &p, &isect->distance, &isect->surfaceNormal, &isect->uv)) { - hasHit = true; - isect->type = hitTypePolygon; - isect->polyIndex = p.polyIndex; - } - } - if (hasHit) { // Return only after checking every polygon to make sure we got the closest one. - isect->didIntersect = true; - return true; - } - } - return false; -} - -void destroyTree(struct kdTreeNode *node) { - if (node) { - destroyTree(node->left); - destroyTree(node->right); - free(node->bbox); - free(node->polygons); - free(node); - } -} diff --git a/src/accelerators/kdtree.h b/src/accelerators/kdtree.h deleted file mode 100644 index e987c723..00000000 --- a/src/accelerators/kdtree.h +++ /dev/null @@ -1,44 +0,0 @@ -// -// kdtree.h -// C-ray -// -// Created by Valtteri Koskivuori on 28/04/2017. -// Copyright © 2015-2020 Valtteri Koskivuori. All rights reserved. -// - -#pragma once - -struct lightRay; -struct hitRecord; - -struct kdTreeNode { - struct boundingBox *bbox;//Bounding box - struct kdTreeNode *left; //Pointer to left child - struct kdTreeNode *right;//Pointer to right child - int *polygons; //indices to polygons within the bounding box - int polyCount; //Amount of polygons -}; - -/// Builds a KD-tree for a given array of polygons and returns a pointer to the root node -/// @param polygons Array of polygons to process -/// @param count Amount of polygons given -/// @param depth Current depth for recursive calls -struct kdTreeNode *buildTree(int *polygons, const int count); - -/// Traverses a given KD-tree to find an intersection between a ray and a polygon in that tree. Hopefully really fast. -/// @param node Root node to start traversing from -/// @param ray Ray to check intersection against -/// @param isect Intersection information is saved to this struct -bool rayIntersectsWithNode(const struct kdTreeNode *node, const struct lightRay *ray, struct hitRecord *isect); - -/// Count total nodes in a given tree -/// @param node root node of a tree to evaluate -int countNodes(const struct kdTreeNode *node); - -/// Check the health of a given tree -/// @param node root node of a tree to evaluate -int checkTree(const struct kdTreeNode *node); - -/// Free a given tree -/// @param node Root node of a tree to free -void destroyTree(struct kdTreeNode *node); diff --git a/src/datatypes/bbox.h b/src/datatypes/bbox.h new file mode 100644 index 00000000..0ac7f5da --- /dev/null +++ b/src/datatypes/bbox.h @@ -0,0 +1,36 @@ +// +// bbox.h +// C-ray +// +// Created by Valtteri Koskivuori on 28/04/2017. +// Copyright © 2015-2020 Valtteri Koskivuori. All rights reserved. +// + +#pragma once + +#include +#include "../datatypes/vector.h" + +/// Bounding box for a given set of primitives +struct boundingBox { + struct vector min, max; +}; + +static const struct boundingBox emptyBBox = { + .min = { FLT_MAX, FLT_MAX, FLT_MAX }, + .max = { -FLT_MAX, -FLT_MAX, -FLT_MAX } +}; + +static inline float bboxHalfArea(const struct boundingBox *bbox) { + vector extent = vecSub(bbox->max, bbox->min); + return extent.x * (extent.y + extent.z) + extent.y * extent.z; +} + +static inline void extendBBox(struct boundingBox *dst, const struct boundingBox *src) { + dst->min = vecMin(dst->min, src->min); + dst->max = vecMax(dst->max, src->max); +} + +static inline vector bboxCenter(const struct boundingBox* bbox) { + return vecScale(vecAdd(bbox->max, bbox->min), 0.5f); +} diff --git a/src/datatypes/mesh.c b/src/datatypes/mesh.c index 3d83a7a6..169a47d6 100644 --- a/src/datatypes/mesh.c +++ b/src/datatypes/mesh.c @@ -9,7 +9,6 @@ #include "../includes.h" #include "mesh.h" -#include "../accelerators/kdtree.h" #include "../accelerators/bvh.h" #include "vertexbuffer.h" #include "transforms.h" @@ -65,11 +64,7 @@ void destroyMesh(struct mesh *mesh) { if (mesh->transformCount > 0) { free(mesh->transforms); } -#ifdef OLD_KD_TREE - destroyTree(mesh->tree); -#else destroyBvh(mesh->bvh); -#endif if (mesh->materials) { for (int i = 0; i < mesh->materialCount; ++i) { destroyMaterial(&mesh->materials[i]); diff --git a/src/datatypes/mesh.h b/src/datatypes/mesh.h index 9111229a..d3d682ba 100644 --- a/src/datatypes/mesh.h +++ b/src/datatypes/mesh.h @@ -42,13 +42,8 @@ struct mesh { int materialCount; struct material *materials; -#ifdef OLD_KD_TREE - //Root node of the kd-tree for this mesh - struct kdTreeNode *tree; -#else struct bvh *bvh; -#endif - + char *name; }; diff --git a/src/datatypes/poly.h b/src/datatypes/poly.h index 55ef78df..4652084a 100644 --- a/src/datatypes/poly.h +++ b/src/datatypes/poly.h @@ -12,9 +12,10 @@ struct poly { int vertexIndex[MAX_CRAY_VERTEX_COUNT]; int normalIndex[MAX_CRAY_VERTEX_COUNT]; int textureIndex[MAX_CRAY_VERTEX_COUNT]; - int materialIndex; + int materialIndex: 16; + int meshIndex: 16; //FIXME: This shouldn't be here. int polyIndex; - int vertexCount; + int vertexCount: 3; bool hasNormals; }; diff --git a/src/datatypes/scene.c b/src/datatypes/scene.c index e6a8e87c..690a5f4a 100644 --- a/src/datatypes/scene.c +++ b/src/datatypes/scene.c @@ -18,7 +18,6 @@ #include "image/hdr.h" #include "camera.h" #include "vertexbuffer.h" -#include "../accelerators/kdtree.h" #include "../accelerators/bvh.h" #include "tile.h" #include "mesh.h" @@ -40,11 +39,7 @@ void transformMeshes(struct world *scene) { //TODO: Parallelize this task void computeAccels(struct mesh *meshes, int meshCount) { -#ifdef OLD_KD_TREE - logr(info, "Computing KD-trees: "); -#else logr(info, "Computing BVHs: "); -#endif struct timeval timer = {0}; startTimer(&timer); for (int i = 0; i < meshCount; ++i) { @@ -52,23 +47,21 @@ void computeAccels(struct mesh *meshes, int meshCount) { for (int j = 0; j < meshes[i].polyCount; ++j) { indices[j] = meshes[i].firstPolyIndex + j; } -#ifdef OLD_KD_TREE - meshes[i].tree = buildTree(indices, meshes[i].polyCount); -#else - meshes[i].bvh = buildBvh(indices, meshes[i].polyCount); -#endif - - // Optional tree checking - /*int orphans = checkTree(meshes[i].tree); - if (orphans > 0) { - int total = countNodes(meshes[i].tree); - logr(warning, "Found %i/%i orphan nodes in %s kdtree\n", orphans, total, meshes[i].name); - }*/ + meshes[i].bvh = buildBottomLevelBvh(indices, meshes[i].polyCount); } printSmartTime(getMs(timer)); printf("\n"); } +void computeTopLevelBvh(struct world *scene) { + logr(info, "Computing top-level BVH: "); + struct timeval timer = {0}; + startTimer(&timer); + scene->topLevel = buildTopLevelBvh(scene->meshes, scene->meshCount); + printSmartTime(getMs(timer)); + printf("\n"); +} + void printSceneStats(struct world *scene, unsigned long long ms) { logr(info, "Scene construction completed in "); printSmartTime(ms); @@ -132,10 +125,10 @@ int loadScene(struct renderer *r, char *input) { } checkAndSetCliOverrides(r); - transformCameraIntoView(r->scene->camera); transformMeshes(r->scene); computeAccels(r->scene->meshes, r->scene->meshCount); + computeTopLevelBvh(r->scene); printSceneStats(r->scene, getMs(timer)); //Quantize image into renderTiles diff --git a/src/datatypes/scene.h b/src/datatypes/scene.h index c715ce3b..d1f8cc60 100644 --- a/src/datatypes/scene.h +++ b/src/datatypes/scene.h @@ -24,6 +24,8 @@ struct world { struct mesh *meshes; int meshCount; + struct bvh *topLevel; + //Spheres struct sphere *spheres; int sphereCount; diff --git a/src/includes.h b/src/includes.h index 5e926663..b6eb6a32 100644 --- a/src/includes.h +++ b/src/includes.h @@ -19,6 +19,13 @@ #define min(a,b) (((a) < (b)) ? (a) : (b)) #define max(a,b) (((a) > (b)) ? (a) : (b)) #define invsqrt(x) (1.0f / sqrtf(x)) +#if defined(__GNUC__) || defined(__clang__) +#define unlikely(x) __builtin_expect(x, false) +#define likely(x) __builtin_expect(x, true) +#else +#define unlikely(x) (x) +#define likely(x) (x) +#endif //Master include file #ifdef __linux__ diff --git a/src/renderer/pathtrace.c b/src/renderer/pathtrace.c index e1ba0f7b..87e6145c 100644 --- a/src/renderer/pathtrace.c +++ b/src/renderer/pathtrace.c @@ -11,8 +11,6 @@ #include "../datatypes/scene.h" #include "../datatypes/camera.h" -#include "../accelerators/bbox.h" -#include "../accelerators/kdtree.h" #include "../accelerators/bvh.h" #include "../datatypes/image/texture.h" #include "../datatypes/image/hdr.h" @@ -21,6 +19,7 @@ #include "../datatypes/poly.h" #include "../datatypes/mesh.h" #include "samplers/sampler.h" +#include "sky.h" struct hitRecord getClosestIsect(const struct lightRay *incidentRay, const struct world *scene); struct color getBackground(const struct lightRay *incidentRay, const struct world *scene); @@ -113,21 +112,12 @@ struct hitRecord getClosestIsect(const struct lightRay *incidentRay, const struc isect.didIntersect = true; } } - for (int o = 0; o < scene->meshCount; ++o) { -#ifdef OLD_KD_TREE - if (rayIntersectsWithNode(scene->meshes[o].tree, incidentRay, &isect)) { -#else - if (rayIntersectsWithBvh(scene->meshes[o].bvh, incidentRay, &isect)) { -#endif - isect.end = scene->meshes[o].materials[polygonArray[isect.polyIndex].materialIndex]; - computeSurfaceProps(polygonArray[isect.polyIndex], isect.uv, &isect.hitPoint, &isect.surfaceNormal); - - if (isect.end.hasNormalMap) { - isect.surfaceNormal = bumpmap(&isect); - } - - isect.didIntersect = true; - } + + if (traverseTopLevelBvh(scene->meshes, scene->topLevel, incidentRay, &isect)) { + isect.end = scene->meshes[polygonArray[isect.polyIndex].meshIndex].materials[polygonArray[isect.polyIndex].materialIndex]; + computeSurfaceProps(polygonArray[isect.polyIndex], isect.uv, &isect.hitPoint, &isect.surfaceNormal); + if (isect.end.hasNormalMap) + isect.surfaceNormal = bumpmap(&isect); } return isect; } diff --git a/src/utils/converter.c b/src/utils/converter.c index dcf1fa52..703f29b6 100644 --- a/src/utils/converter.c +++ b/src/utils/converter.c @@ -35,9 +35,10 @@ struct coord coordFromObj(obj_vector *vec) { @param firstNormalIndex First normal index of the new polygon @param firstTextureIndex First texture index of the new polygon @param polyIndex polygonArray index offset + @param meshIndex Mesh this polygon belongs to. @return c-ray polygon */ -struct poly polyFromObj(obj_face *face, int firstVertexIndex, int firstNormalIndex, int firstTextureIndex, int polyIndex) { +struct poly polyFromObj(obj_face *face, int firstVertexIndex, int firstNormalIndex, int firstTextureIndex, int polyIndex, int meshIndex) { struct poly polygon; if (face->normal_index[0] == -1) { @@ -50,6 +51,7 @@ struct poly polyFromObj(obj_face *face, int firstVertexIndex, int firstNormalInd //If no materials are found (missing .mtl), we will just patch in a bright pink material to show that polygon.materialIndex = face->material_index == -1 ? 0 : face->material_index; polygon.polyIndex = polyIndex; + polygon.meshIndex = meshIndex; for (int i = 0; i < polygon.vertexCount; ++i) { polygon.vertexIndex[i] = firstVertexIndex + face->vertex_index[i]; } diff --git a/src/utils/converter.h b/src/utils/converter.h index 8e9a159e..fdca47f1 100644 --- a/src/utils/converter.h +++ b/src/utils/converter.h @@ -29,7 +29,7 @@ struct coord coordFromObj(obj_vector *vec); @param polyIndex polygonArray index offset @return c-ray polygon */ -struct poly polyFromObj(obj_face *face, int firstVertexIndex, int firstNormalIndex, int firstTextureIndex, int polyIndex); +struct poly polyFromObj(obj_face *face, int firstVertexIndex, int firstNormalIndex, int firstTextureIndex, int polyIndex, int meshIndex); /** Convert a given OBJ loader material into a c-ray material diff --git a/src/utils/loaders/sceneloader.c b/src/utils/loaders/sceneloader.c index 9dcc8dec..018f424f 100644 --- a/src/utils/loaders/sceneloader.c +++ b/src/utils/loaders/sceneloader.c @@ -30,7 +30,6 @@ #include "../platform/capabilities.h" #include "../../datatypes/image/imagefile.h" #include "../../renderer/renderer.h" -#include "../../accelerators/kdtree.h" #include "../converter.h" #include "textureloader.h" #include "objloader.h" @@ -174,7 +173,8 @@ bool loadMesh(struct renderer *r, char *inputFilePath, int idx, int meshCount) { newMesh->firstVectorIndex, newMesh->firstNormalIndex, newMesh->firstTextureIndex, - newMesh->firstPolyIndex + i); + newMesh->firstPolyIndex + i, + idx - 1); } newMesh->materials = calloc(1, sizeof(*newMesh->materials));