aboutsummaryrefslogtreecommitdiff
path: root/src/accelerate
diff options
context:
space:
mode:
authorsotech117 <michael_foiani@brown.edu>2023-12-07 16:23:20 -0500
committersotech117 <michael_foiani@brown.edu>2023-12-07 16:23:20 -0500
commitcaa765bff49d54217b75aaf0e7acf4e5392a11e4 (patch)
tree9b92914dfb88b99599e8e60e4512e9e9ea9a25db /src/accelerate
parenta9274459443f1d560d7580a162deb581549980cb (diff)
upload base code
Diffstat (limited to 'src/accelerate')
-rw-r--r--src/accelerate/bvh.cpp139
-rw-r--r--src/accelerate/bvh.h20
-rw-r--r--src/accelerate/kdtree.cpp273
-rw-r--r--src/accelerate/kdtree.h53
-rw-r--r--src/accelerate/myqtconcurrent.cpp80
-rw-r--r--src/accelerate/myqthreads.cpp130
6 files changed, 695 insertions, 0 deletions
diff --git a/src/accelerate/bvh.cpp b/src/accelerate/bvh.cpp
new file mode 100644
index 0000000..ce104a0
--- /dev/null
+++ b/src/accelerate/bvh.cpp
@@ -0,0 +1,139 @@
+#include "raytracer/raytracer.h"
+#include "bvh.h"
+
+bvh::bvh(
+ const std::vector<KdShape>& p_shapes,
+ int p_dimension)
+{
+ dimension = p_dimension;
+ if (p_shapes.empty()) {
+ return;
+ }
+
+ // compute the new bouding ragion from the shapes and add shapes to the root
+ BoundingRegion tmp {
+ glm::vec4(-FINF, -FINF, -FINF, 1.f),
+ glm::vec4(FINF, FINF, FINF, 1.f),
+ glm::vec4(-FINF, -FINF, -FINF, 1.f),
+ glm::vec4(FINF, FINF, FINF, 1.f),
+ glm::vec4(-FINF, -FINF, -FINF, 1.f),
+ glm::vec4(FINF, FINF, FINF, 1.f),
+ glm::vec4(0.f, 0.f, 0.f, 1.f)
+ };
+ shapes = std::vector<KdShape>();
+ for (const auto& shape : p_shapes) {
+ tmp.xmax = glm::max(tmp.xmax, shape.region.xmax);
+ tmp.xmin = glm::min(tmp.xmin, shape.region.xmin);
+ tmp.ymax = glm::max(tmp.ymax, shape.region.ymax);
+ tmp.ymin = glm::min(tmp.ymin, shape.region.ymin);
+ tmp.zmax = glm::max(tmp.zmax, shape.region.zmax);
+ tmp.zmin = glm::min(tmp.zmin, shape.region.zmin);
+ tmp.center.x = (tmp.xmax.x + tmp.xmin.x) / 2.f;
+ tmp.center.y = (tmp.ymax.y + tmp.ymin.y) / 2.f;
+ tmp.center.z = (tmp.zmax.z + tmp.zmin.z) / 2.f;
+
+ shapes.push_back(shape);
+ }
+ region = tmp;
+
+ // split the shapes into two groups, if more than two shapes
+ if (shapes.size() <= 2) {
+ return;
+ }
+ std::vector<KdShape> leftShapes;
+ std::vector<KdShape> rightShapes;
+ for (const auto& shape : shapes) {
+ if (shape.region.center[dimension] < region.center[dimension]) {
+ leftShapes.push_back(shape);
+ }
+ else if (shape.region.center[dimension] > region.center[dimension]) {
+ rightShapes.push_back(shape);
+ } else {
+ if (leftShapes.size() < rightShapes.size()) {
+ leftShapes.push_back(shape);
+ } else {
+ rightShapes.push_back(shape);
+ }
+ }
+ }
+
+ // make the children
+ leftChild = new bvh(leftShapes, (dimension + 1) % 3);
+ rightChild = new bvh(rightShapes, (dimension + 1) % 3);
+}
+
+float intersectRegion(
+ glm::vec4 p,
+ glm::vec4 d,
+ BoundingRegion region)
+{
+ float tXmin = (region.xmin.x - p.x) / d.x;
+ float tXmax = (region.xmax.x - p.x) / d.x;
+ float tYmin = (region.ymin.y - p.y) / d.y;
+ float tYmax = (region.ymax.y - p.y) / d.y;
+ float tZmin = (region.zmin.z - p.z) / d.z;
+ float tZmax = (region.zmax.z - p.z) / d.z;
+
+ float tMin = std::max(std::max(std::min(tXmin, tXmax), std::min(tYmin, tYmax)), std::min(tZmin, tZmax));
+ float tMax = std::min(std::min(std::max(tXmin, tXmax), std::max(tYmin, tYmax)), std::max(tZmin, tZmax));
+
+ if (tMin > tMax) {
+ return FINF;
+ }
+ return tMin;
+}
+
+float RayTracer::traverseBVH(
+ glm::vec4 p,
+ glm::vec4 d,
+ RenderShapeData &testShape,
+ bvh *root)
+{
+ std::vector<bvh*> stack = std::vector<bvh*>();
+ stack.push_back(root);
+ float minT = FINF;
+
+ while (!stack.empty())
+ {
+ auto current = *stack.back();
+ stack.pop_back();
+
+ if (current.leftChild == nullptr && current.rightChild == nullptr) {
+ for (const auto &shape: current.shapes) {
+ glm::vec4 pObject = shape.shape.inverseCTM * p;
+ glm::vec4 dObject = glm::normalize(shape.shape.inverseCTM * d);
+
+ glm::vec4 intersection = findIntersection(pObject, dObject, shape.shape);
+ if (intersection.w == 0.f) {
+ continue;
+ }
+ intersection = shape.shape.ctm * intersection;
+ // check within bounds
+ float tWorld = (intersection.x - p.x) / d.x;
+ if (tWorld < minT)
+ {
+ minT = tWorld;
+ testShape = shape.shape;
+ }
+ }
+ } else {
+ float leftIntersect = intersectRegion(p, d, current.leftChild->region);
+ float rightIntersect = intersectRegion(p, d, current.rightChild->region);
+ if (leftIntersect != FINF && rightIntersect != FINF) {
+ if (leftIntersect < rightIntersect) {
+ stack.push_back(current.rightChild);
+ stack.push_back(current.leftChild);
+ } else {
+ stack.push_back(current.leftChild);
+ stack.push_back(current.rightChild);
+ }
+ } else if (leftIntersect != FINF) {
+ stack.push_back(current.leftChild);
+ } else if (rightIntersect != FINF) {
+ stack.push_back(current.rightChild);
+ }
+ }
+ }
+
+ return minT;
+} \ No newline at end of file
diff --git a/src/accelerate/bvh.h b/src/accelerate/bvh.h
new file mode 100644
index 0000000..062f748
--- /dev/null
+++ b/src/accelerate/bvh.h
@@ -0,0 +1,20 @@
+#include "raytracer/raytracer.h"
+
+#ifndef PROJECTS_RAY_BVH_H
+
+class bvh
+{
+public:
+ bvh(const std::vector<KdShape> &shapes, int dimension);
+
+ std::vector<KdShape> shapes;
+ int dimension;
+ BoundingRegion region{};
+ bvh *leftChild;
+ bvh *rightChild;
+};
+
+
+#define PROJECTS_RAY_BVH_H
+
+#endif //PROJECTS_RAY_BVH_H
diff --git a/src/accelerate/kdtree.cpp b/src/accelerate/kdtree.cpp
new file mode 100644
index 0000000..4156c98
--- /dev/null
+++ b/src/accelerate/kdtree.cpp
@@ -0,0 +1,273 @@
+#include "kdtree.h"
+#include "raytracer/raytracer.h"
+
+// Constructor
+KdTree::KdTree(int pDimension, float pSplitCoord) {
+ empty = true;
+ dimension = pDimension;
+ splitCoord = pSplitCoord;
+
+ shapesWithinBounds = std::vector<KdShape>();
+
+ leftChild = nullptr;
+ rightChild = nullptr;
+}
+
+void KdTree::insert(KdShape shape)
+{
+ // first, add shape to this node
+ shapesWithinBounds.push_back(shape);
+
+ if (empty) {
+ empty = false;
+ return;
+ }
+
+ int nextDimension = (dimension + 1) % 3;
+ if (dimension == 0) // x split
+ {
+ if (
+ shape.region.xmin.x > splitCoord
+ )
+ // bound box is strictly to the right, only add right
+ {
+ if (rightChild == nullptr) {
+ rightChild = new KdTree(
+ nextDimension,
+ shape.region.center[nextDimension]); // next dim is y
+ }
+ }
+ else if (
+ shape.region.xmin.x < splitCoord
+ && shape.region.xmax.x > splitCoord
+ )
+ // bounding box overlaps center, need to add to both children
+ {
+ if (rightChild == nullptr) {
+ rightChild = new KdTree(
+ nextDimension,
+ shape.region.center[nextDimension]); // next dim is y
+ }
+
+ if (leftChild == nullptr) {
+ leftChild = new KdTree(
+ nextDimension,
+ shape.region.center[nextDimension]); // next dim is y
+ }
+
+ }
+ else if (
+ shape.region.xmax.x < splitCoord
+ )
+ // bounding box strictly to the left, only add left
+ {
+ if (leftChild == nullptr) {
+ leftChild = new KdTree(
+ nextDimension,
+ shape.region.center[nextDimension]); // next dim is y
+ }
+ }
+ }
+
+ else if (dimension == 1) // y split
+ {
+ if (shape.region.ymin.y > splitCoord) {
+ if (rightChild == nullptr) {
+ rightChild = new KdTree(
+ nextDimension,
+ shape.region.center[nextDimension]); // next dim is z
+ }
+ }
+ else if (
+ shape.region.ymin.y < splitCoord
+ && shape.region.ymax.y > splitCoord
+ ) {
+ if (rightChild == nullptr) {
+ rightChild = new KdTree(
+ nextDimension,
+ shape.region.center[nextDimension]); // next dim is z
+ }
+
+ if (leftChild == nullptr) {
+ leftChild = new KdTree(
+ nextDimension,
+ shape.region.center[nextDimension]); // next dim is z
+ }
+ }
+ else if (
+ shape.region.ymax.y < splitCoord
+ ) {
+ if (leftChild == nullptr) {
+ leftChild = new KdTree(
+ nextDimension,
+ shape.region.center[nextDimension]); // next dim is z
+ }
+ }
+ }
+
+ else if (dimension == 2) // z split
+ {
+ if (shape.region.zmin.z > splitCoord) {
+ if (rightChild == nullptr) {
+ rightChild = new KdTree(
+ nextDimension,
+ shape.region.center[nextDimension]); // next dim is x
+ }
+ }
+ else if (
+ shape.region.zmin.z < splitCoord
+ && shape.region.zmax.z > splitCoord
+ ) {
+ if (rightChild == nullptr) {
+ rightChild = new KdTree(
+ nextDimension,
+ shape.region.center[nextDimension]); // next dim is x
+ }
+
+ if (leftChild == nullptr) {
+ leftChild = new KdTree(
+ nextDimension,
+ shape.region.center[nextDimension]); // next dim is x
+ }
+ }
+ else if (
+ shape.region.zmax.z < splitCoord
+ ) {
+ if (leftChild == nullptr) {
+ leftChild = new KdTree(
+ nextDimension,
+ shape.region.center[nextDimension]); // next dim is x
+ }
+ }
+ }
+
+ // now, add shape to children
+ if (leftChild != nullptr) {
+ leftChild->insert(shape);
+ }
+ if (rightChild != nullptr) {
+ rightChild->insert(shape);
+ }
+}
+
+BoundingRegion KdTree::transformBoundingRegion(BoundingRegion region, glm::mat4 transformationMatrix, glm::vec3 basis)
+{
+ std::vector<glm::vec4> transformedPoints = std::vector<glm::vec4>();
+ transformedPoints.push_back(transformationMatrix * region.xmax);
+ transformedPoints.push_back(transformationMatrix * region.xmin);
+ transformedPoints.push_back(transformationMatrix * region.ymax);
+ transformedPoints.push_back(transformationMatrix * region.ymin);
+ transformedPoints.push_back(transformationMatrix * region.zmax);
+ transformedPoints.push_back(transformationMatrix * region.zmin);
+
+ BoundingRegion transformedRegion{
+ glm::vec4(-FINF),
+ glm::vec4(FINF),
+ glm::vec4(-FINF),
+ glm::vec4(FINF),
+ glm::vec4(-FINF),
+ glm::vec4(FINF),
+ glm::vec4(0.f)
+ }; // just init values, will be set to be correct
+
+ // these are the new bound points, but they may have been rotated or reflected
+ // this also ensures axis aligned bounding boxes, given the dots with the basis
+ for (glm::vec4 point: transformedPoints) {
+ if (point.x * basis.x > transformedRegion.xmax.x) {
+ transformedRegion.xmax = point;
+ }
+ if (point.x * basis.x < transformedRegion.xmin.x) {
+ transformedRegion.xmin = point;
+ }
+ if (point.y * basis.y > transformedRegion.ymax.y) {
+ transformedRegion.ymax = point;
+ }
+ if (point.y * basis.y < transformedRegion.ymin.y) {
+ transformedRegion.ymin = point;
+ }
+ if (point.z * basis.z > transformedRegion.zmax.z) {
+ transformedRegion.zmax = point;
+ }
+ if (point.z * basis.z < transformedRegion.zmin.z) {
+ transformedRegion.zmin = point;
+ }
+ }
+
+ transformedRegion.center = transformationMatrix * region.center;
+ return transformedRegion;
+}
+
+// TODO: return the float with the shape
+float RayTracer::traverse(
+ glm::vec4 p,
+ glm::vec4 d,
+ float tStart,
+ float tEnd,
+ RenderShapeData &testShape,
+ KdTree *tree)
+{
+ if (tree == nullptr) {
+ return FINF;
+ }
+
+ // leaf node
+ if ( tree->shapesWithinBounds.size() <= 2 ||
+ tree->leftChild == nullptr || tree->rightChild == nullptr)
+ {
+ float minT = FINF;
+ for (const auto &shape: tree->shapesWithinBounds) {
+ glm::vec4 pObject = shape.shape.inverseCTM * p;
+ glm::vec4 dObject = glm::normalize(shape.shape.inverseCTM * d);
+
+ glm::vec4 intersection = findIntersection(pObject, dObject, shape.shape);
+ if (intersection.w == 0.f) {
+ continue;
+ }
+ intersection = shape.shape.ctm * intersection;
+ // check within bounds
+ if (
+ intersection.x <= shape.region.xmax.x && intersection.x >= shape.region.xmin.x
+ &&
+ intersection.y <= shape.region.ymax.y && intersection.y >= shape.region.ymin.y
+ &&
+ intersection.z <= shape.region.zmax.z && intersection.z >= shape.region.zmin.z
+ )
+ {
+ float tWorld = (intersection.x - p.x) / d.x;
+ if (tWorld < minT) {
+ minT = tWorld;
+ testShape = shape.shape;
+ }
+ }
+ }
+ return minT;
+ }
+
+ // solve for t, only in current 1d-dimension
+ float t = (tree->splitCoord - p[tree->dimension]) / d[tree->dimension];
+
+ // There are three cases:
+ // 1) only intersects with left (front) child (t <= tEnd)
+ // 2) only intersects with right (back) child (t <= tStart)
+ // 3) intersects with both children (tStart <= t <= tEnd)
+ // on last case, we need to traverse both children,
+ // but gain a significant speedup by traversing the one that is closer first.
+
+ if (t <= tStart && tree->rightChild != nullptr) // case 1)
+ {
+ return traverse(p, d, tStart, tEnd, testShape, tree->rightChild);
+ }
+ else if (t >= tEnd && tree->leftChild != nullptr) // case 2)
+ {
+ return traverse(p, d, tStart, tEnd, testShape, tree->leftChild);
+ }
+ else // case 3)
+ {
+ float t_hit = traverse(p, d, tStart, t, testShape, tree->leftChild);
+ if (t_hit < t)
+ { // this is where we save time!
+ return t_hit;
+ }
+ return traverse(p, d, t, tEnd, testShape, tree->rightChild);
+ }
+} \ No newline at end of file
diff --git a/src/accelerate/kdtree.h b/src/accelerate/kdtree.h
new file mode 100644
index 0000000..e33aa59
--- /dev/null
+++ b/src/accelerate/kdtree.h
@@ -0,0 +1,53 @@
+#ifndef KDTREE_H
+#define KDTREE_H
+#include "utils/sceneparser.h"
+#include <queue>
+#include <vector>
+
+typedef struct {
+ glm::vec4 xmax;
+ glm::vec4 xmin;
+ glm::vec4 ymax;
+ glm::vec4 ymin;
+ glm::vec4 zmax;
+ glm::vec4 zmin;
+ glm::vec4 center;
+} BoundingRegion;
+
+typedef struct KdShape
+{
+ RenderShapeData shape;
+ BoundingRegion region;
+} KdShape;
+
+class KdTree
+{
+public:
+
+ KdTree(int pDimension, float pSplitCoord);
+
+ bool empty;
+ int dimension;
+ float splitCoord;
+ std::vector<KdShape> shapesWithinBounds;
+ void insert(KdShape shape);
+
+ KdTree *leftChild;
+ KdTree *rightChild;
+
+ // todo: make basis a matrix
+ static BoundingRegion transformBoundingRegion(BoundingRegion region, glm::mat4 transformationMatrix, glm::vec3 basis=glm::vec3(1.0f, 1.0f, 1.0f));
+};
+
+const static BoundingRegion OBJECT_BOUNDS{
+ glm::vec4(.5f, 0.f, 0.f, 1.f),
+ glm::vec4(-.5f, 0.f, 0.f, 1.f),
+ glm::vec4(0.f, .5f, 0.f, 1.f),
+ glm::vec4(0.f, -.5f, 0.f, 1.f),
+ glm::vec4(0.f, 0.f, .5f, 1.f),
+ glm::vec4(0.f, 0.f, -.5f, 1.f),
+ glm::vec4(0.f, 0.f, 0.f, 1.f)
+};
+
+
+#endif // KDTREE_H \ No newline at end of file
diff --git a/src/accelerate/myqtconcurrent.cpp b/src/accelerate/myqtconcurrent.cpp
new file mode 100644
index 0000000..1dff0e0
--- /dev/null
+++ b/src/accelerate/myqtconcurrent.cpp
@@ -0,0 +1,80 @@
+
+#include <QList>
+#include <QtConcurrent>
+#include "raytracer/raytracer.h"
+
+struct pixelRoutineArgs {
+ glm::vec4 pCamera;
+ glm::vec4 dCamera;
+ const RayTraceScene &scene;
+ RayTracer rt;
+};
+static RGBA pixelRoutine(pixelRoutineArgs args);
+
+void RayTracer::renderParallel(RGBA *imageData, const RayTraceScene &scene)
+{
+ Camera camera = scene.getCamera();
+ float cameraDepth = 1.f;
+ float viewplaneHeight = 2.f*cameraDepth*std::tan(camera.getHeightAngle() / 2.f);
+ float viewplaneWidth = cameraDepth*viewplaneHeight*((float)scene.width()/(float)scene.height());
+
+ QList<pixelRoutineArgs> l{};
+ for (int imageRow = 0; imageRow < scene.height(); imageRow++) {
+ for (int imageCol = 0; imageCol < scene.width(); imageCol++) {
+ float xCameraSpace = viewplaneWidth *
+ (-.5f + (imageCol + .5f) / scene.width());
+ float yCameraSpace = viewplaneHeight *
+ (-.5f + (imageRow + .5f) / scene.height());
+
+ glm::vec4 pixelDirCamera{xCameraSpace, -yCameraSpace, -cameraDepth, 0.f}; //w=0 for dir
+ glm::vec4 eyeCamera{0.f, 0.f, 0.f, 1.f}; // w=1.f for point
+ l.append({
+ eyeCamera, // eye
+ pixelDirCamera, // direction
+ scene,
+ *this
+ });
+
+ }
+ }
+ QList<RGBA> pixels = QtConcurrent::blockingMapped(l, pixelRoutine);
+ QtConcurrent::blockingMap(l, pixelRoutine);
+ int index = 0;
+ for (RGBA p : pixels) {
+ imageData[index++] = p;
+ }
+
+ if (m_config.enableAntiAliasing)
+ {
+ filterBlur(imageData, scene.width(), scene.height());
+ }
+}
+
+
+RGBA pixelRoutine(pixelRoutineArgs args)
+{
+ auto eyeCamera = args.pCamera;
+ auto pixelDirCamera = args.dCamera;
+ auto scene = args.scene;
+ auto rt = args.rt;
+
+ // convert camera space to world space
+ auto inv = scene.getCamera().getInverseViewMatrix();
+ glm::vec4 pWorld = inv * eyeCamera;
+ glm::vec4 dWorld = glm::normalize(inv * pixelDirCamera);
+
+ if (rt.m_config.enableDepthOfField)
+ {
+ // if we're doing depth of field, we need to shoot multiple rays, see camera.cpp
+ return RayTracer::toRGBA(rt.secondaryRays(pWorld, dWorld, scene));
+ }
+ if (rt.m_config.enableSuperSample)
+ {
+ // if we're doing super sampling, we need to shoot multiple rays, see raytracer.cpp
+ return rt.superSample(eyeCamera, pixelDirCamera, scene);
+ }
+
+ // shoot ray!
+ RGBA pixel = RayTracer::toRGBA(rt.getPixelFromRay(pWorld, dWorld, scene, 0));
+ return pixel;
+}
diff --git a/src/accelerate/myqthreads.cpp b/src/accelerate/myqthreads.cpp
new file mode 100644
index 0000000..ead3aec
--- /dev/null
+++ b/src/accelerate/myqthreads.cpp
@@ -0,0 +1,130 @@
+#include "raytracer/raytracer.h"
+#include <QThread>
+
+/**
+ * Extra credit -> own implementation of multithreading using QThreads.
+ * NOT USED for illuminate (not any faster than QT's version), but was used in intersect.
+ */
+
+//struct intersectRoutineArgs {
+// RenderShapeData shape;
+// glm::vec4 pWorld;
+// glm::vec4 dWorld;
+//};
+//
+//struct intersectData {
+// float distance;
+// glm::vec4 intersectionWorld;
+// glm::vec4 intersectionObj;
+// RenderShapeData intersectedShape;
+//};
+//
+//Q_DECLARE_METATYPE(intersectData);
+//
+//class IntersectWorker : public QThread
+//{
+// Q_OBJECT
+// void run() override {
+// exec();
+// /* ... here is the expensive or blocking operation ... */
+// glm::vec4 pObject = glm::inverse(a.shape.ctm) * a.pWorld;
+// glm::vec4 dObject = glm::normalize(glm::inverse(a.shape.ctm) * a.dWorld);
+//
+// glm::vec4 intersectionObj = RayTracer::findIntersection(pObject, dObject, a.shape);
+// if (intersectionObj.w == 0) // no hit
+// {
+// const intersectData response{
+// FINF,
+// glm::vec4(0.f),
+// glm::vec4(0.f),
+// a.shape
+// };
+// ps.append(response);
+// emit data(response);
+// } else {
+// auto intersectionWorld = a.shape.ctm * intersectionObj;
+// float distance = glm::distance(intersectionWorld, a.pWorld);
+//
+// const intersectData response{
+// distance,
+// intersectionWorld,
+// intersectionObj,
+// a.shape
+// };
+// ps.append(response);
+// emit data(response);
+// }
+// emit finished();
+// }
+//public:
+// intersectRoutineArgs a;
+// QList<intersectData> &ps;
+// IntersectWorker(intersectRoutineArgs args, QList<intersectData> &p) : ps(p)
+// {
+// a = args;
+// }
+// signals:
+// void data(const intersectData &s);
+// void finished();
+//};
+//
+//
+//class IntersectController : public QObject
+//{
+// Q_OBJECT
+//public:
+// std::vector<QThread*> qthreads;
+// QList<intersectData> intersectPoints;
+// IntersectController(const std::vector<RenderShapeData> &shapes, glm::vec4 pWorld, glm::vec4 dWorld) {
+// qRegisterMetaType<const intersectData&>("myType");
+// int id = 0;
+// for (const RenderShapeData &shape: shapes) {
+// const intersectRoutineArgs threadArgs{shape, pWorld, dWorld};
+// IntersectWorker *thread = new IntersectWorker(threadArgs, intersectPoints);
+//
+// connect(thread, &IntersectWorker::data, this, &IntersectController::addIntersectionPoint);
+// connect(thread, &IntersectWorker::finished, thread, &QThread::quit);
+//
+// connect(thread, &IntersectWorker::finished, thread, &QThread::deleteLater);
+//
+// qthreads.push_back(thread);
+// }
+// }
+// ~IntersectController() {
+// for (QThread* workerThread: qthreads) {
+// workerThread->exit();
+// }
+// qthreads.clear();
+// intersectPoints.clear();
+// }
+// void getClosestIntersection(float &minDist, glm::vec4 &closestIntersectionWorld, glm::vec4 &closestIntersectionObj, RenderShapeData intersectedShape) {
+// // start then wait
+// for (QThread* thread: qthreads) {
+// thread->start();
+// }
+// for (QThread* thread: qthreads) {
+// thread->quit();
+// thread->wait();
+// }
+//
+//
+// // once all threads are done, find the closest
+// for (const intersectData &i : intersectPoints) {
+// if (i.distance < minDist) {
+// minDist = i.distance;
+//
+// intersectedShape = i.intersectedShape;
+// closestIntersectionObj = i.intersectionObj;
+// closestIntersectionWorld = i.intersectionWorld;
+// }
+// }
+//}
+//public slots:
+// void addIntersectionPoint(const intersectData &s) {
+// intersectPoints.append(s);
+// }
+// signals:
+// void operate(intersectRoutineArgs a);
+//};
+//
+//#include "myqthreads.moc" \ No newline at end of file