sphere_tracing_example.cpp

Back to KdTree class

example/PastelExample/

// Description: Sphere tracing example
// DocumentationOf: kdtree.h

#include "pastel_example.h"

#include "pastel/geometry/kdtree.h"
#include "pastel/geometry/kdtree_tools.h"
#include "pastel/geometry/intersect_line_sphere.h"
#include "pastel/geometry/intersect_line_alignedbox.h"

#include "pastel/math/uniform_sampling.h"
#include "pastel/math/smoothstep.h"

#include "pastel/gfx/color_tools.h"
#include "pastel/gfx/savepcx.h"
#include "pastel/gfx/color_palette.h"

#include "pastel/sys/array.h"
#include "pastel/sys/tristate.h"
#include "pastel/sys/pool_allocator.h"

#include <iostream>
#include <string>

using namespace Pastel;

namespace
{

    class SpherePolicy
    {
    public:
        typedef Sphere3* Object;

        AlignedBox3 bound(const Sphere3* object) const
        {
            Sphere3 temp = *object;
            temp.setRadius(temp.radius() * 1.1);
            return boundingAlignedBox(temp);

            return boundingAlignedBox(*object);
        }

        Tuple<real, 2> bound(const Sphere3* object, integer axis) const
        {
            return Tuple<real, 2>(
                object->position()[axis] - object->radius(),
                object->position()[axis] + object->radius());
        }
    };

    bool sphereIntersector(const Sphere3* object,
        const Line3& ray, real& t)
    {
        const AlignedBox3 box = boundingAlignedBox(*object);

        /*
       Vector2 hitList;
       if (!intersect(ray, box, hitList) ||
           hitList[1] < 0)
       {
           return false;
       }
       */

        Vector2 hitList;
        if (!intersect(ray, *object, hitList) ||
            hitList[1] < 0)
        {
            return false;
        }

        if (hitList[0] > 0)
        {
            t = hitList[0];
        }
        else
        {
            t = hitList[1];
        }

        return true;
    }

    void testSphereTracing()
    {
        typedef KdTree<real, 3, SpherePolicy> Tree;
        typedef Tree::ConstObjectIterator ConstObjectIterator;

        SpherePolicy spherePolicy;
        Tree kdTree(3, spherePolicy);
        const integer spheres = 10000;

        PoolAllocator allocator(sizeof(Sphere3));

        std::vector<Sphere3*> sphereList;
        sphereList.reserve(spheres);

        for (integer i = 0;i < spheres;++i)
        {
            Sphere3* sphere = (Sphere3*)allocator.allocate();
            new(sphere) Sphere3(Vector3(1 * randomVectorBall<real, 3>()),
                0.01);

            sphereList.push_back(sphere);
        }

        kdTree.reserveBound(AlignedBox3(Vector3(-10), Vector3(10)));
        kdTree.insert(sphereList.begin(), sphereList.end());

        std::cout << "The kdTree has " << std::endl;
        std::cout << kdTree.nodes() << " nodes" << std::endl;
        std::cout << "Depth of " << depth(kdTree) << std::endl;
        std::cout << kdTree.objects() << " object references ("
            << (real)kdTree.objects() / kdTree.leaves() << " per leaf on average)." << std::endl;

        refineSurfaceAreaHeuristic(computeKdTreeMaxDepth(kdTree.objects()), 2, kdTree);
        //refineSurfaceAreaHeuristic(0, 2, kdTree);

        kdTree.reserveBound(AlignedBox3(kdTree.bound().min() - 1, kdTree.bound().max() + 1));

        REPORT(!check(kdTree));

        std::cout << "The kdTree has " << std::endl;
        std::cout << kdTree.nodes() << " nodes" << std::endl;
        std::cout << "Of which " << kdTree.leaves() << " are leaf nodes." << std::endl;
        std::cout << "Depth of " << depth(kdTree) << std::endl;
        std::cout << kdTree.objects() << " object references ("
            << (real)kdTree.objects() / kdTree.leaves() << " per leaf on average)." << std::endl;

        std::vector<Color> palette;
        falseColorPalette(palette, 256);

        const integer width = 800;
        const integer height = 800;

        Array<Color, 2> image(Vector2i(width, height), Color(0));

        integer reportedProgress = -1;

        for (integer y = 0;y < height;++y)
        {
            const integer progress = 100 * ((real)y / (height - 1));
            if (progress != reportedProgress)
            {
                std::cout << progress << "% ";
                reportedProgress = progress;
            }
            for (integer x = 0;x < width;++x)
            {
                const Line3 ray(
                    Vector3(
                    2 * ((real)(x + 0.5) / (width - 1)) - 1,
                    2 * ((real)(y + 0.5) / (height - 1)) - 1,
                    -2),
                    Vector3(0, 0, 1));

                const ConstObjectIterator iter =
                    recursiveRayTraversal(kdTree, ray, sphereIntersector);
                if (iter != kdTree.end())
                {
                    image(x, y) = linear(Color(1), evaluate(0.5 * Color(0.5, 0, 1)), cubicSmoothStep(norm((*iter)->position())));
                    //image(x, y) = palette[(uint8)((pointer_integer)(*iter) % 256)];
                    //image(x, y) = Color(1);
                }
            }
        }

        std::cout << std::endl;

        allocator.clear();

        savePcx(image, "spheretracing.pcx");
    }

    void addTest()
    {
        testRunner().add("Sphere tracing", testSphereTracing);
    }

    CallFunction run(addTest);

}