From 0aa949c60b0b553aef15eccd1774ec98a574afdc Mon Sep 17 00:00:00 2001 From: Godzil Date: Wed, 11 Mar 2020 19:35:08 +0000 Subject: [PATCH] Continue working work optimiser. --- source/CMakeLists.txt | 2 +- source/include/group.h | 3 +- source/include/worldoptimiser.h | 22 +++- source/world.cpp | 5 +- source/worldbuilder/octreeoptimisation.cpp | 115 +++++++++++++++++++ source/worldoptimiser.cpp | 124 +++++++++++++++++++++ 6 files changed, 263 insertions(+), 8 deletions(-) create mode 100644 source/worldbuilder/octreeoptimisation.cpp create mode 100644 source/worldoptimiser.cpp diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index 2578a42..e6c030a 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -12,7 +12,7 @@ file(GLOB RAY_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/include/*.h ${CMAKE_CURRENT_SO ${CMAKE_CURRENT_SOURCE_DIR}/uvpattern/*.h) file(GLOB RAY_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/shapes/*.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/worldbuilder/*.cpp) + ${CMAKE_CURRENT_SOURCE_DIR}/worldbuilder/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/worldoptimiser/*.cpp) target_include_directories(rayonnement PUBLIC include pattern) diff --git a/source/include/group.h b/source/include/group.h index 09673fb..a816731 100644 --- a/source/include/group.h +++ b/source/include/group.h @@ -39,7 +39,8 @@ public: void addObject(Shape *s); void removeObject(Shape *s); - Shape *operator[](const int p) { return this->objectList[p]; }; + Shape *operator[](const int p) { return this->getObject(p); }; + Shape *getObject(const int p) { return this->objectList[p]; }; Shape *getUnboxable(const int p) { return this->unboxableObjectList[p]; }; Intersect intersect(Ray r); diff --git a/source/include/worldoptimiser.h b/source/include/worldoptimiser.h index 59100cf..4fb898e 100644 --- a/source/include/worldoptimiser.h +++ b/source/include/worldoptimiser.h @@ -11,25 +11,39 @@ #include -/* World Optimiser subclasses will created move objcet around to try to optimise the parsing of the world. +/* World Optimiser subclasses will created move objects around to try to optimise the raytrace of the world, to + * have as least as possible object to intersect per ray. * This class is abstract to we can implement different type and change at runtime or build time */ class WorldOptimiser { +protected: + Group *root; + void moveInfiniteObjects(Shape *s = nullptr); + void moveAllObjects(Shape *s = nullptr); public: - virtual void run(Group *root) = 0; + void setRoot(Group *root) { this->root = root; }; + virtual void run() = 0; }; class NoWorldOptimisation : public WorldOptimiser { public: - void run(Group *root) {}; + void run() {}; +}; + +class BVHOptimisation : public WorldOptimiser +{ +public: + void run(); }; class OctreeOptimisation : public WorldOptimiser { +private: + void makeTree(Group *leaf); public: - void run(Group *root) {}; + void run(); }; #endif /* DORAYME_WORLDOPTIMISER_H */ diff --git a/source/world.cpp b/source/world.cpp index c09e1ed..e6dbd88 100644 --- a/source/world.cpp +++ b/source/world.cpp @@ -189,11 +189,12 @@ Colour World::refractedColour(Computation comps, uint32_t depthCount) void World::finalise(WorldOptimiser &opt) { - /* First lock eveyrything */ + /* First lock everything */ this->worldGroup.lock(); /* Now run the optimiser */ - opt.run(&this->worldGroup); + opt.setRoot(&this->worldGroup); + opt.run(); } void World::dumpMe(FILE *fp) diff --git a/source/worldbuilder/octreeoptimisation.cpp b/source/worldbuilder/octreeoptimisation.cpp new file mode 100644 index 0000000..22f78ad --- /dev/null +++ b/source/worldbuilder/octreeoptimisation.cpp @@ -0,0 +1,115 @@ +/* + * DoRayMe - a quick and dirty Raytracer + * Octree world optimiser implementation + * + * Created by Manoël Trapier + * Copyright (c) 2020 986-Studio. + * + */ +#include + +void OctreeOptimisation::makeTree(Group *leaf) +{ + /* Let's take the bounding box of the root */ + BoundingBox rootBB = leaf->getBounds(); + + /* Take the mid value for each axes */ + double midX = (rootBB.max.x - rootBB.min.x) / 2.0 + rootBB.min.x; + double midY = (rootBB.max.y - rootBB.min.y) / 2.0 + rootBB.min.y; + double midZ = (rootBB.max.z - rootBB.min.z) / 2.0 + rootBB.min.z; + BoundingBox QuadrantBB[8]; + int quadrantIdx; + Group *Quadrants[8]; + + int i; + + if (leaf->getObjectCount() > 4) + { + /* Split the main bounding box into 8 boxes */ + QuadrantBB[0] | rootBB.min; + QuadrantBB[0] | Point(midX, midY, midZ); + + QuadrantBB[1] | Point(midX, rootBB.min.y, rootBB.min.z); + QuadrantBB[1] | Point(rootBB.max.x, midY, midZ); + + QuadrantBB[2] | Point(rootBB.min.x, midY, rootBB.min.z); + QuadrantBB[2] | Point(midX, rootBB.max.y, midZ); + + QuadrantBB[3] | Point(midX, midY, rootBB.min.z); + QuadrantBB[3] | Point(rootBB.max.x, rootBB.max.y, midZ); + + QuadrantBB[4] | Point(rootBB.min.x, midY, midZ); + QuadrantBB[4] | Point(midX, rootBB.max.y, rootBB.max.z); + + QuadrantBB[5] | Point(midX, midY, midZ); + QuadrantBB[5] | rootBB.max; + + QuadrantBB[6] | Point(rootBB.min.x, rootBB.min.y, midZ); + QuadrantBB[6] | Point(midX, midY, rootBB.max.z); + + QuadrantBB[7] | Point(midX, rootBB.min.y, midZ); + QuadrantBB[7] | Point(rootBB.max.x, midY, rootBB.max.z); + + for (quadrantIdx = 0 ; quadrantIdx < 8 ; quadrantIdx++) + { + Quadrants[quadrantIdx] = nullptr; + } + + for (i = 0 ; i < leaf->getObjectCount(); i++) + { + Shape *shp = leaf->getObject(i); + if ((shp->getType() != Shape::GROUP) && (shp->getType() != Shape::OBJFILE)) + { + BoundingBox objBB = shp->getBounds(); + + for (quadrantIdx = 0 ; quadrantIdx < 8 ; quadrantIdx++) + { + if (QuadrantBB[quadrantIdx].fitsIn(objBB)) + { + if (Quadrants[quadrantIdx] == nullptr) + { + char name[32]; + snprintf(name, 32, "Quadrant %d", quadrantIdx); + Quadrants[quadrantIdx] = new Group(name); + } + + Quadrants[quadrantIdx]->addObject(shp); + leaf->removeObject(shp); + + i -= 1; + break; + } + } + } + else + { + leaf->removeObject(shp); + /* No cleanup for now, it's bad, I know */ + //delete shp; + i -= 1; + } + } + + /* Now add the quadrant to the root and recurse in it */ + for (quadrantIdx = 0 ; quadrantIdx < 8 ; quadrantIdx++) + { + if (Quadrants[quadrantIdx] != nullptr) + { + this->makeTree(Quadrants[quadrantIdx]); + leaf->addObject(Quadrants[quadrantIdx]); + } + } + } +} + +void OctreeOptimisation::run() +{ + /* First let's clear our hands */ + this->moveInfiniteObjects(); + + /* Then let's have some fun! */ + this->moveAllObjects(); + + /* Now.. The fun start ! */ + makeTree(this->root); +} \ No newline at end of file diff --git a/source/worldoptimiser.cpp b/source/worldoptimiser.cpp new file mode 100644 index 0000000..09b2a4f --- /dev/null +++ b/source/worldoptimiser.cpp @@ -0,0 +1,124 @@ +/* + * DoRayMe - a quick and dirty Raytracer + * WorldOptimiser implementation + * + * Created by Manoël Trapier + * Copyright (c) 2020 986-Studio. + * + */ +#include +#include +#include +#include +#include + +/* This function is meant to move all infinite object to the root group */ +void WorldOptimiser::moveInfiniteObjects(Shape *s) +{ + if (s == nullptr) + { + s = this->root; + } + + if (s->getType() == Shape::OBJFILE) + { + /* Special case */ + OBJFile *obj = (OBJFile *)s; + s = obj->getBaseGroup(); + } + + + if (s->getType() == Shape::GROUP) + { + int i; + Group *grp = (Group *)s; + + if (grp->getUnboxableCount() > 0) + { + for(i = 0; i < grp->getUnboxableCount(); i++) + { + Shape *shp = grp->getUnboxable(i); + + if (this->root != s) + { + if (shp->getType() == Shape::GROUP) + { + /* Issue a warning if it is a group */ + printf("WARNING: The group '%s' in '%s' have infinite bounds, all items part of it will not be optimised." + "That may affect performances!", + ((Group *)shp)->getName(), + grp->getName()); + } + + this->root->addObject(shp); + grp->removeObject(shp); + + /* We remove an object from that list, so need to do some stuffs. */ + i -= 1; + } + } + } + + /* Now let's traverse the rest of that group */ + if (grp->getObjectCount() > 0) + { + for(i = 0; i < grp->getObjectCount(); i++) + { + Shape *shp = grp->getObject(i); + this->moveInfiniteObjects(shp); + } + } + } + /* If it is not a group, there is nothing to be done there. */ +} + +void WorldOptimiser::moveAllObjects(Shape *s) +{ + if (s == nullptr) + { + s = this->root; + } + + if (s->getType() == Shape::OBJFILE) + { + /* Special case */ + OBJFile *obj = (OBJFile *)s; + s = obj->getBaseGroup(); + } + + /* We should be here only when it is a group, but better being safe. */ + if (s->getType() == Shape::GROUP) + { + int i; + Group *grp = (Group *)s; + + /* Now let's traverse the rest of that group */ + if (grp->getObjectCount() > 0) + { + for(i = 0; i < grp->getObjectCount(); i++) + { + Shape *shp = grp->getObject(i); + switch(shp->getType()) + { + default: + /* Don't move if we are on the same leaf */ + if (this->root != s) + { + /* It is not a group type object so, move it! */ + this->root->addObject(shp); + grp->removeObject(shp); + + /* We remove an object from that list, so need to do some stuffs. */ + i -= 1; + } + break; + + case Shape::GROUP: + case Shape::OBJFILE: + this->moveAllObjects(shp); + } + } + } + } + /* If it is not a group, there is nothing to be done there. */ +} \ No newline at end of file