<commit_before>// This file is distributed under the MIT license.
// See the LICENSE file for details.

#include <common/config.h>

#include <algorithm>
#include <cassert>
#include <chrono>
#include <cmath>
#include <exception>
#include <fstream>
#include <future>
#include <iomanip>
#include <iostream>
#include <istream>
#include <iterator>
#include <map>
#include <memory>
#include <mutex>
#include <new>
#include <ostream>
#include <set>
#include <sstream>
#include <string>
#include <thread>

#include <boost/filesystem.hpp>

#include <imgui.h>

#include <Support/CmdLine.h>
#include <Support/CmdLineUtil.h>

#include <visionaray/gl/bvh_outline_renderer.h>
#include <visionaray/gl/debug_callback.h>
#include <visionaray/math/math.h>
#include <visionaray/texture/texture.h>
#include <visionaray/aligned_vector.h>
#include <visionaray/area_light.h>
#include <visionaray/bvh.h>
#include <visionaray/generic_material.h>
#include <visionaray/kernels.h>
#include <visionaray/material.h>
#include <visionaray/pinhole_camera.h>
#include <visionaray/point_light.h>
#include <visionaray/scheduler.h>
#include <visionaray/thin_lens_camera.h>

#if defined(__INTEL_COMPILER) || defined(__MINGW32__) || defined(__MINGW64__)
#include <visionaray/detail/tbb_sched.h>
#endif

#include <common/manip/arcball_manipulator.h>
#include <common/manip/pan_manipulator.h>
#include <common/manip/zoom_manipulator.h>
#include <common/make_materials.h>
#include <common/model.h>
#include <common/sg.h>
#include <common/timer.h>
#include <common/viewer_glut.h>

#ifdef __CUDACC__
#include <common/cuda.h>
#endif

#if VSNRAY_COMMON_HAVE_PTEX
#include <common/ptex.h>
#endif

#include "call_kernel.h"
#include "host_device_rt.h"
#include "render.h"


using namespace visionaray;

using viewer_type = viewer_glut;


//-------------------------------------------------------------------------------------------------
// Renderer, stores state, geometry, normals, ...
//

struct renderer : viewer_type
{
    using primitive_type            = model::triangle_type;
    using normal_type               = model::normal_type;
    using tex_coord_type            = model::tex_coord_type;
    using host_bvh_type             = index_bvh<primitive_type>;
#ifdef __CUDACC__
    using device_bvh_type           = cuda_index_bvh<primitive_type>;
    using device_tex_type           = cuda_texture<vector<4, unorm<8>>, 2>;
    using device_tex_ref_type       = typename device_tex_type::ref_type;
#endif

    enum bvh_build_strategy
    {
        Binned = 0,  // Binned SAH builder, no spatial splits
        Split        // Split BVH, also binned and with SAH
    };

    enum texture_format { Ptex, UV };

    renderer()
        : viewer_type(800, 800, "Visionaray Viewer")
        , host_sched(std::thread::hardware_concurrency())
        , rt(
            host_device_rt::CPU,
            true /* double buffering */,
            true /* direct rendering */,
            host_device_rt::SRGB
            )
#ifdef __CUDACC__
        , device_sched(8, 8)
#endif
        , mouse_pos(0)
    {
        using namespace support;

        add_cmdline_option( cl::makeOption<std::set<std::string>&>(
            cl::Parser<>(),
            "filenames",
            cl::Desc("Input files in wavefront obj format"),
            cl::Positional,
            cl::OneOrMore,
            cl::init(filenames)
            ) );

        add_cmdline_option( cl::makeOption<std::string&>(
            cl::Parser<>(),
            "camera",
            cl::Desc("Text file with camera parameters"),
            cl::ArgRequired,
            cl::init(this->initial_camera)
            ) );

        add_cmdline_option( cl::makeOption<algorithm&>({
                { "simple",             Simple,         "Simple ray casting kernel" },
                { "whitted",            Whitted,        "Whitted style ray tracing kernel" },
                { "pathtracing",        Pathtracing,    "Pathtracing global illumination kernel" }
            },
            "algorithm",
            cl::Desc("Rendering algorithm"),
            cl::ArgRequired,
            cl::init(this->algo)
            ) );

        add_cmdline_option( cl::makeOption<bvh_build_strategy&>({
                { "default",            Binned,         "Binned SAH" },
                { "split",              Split,          "Binned SAH with spatial splits" }
            },
            "bvh",
            cl::Desc("BVH build strategy"),
            cl::ArgRequired,
            cl::init(this->builder)
            ) );

        add_cmdline_option( cl::makeOption<unsigned&>({
                { "1",      1,      "1x supersampling" },
                { "2",      2,      "2x supersampling" },
                { "4",      4,      "4x supersampling" },
                { "8",      8,      "8x supersampling" }
            },
            "ssaa",
            cl::Desc("Supersampling anti-aliasing factor"),
            cl::ArgRequired,
            cl::init(this->ssaa_samples)
            ) );

        add_cmdline_option( cl::makeOption<bool&>(
            cl::Parser<>(),
            "headlight",
            cl::Desc("Activate headlight"),
            cl::ArgRequired,
            cl::init(this->use_headlight)
            ) );

        add_cmdline_option( cl::makeOption<bool&>(
            cl::Parser<>(),
            "dof",
            cl::Desc("Activate depth of field"),
            cl::ArgRequired,
            cl::init(this->use_dof)
            ) );

        add_cmdline_option( cl::makeOption<unsigned&>(
            cl::Parser<>(),
            "bounces",
            cl::Desc("Number of bounces for recursive ray tracing"),
            cl::ArgRequired,
            cl::init(this->bounces)
            ) );

        add_cmdline_option( cl::makeOption<vec3&, cl::ScalarType>(
            [&](StringRef name, StringRef /*arg*/, vec3& value)
            {
                cl::Parser<>()(name + "-r", cmd_line_inst().bump(), value.x);
                cl::Parser<>()(name + "-g", cmd_line_inst().bump(), value.y);
                cl::Parser<>()(name + "-b", cmd_line_inst().bump(), value.z);
            },
            "ambient",
            cl::Desc("Ambient color"),
            cl::ArgDisallowed,
            cl::init(this->ambient)
            ) );

        add_cmdline_option( cl::makeOption<host_device_rt::color_space_type&>({
                { "rgb",  host_device_rt::RGB,  "RGB color space for display" },
                { "srgb", host_device_rt::SRGB, "sRGB color space for display" },
            },
            "colorspace",
            cl::Desc("Color space"),
            cl::ArgRequired,
            cl::init(rt.color_space())
            ) );

#ifdef __CUDACC__
        add_cmdline_option( cl::makeOption<host_device_rt::mode_type&>({
                { "cpu", host_device_rt::CPU, "Rendering on the CPU" },
                { "gpu", host_device_rt::GPU, "Rendering on the GPU" },
            },
            "device",
            cl::Desc("Rendering device"),
            cl::ArgRequired,
            cl::init(rt.mode())
            ) );
#endif
    }


    int                                         w               = 800;
    int                                         h               = 800;
    unsigned                                    frame_num       = 0;
    unsigned                                    bounces         = 0;
    unsigned                                    ssaa_samples    = 1;
    algorithm                                   algo            = Simple;
    bvh_build_strategy                          builder         = Binned;
    bool                                        use_headlight   = true;
    bool                                        use_dof         = false;
    bool                                        show_hud        = true;
    bool                                        show_hud_ext    = true;
    bool                                        show_bvh        = false;


    std::set<std::string>                       filenames;
    std::string                                 initial_camera;

    model                                       mod;
    vec3                                        ambient         = vec3(-1.0f);

    index_bvh<host_bvh_type::bvh_inst>          host_top_level_bvh;
    aligned_vector<host_bvh_type>               host_bvhs;
    aligned_vector<host_bvh_type::bvh_inst>     host_instances;
    aligned_vector<plastic<float>>              plastic_materials;
    aligned_vector<generic_material_t>          generic_materials;
    aligned_vector<point_light<float>>          point_lights;
    aligned_vector<area_light<float,
                   basic_triangle<3, float>>>   area_lights;
#if VSNRAY_COMMON_HAVE_PTEX
    aligned_vector<ptex::face_id_t>             ptex_tex_coords;
    aligned_vector<ptex::texture>               ptex_textures;
#endif
#ifdef __CUDACC__
    device_bvh_type                             device_bvh;
    thrust::device_vector<normal_type>          device_geometric_normals;
    thrust::device_vector<normal_type>          device_shading_normals;
    thrust::device_vector<tex_coord_type>       device_tex_coords;
    thrust::device_vector<plastic<float>>       device_plastic_materials;
    thrust::device_vector<generic_material_t>   device_generic_materials;
    std::map<std::string, device_tex_type>      device_texture_map;
    thrust::device_vector<device_tex_ref_type>  device_textures;
#endif

    host_sched_t<ray_type_cpu>                  host_sched;
    host_device_rt                              rt;
#ifdef __CUDACC__
    cuda_sched<ray_type_gpu>                    device_sched;
#endif
    thin_lens_camera                            cam;

    std::shared_ptr<visionaray::texture<vec4, 2>>
                                                environment_map = nullptr;


    // List of cameras, e.g. read from scene graph
    aligned_vector<std::pair<
            std::string, thin_lens_camera>>     cameras;

    mouse::pos                                  mouse_pos;

    texture_format                              tex_format = UV;

    visionaray::frame_counter                   counter;
    double                                      last_frame_time = 0.0;
    gl::bvh_outline_renderer                    outlines;
    gl::debug_callback                          gl_debug_callback;

    bool                                        render_async  = false;
    std::future<void>                           render_future;
    std::mutex                                  display_mutex;

    void build_bvhs();

protected:

    void on_close();
    void on_display();
    void on_key_press(visionaray::key_event const& event);
    void on_mouse_move(visionaray::mouse_event const& event);
    void on_resize(int w, int h);

private:

    void load_camera(std::string filename);
    void clear_frame();
    void render_hud();
    void render_impl();

};


//-------------------------------------------------------------------------------------------------
// I/O utility for camera lookat only - not fit for the general case!
//

std::istream& operator>>(std::istream& in, pinhole_camera& cam)
{
    vec3 eye;
    vec3 center;
    vec3 up;

    in >> eye >> std::ws >> center >> std::ws >> up >> std::ws;
    cam.look_at(eye, center, up);

    return in;
}

std::ostream& operator<<(std::ostream& out, pinhole_camera const& cam)
{
    out << cam.eye() << '\n';
    out << cam.center() << '\n';
    out << cam.up() << '\n';
    return out;
}


//-------------------------------------------------------------------------------------------------
// Reset triangle mesh flags to 0
//

struct reset_flags_visitor : sg::node_visitor
{
    using node_visitor::apply;

    void apply(sg::surface_properties& sp)
    {
        sp.flags() = 0;

        node_visitor::apply(sp);
    }

    void apply(sg::triangle_mesh& tm)
    {
        tm.flags() = 0;

        node_visitor::apply(tm);
    }

    void apply(sg::indexed_triangle_mesh& itm)
    {
        itm.flags() = 0;

        node_visitor::apply(itm);
    }
};


//-------------------------------------------------------------------------------------------------
// Traverse the scene graph to construct geometry, materials and BVH instances
//

struct build_bvhs_visitor : sg::node_visitor
{
    using node_visitor::apply;

    build_bvhs_visitor(
            aligned_vector<renderer::host_bvh_type>& bvhs,
            aligned_vector<size_t>& instance_indices,
            aligned_vector<mat4>& instance_transforms,
            aligned_vector<vec3>& shading_normals,
            aligned_vector<vec3>& geometric_normals,
            aligned_vector<vec2>& tex_coords,
            aligned_vector<vec3>& colors,
#if VSNRAY_COMMON_HAVE_PTEX
            aligned_vector<ptex::face_id_t>& face_ids,
#endif
            aligned_vector<std::pair<std::string, thin_lens_camera>>& cameras
            )
        : bvhs_(bvhs)
        , instance_indices_(instance_indices)
        , instance_transforms_(instance_transforms)
        , shading_normals_(shading_normals)
        , geometric_normals_(geometric_normals)
        , tex_coords_(tex_coords)
        , colors_(colors)
#if VSNRAY_COMMON_HAVE_PTEX
        , face_ids_(face_ids)
#endif
        , cameras_(cameras)
        , environment_map(nullptr)
    {
    }

    void apply(sg::camera& c)
    {
        cameras_.push_back(std::make_pair(c.name(), static_cast<thin_lens_camera>(c)));

        node_visitor::apply(c);
    }

    void apply(sg::environment_light& el)
    {
        auto tex = std::dynamic_pointer_cast<sg::texture2d<vec4>>(el.texture());

        if (tex != nullptr)
        {
            environment_map = std::make_shared<visionaray::texture<vec4, 2>>(tex->width(), tex->height());
            environment_map->set_address_mode(tex->get_address_mode());
            environment_map->set_filter_mode(tex->get_filter_mode());
            environment_map->reset(tex->data());
        }

        node_visitor::apply(el);
    }

    void apply(sg::transform& t)
    {
        mat4 prev = current_transform_;

        current_transform_ = current_transform_ * t.matrix();

        node_visitor::apply(t);

        current_transform_ = prev;
    }

    void apply(sg::surface_properties& sp)
    {
        unsigned prev = current_geom_id_;

        if (sp.flags() == 0 && sp.material() && sp.textures().size() > 0)
        {
            std::shared_ptr<sg::material> material = sp.material();
            std::shared_ptr<sg::texture> texture = sp.textures()[0];

            auto surf = std::make_pair(material, texture);

            auto it = std::find(surfaces.begin(), surfaces.end(), surf);
            if (it == surfaces.end())
            {
                current_geom_id_ = static_cast<unsigned>(surfaces.size());
                surfaces.push_back(surf);
            }
            else
            {
                current_geom_id_ = static_cast<unsigned>(std::distance(surfaces.begin(), it));
            }

            sp.flags() = ~sp.flags();
        }

        node_visitor::apply(sp);

        current_geom_id_ = prev;
    }

    void apply(sg::triangle_mesh& tm)
    {
        if (tm.flags() == 0 && tm.vertices.size() > 0)
        {
            assert(tm.vertices.size() % 3 == 0);

            aligned_vector<basic_triangle<3, float>> triangles(tm.vertices.size() / 3);

            size_t first_geometric_normal = geometric_normals_.size();
            geometric_normals_.resize(geometric_normals_.size() + tm.vertices.size() / 3);

            shading_normals_.insert(shading_normals_.end(), tm.normals.begin(), tm.normals.end());

            tex_coords_.insert(tex_coords_.end(), tm.tex_coords.begin(), tm.tex_coords.end());

            size_t first_color = colors_.size();
            if (tm.colors.size() > 0)
            {
                colors_.resize(first_color + tm.colors.size());
                for (size_t i = 0; i < tm.colors.size(); ++i)
                {
                    colors_[first_color + i] = vec3(tm.colors[i]);
                }
            }

#if VSNRAY_COMMON_HAVE_PTEX
            face_ids_.insert(face_ids_.end(), tm.face_ids.begin(), tm.face_ids.end());
#endif

            for (size_t i = 0; i < tm.vertices.size(); i += 3)
            {
                vec3 v1 = tm.vertices[i];
                vec3 v2 = tm.vertices[i + 1];
                vec3 v3 = tm.vertices[i + 2];

                basic_triangle<3, float> tri(v1, v2 - v1, v3 - v1);
                tri.prim_id = current_prim_id_++;
                tri.geom_id = current_geom_id_;
                triangles[i / 3] = tri;

                vec3 gn = normalize(cross(v2 - v1, v3 - v1));

                geometric_normals_[first_geometric_normal + i / 3] = gn;
            }

            // Build single bvh
            bvhs_.emplace_back(build<renderer::host_bvh_type>(
                    triangles.data(),
                    triangles.size(),
                    false//builder == Split
                    ));

            tm.flags() = ~(bvhs_.size() - 1);
        }

        instance_indices_.push_back(~tm.flags());
        instance_transforms_.push_back(current_transform_);

        node_visitor::apply(tm);
    }

    void apply(sg::indexed_triangle_mesh& itm)
    {
        if (itm.flags() == 0 && itm.vertices.size() > 0)
        {
            assert(itm.indices.size() % 3 == 0);

            aligned_vector<basic_triangle<3, float>> triangles(itm.indices.size() / 3);

            size_t first_geometric_normal = geometric_normals_.size();
            geometric_normals_.resize(geometric_normals_.size() + itm.indices.size() / 3);

            shading_normals_.insert(shading_normals_.end(), itm.normals.begin(), itm.normals.end());

            tex_coords_.insert(tex_coords_.end(), itm.tex_coords.begin(), itm.tex_coords.end());

            size_t first_color = colors_.size();
            if (itm.colors.size() > 0)
            {
                colors_.resize(first_color + itm.colors.size());
                for (size_t i = 0; i < itm.colors.size(); ++i)
                {
                    colors_[first_color + i] = vec3(itm.colors[i]);
                }
            }

            for (size_t i = 0; i < itm.indices.size(); i += 3)
            {
                vec3 v1 = itm.vertices[itm.indices[i]];
                vec3 v2 = itm.vertices[itm.indices[i + 1]];
                vec3 v3 = itm.vertices[itm.indices[i + 2]];

                basic_triangle<3, float> tri(v1, v2 - v1, v3 - v1);
                tri.prim_id = current_prim_id_++;
                tri.geom_id = current_geom_id_;
                triangles[i / 3] = tri;

                vec3 gn = normalize(cross(v2 - v1, v3 - v1));

                geometric_normals_[first_geometric_normal + i / 3] = gn;
            }<commit_msg>Correctly process index_triangle_mesh objects<commit_after>// This file is distributed under the MIT license.
// See the LICENSE file for details.

#include <common/config.h>

#include <algorithm>
#include <cassert>
#include <chrono>
#include <cmath>
#include <exception>
#include <fstream>
#include <future>
#include <iomanip>
#include <iostream>
#include <istream>
#include <iterator>
#include <map>
#include <memory>
#include <mutex>
#include <new>
#include <ostream>
#include <set>
#include <sstream>
#include <string>
#include <thread>

#include <boost/filesystem.hpp>

#include <imgui.h>

#include <Support/CmdLine.h>
#include <Support/CmdLineUtil.h>

#include <visionaray/gl/bvh_outline_renderer.h>
#include <visionaray/gl/debug_callback.h>
#include <visionaray/math/math.h>
#include <visionaray/texture/texture.h>
#include <visionaray/aligned_vector.h>
#include <visionaray/area_light.h>
#include <visionaray/bvh.h>
#include <visionaray/generic_material.h>
#include <visionaray/kernels.h>
#include <visionaray/material.h>
#include <visionaray/pinhole_camera.h>
#include <visionaray/point_light.h>
#include <visionaray/scheduler.h>
#include <visionaray/thin_lens_camera.h>

#if defined(__INTEL_COMPILER) || defined(__MINGW32__) || defined(__MINGW64__)
#include <visionaray/detail/tbb_sched.h>
#endif

#include <common/manip/arcball_manipulator.h>
#include <common/manip/pan_manipulator.h>
#include <common/manip/zoom_manipulator.h>
#include <common/make_materials.h>
#include <common/model.h>
#include <common/sg.h>
#include <common/timer.h>
#include <common/viewer_glut.h>

#ifdef __CUDACC__
#include <common/cuda.h>
#endif

#if VSNRAY_COMMON_HAVE_PTEX
#include <common/ptex.h>
#endif

#include "call_kernel.h"
#include "host_device_rt.h"
#include "render.h"


using namespace visionaray;

using viewer_type = viewer_glut;


//-------------------------------------------------------------------------------------------------
// Renderer, stores state, geometry, normals, ...
//

struct renderer : viewer_type
{
    using primitive_type            = model::triangle_type;
    using normal_type               = model::normal_type;
    using tex_coord_type            = model::tex_coord_type;
    using host_bvh_type             = index_bvh<primitive_type>;
#ifdef __CUDACC__
    using device_bvh_type           = cuda_index_bvh<primitive_type>;
    using device_tex_type           = cuda_texture<vector<4, unorm<8>>, 2>;
    using device_tex_ref_type       = typename device_tex_type::ref_type;
#endif

    enum bvh_build_strategy
    {
        Binned = 0,  // Binned SAH builder, no spatial splits
        Split        // Split BVH, also binned and with SAH
    };

    enum texture_format { Ptex, UV };

    renderer()
        : viewer_type(800, 800, "Visionaray Viewer")
        , host_sched(std::thread::hardware_concurrency())
        , rt(
            host_device_rt::CPU,
            true /* double buffering */,
            true /* direct rendering */,
            host_device_rt::SRGB
            )
#ifdef __CUDACC__
        , device_sched(8, 8)
#endif
        , mouse_pos(0)
    {
        using namespace support;

        add_cmdline_option( cl::makeOption<std::set<std::string>&>(
            cl::Parser<>(),
            "filenames",
            cl::Desc("Input files in wavefront obj format"),
            cl::Positional,
            cl::OneOrMore,
            cl::init(filenames)
            ) );

        add_cmdline_option( cl::makeOption<std::string&>(
            cl::Parser<>(),
            "camera",
            cl::Desc("Text file with camera parameters"),
            cl::ArgRequired,
            cl::init(this->initial_camera)
            ) );

        add_cmdline_option( cl::makeOption<algorithm&>({
                { "simple",             Simple,         "Simple ray casting kernel" },
                { "whitted",            Whitted,        "Whitted style ray tracing kernel" },
                { "pathtracing",        Pathtracing,    "Pathtracing global illumination kernel" }
            },
            "algorithm",
            cl::Desc("Rendering algorithm"),
            cl::ArgRequired,
            cl::init(this->algo)
            ) );

        add_cmdline_option( cl::makeOption<bvh_build_strategy&>({
                { "default",            Binned,         "Binned SAH" },
                { "split",              Split,          "Binned SAH with spatial splits" }
            },
            "bvh",
            cl::Desc("BVH build strategy"),
            cl::ArgRequired,
            cl::init(this->builder)
            ) );

        add_cmdline_option( cl::makeOption<unsigned&>({
                { "1",      1,      "1x supersampling" },
                { "2",      2,      "2x supersampling" },
                { "4",      4,      "4x supersampling" },
                { "8",      8,      "8x supersampling" }
            },
            "ssaa",
            cl::Desc("Supersampling anti-aliasing factor"),
            cl::ArgRequired,
            cl::init(this->ssaa_samples)
            ) );

        add_cmdline_option( cl::makeOption<bool&>(
            cl::Parser<>(),
            "headlight",
            cl::Desc("Activate headlight"),
            cl::ArgRequired,
            cl::init(this->use_headlight)
            ) );

        add_cmdline_option( cl::makeOption<bool&>(
            cl::Parser<>(),
            "dof",
            cl::Desc("Activate depth of field"),
            cl::ArgRequired,
            cl::init(this->use_dof)
            ) );

        add_cmdline_option( cl::makeOption<unsigned&>(
            cl::Parser<>(),
            "bounces",
            cl::Desc("Number of bounces for recursive ray tracing"),
            cl::ArgRequired,
            cl::init(this->bounces)
            ) );

        add_cmdline_option( cl::makeOption<vec3&, cl::ScalarType>(
            [&](StringRef name, StringRef /*arg*/, vec3& value)
            {
                cl::Parser<>()(name + "-r", cmd_line_inst().bump(), value.x);
                cl::Parser<>()(name + "-g", cmd_line_inst().bump(), value.y);
                cl::Parser<>()(name + "-b", cmd_line_inst().bump(), value.z);
            },
            "ambient",
            cl::Desc("Ambient color"),
            cl::ArgDisallowed,
            cl::init(this->ambient)
            ) );

        add_cmdline_option( cl::makeOption<host_device_rt::color_space_type&>({
                { "rgb",  host_device_rt::RGB,  "RGB color space for display" },
                { "srgb", host_device_rt::SRGB, "sRGB color space for display" },
            },
            "colorspace",
            cl::Desc("Color space"),
            cl::ArgRequired,
            cl::init(rt.color_space())
            ) );

#ifdef __CUDACC__
        add_cmdline_option( cl::makeOption<host_device_rt::mode_type&>({
                { "cpu", host_device_rt::CPU, "Rendering on the CPU" },
                { "gpu", host_device_rt::GPU, "Rendering on the GPU" },
            },
            "device",
            cl::Desc("Rendering device"),
            cl::ArgRequired,
            cl::init(rt.mode())
            ) );
#endif
    }


    int                                         w               = 800;
    int                                         h               = 800;
    unsigned                                    frame_num       = 0;
    unsigned                                    bounces         = 0;
    unsigned                                    ssaa_samples    = 1;
    algorithm                                   algo            = Simple;
    bvh_build_strategy                          builder         = Binned;
    bool                                        use_headlight   = true;
    bool                                        use_dof         = false;
    bool                                        show_hud        = true;
    bool                                        show_hud_ext    = true;
    bool                                        show_bvh        = false;


    std::set<std::string>                       filenames;
    std::string                                 initial_camera;

    model                                       mod;
    vec3                                        ambient         = vec3(-1.0f);

    index_bvh<host_bvh_type::bvh_inst>          host_top_level_bvh;
    aligned_vector<host_bvh_type>               host_bvhs;
    aligned_vector<host_bvh_type::bvh_inst>     host_instances;
    aligned_vector<plastic<float>>              plastic_materials;
    aligned_vector<generic_material_t>          generic_materials;
    aligned_vector<point_light<float>>          point_lights;
    aligned_vector<area_light<float,
                   basic_triangle<3, float>>>   area_lights;
#if VSNRAY_COMMON_HAVE_PTEX
    aligned_vector<ptex::face_id_t>             ptex_tex_coords;
    aligned_vector<ptex::texture>               ptex_textures;
#endif
#ifdef __CUDACC__
    device_bvh_type                             device_bvh;
    thrust::device_vector<normal_type>          device_geometric_normals;
    thrust::device_vector<normal_type>          device_shading_normals;
    thrust::device_vector<tex_coord_type>       device_tex_coords;
    thrust::device_vector<plastic<float>>       device_plastic_materials;
    thrust::device_vector<generic_material_t>   device_generic_materials;
    std::map<std::string, device_tex_type>      device_texture_map;
    thrust::device_vector<device_tex_ref_type>  device_textures;
#endif

    host_sched_t<ray_type_cpu>                  host_sched;
    host_device_rt                              rt;
#ifdef __CUDACC__
    cuda_sched<ray_type_gpu>                    device_sched;
#endif
    thin_lens_camera                            cam;

    std::shared_ptr<visionaray::texture<vec4, 2>>
                                                environment_map = nullptr;


    // List of cameras, e.g. read from scene graph
    aligned_vector<std::pair<
            std::string, thin_lens_camera>>     cameras;

    mouse::pos                                  mouse_pos;

    texture_format                              tex_format = UV;

    visionaray::frame_counter                   counter;
    double                                      last_frame_time = 0.0;
    gl::bvh_outline_renderer                    outlines;
    gl::debug_callback                          gl_debug_callback;

    bool                                        render_async  = false;
    std::future<void>                           render_future;
    std::mutex                                  display_mutex;

    void build_bvhs();

protected:

    void on_close();
    void on_display();
    void on_key_press(visionaray::key_event const& event);
    void on_mouse_move(visionaray::mouse_event const& event);
    void on_resize(int w, int h);

private:

    void load_camera(std::string filename);
    void clear_frame();
    void render_hud();
    void render_impl();

};


//-------------------------------------------------------------------------------------------------
// I/O utility for camera lookat only - not fit for the general case!
//

std::istream& operator>>(std::istream& in, pinhole_camera& cam)
{
    vec3 eye;
    vec3 center;
    vec3 up;

    in >> eye >> std::ws >> center >> std::ws >> up >> std::ws;
    cam.look_at(eye, center, up);

    return in;
}

std::ostream& operator<<(std::ostream& out, pinhole_camera const& cam)
{
    out << cam.eye() << '\n';
    out << cam.center() << '\n';
    out << cam.up() << '\n';
    return out;
}


//-------------------------------------------------------------------------------------------------
// Reset triangle mesh flags to 0
//

struct reset_flags_visitor : sg::node_visitor
{
    using node_visitor::apply;

    void apply(sg::surface_properties& sp)
    {
        sp.flags() = 0;

        node_visitor::apply(sp);
    }

    void apply(sg::triangle_mesh& tm)
    {
        tm.flags() = 0;

        node_visitor::apply(tm);
    }

    void apply(sg::indexed_triangle_mesh& itm)
    {
        itm.flags() = 0;

        node_visitor::apply(itm);
    }
};


//-------------------------------------------------------------------------------------------------
// Traverse the scene graph to construct geometry, materials and BVH instances
//

struct build_bvhs_visitor : sg::node_visitor
{
    using node_visitor::apply;

    build_bvhs_visitor(
            aligned_vector<renderer::host_bvh_type>& bvhs,
            aligned_vector<size_t>& instance_indices,
            aligned_vector<mat4>& instance_transforms,
            aligned_vector<vec3>& shading_normals,
            aligned_vector<vec3>& geometric_normals,
            aligned_vector<vec2>& tex_coords,
            aligned_vector<vec3>& colors,
#if VSNRAY_COMMON_HAVE_PTEX
            aligned_vector<ptex::face_id_t>& face_ids,
#endif
            aligned_vector<std::pair<std::string, thin_lens_camera>>& cameras
            )
        : bvhs_(bvhs)
        , instance_indices_(instance_indices)
        , instance_transforms_(instance_transforms)
        , shading_normals_(shading_normals)
        , geometric_normals_(geometric_normals)
        , tex_coords_(tex_coords)
        , colors_(colors)
#if VSNRAY_COMMON_HAVE_PTEX
        , face_ids_(face_ids)
#endif
        , cameras_(cameras)
        , environment_map(nullptr)
    {
    }

    void apply(sg::camera& c)
    {
        cameras_.push_back(std::make_pair(c.name(), static_cast<thin_lens_camera>(c)));

        node_visitor::apply(c);
    }

    void apply(sg::environment_light& el)
    {
        auto tex = std::dynamic_pointer_cast<sg::texture2d<vec4>>(el.texture());

        if (tex != nullptr)
        {
            environment_map = std::make_shared<visionaray::texture<vec4, 2>>(tex->width(), tex->height());
            environment_map->set_address_mode(tex->get_address_mode());
            environment_map->set_filter_mode(tex->get_filter_mode());
            environment_map->reset(tex->data());
        }

        node_visitor::apply(el);
    }

    void apply(sg::transform& t)
    {
        mat4 prev = current_transform_;

        current_transform_ = current_transform_ * t.matrix();

        node_visitor::apply(t);

        current_transform_ = prev;
    }

    void apply(sg::surface_properties& sp)
    {
        unsigned prev = current_geom_id_;

        if (sp.flags() == 0 && sp.material() && sp.textures().size() > 0)
        {
            std::shared_ptr<sg::material> material = sp.material();
            std::shared_ptr<sg::texture> texture = sp.textures()[0];

            auto surf = std::make_pair(material, texture);

            auto it = std::find(surfaces.begin(), surfaces.end(), surf);
            if (it == surfaces.end())
            {
                current_geom_id_ = static_cast<unsigned>(surfaces.size());
                surfaces.push_back(surf);
            }
            else
            {
                current_geom_id_ = static_cast<unsigned>(std::distance(surfaces.begin(), it));
            }

            sp.flags() = ~sp.flags();
        }

        node_visitor::apply(sp);

        current_geom_id_ = prev;
    }

    void apply(sg::triangle_mesh& tm)
    {
        if (tm.flags() == 0 && tm.vertices.size() > 0)
        {
            assert(tm.vertices.size() % 3 == 0);

            aligned_vector<basic_triangle<3, float>> triangles(tm.vertices.size() / 3);

            size_t first_geometric_normal = geometric_normals_.size();
            geometric_normals_.resize(geometric_normals_.size() + tm.vertices.size() / 3);

            shading_normals_.insert(shading_normals_.end(), tm.normals.begin(), tm.normals.end());

            tex_coords_.insert(tex_coords_.end(), tm.tex_coords.begin(), tm.tex_coords.end());

            size_t first_color = colors_.size();
            if (tm.colors.size() > 0)
            {
                colors_.resize(first_color + tm.colors.size());
                for (size_t i = 0; i < tm.colors.size(); ++i)
                {
                    colors_[first_color + i] = vec3(tm.colors[i]);
                }
            }

#if VSNRAY_COMMON_HAVE_PTEX
            face_ids_.insert(face_ids_.end(), tm.face_ids.begin(), tm.face_ids.end());
#endif

            for (size_t i = 0; i < tm.vertices.size(); i += 3)
            {
                vec3 v1 = tm.vertices[i];
                vec3 v2 = tm.vertices[i + 1];
                vec3 v3 = tm.vertices[i + 2];

                basic_triangle<3, float> tri(v1, v2 - v1, v3 - v1);
                tri.prim_id = current_prim_id_++;
                tri.geom_id = current_geom_id_;
                triangles[i / 3] = tri;

                vec3 gn = normalize(cross(v2 - v1, v3 - v1));

                geometric_normals_[first_geometric_normal + i / 3] = gn;
            }

            // Build single bvh
            bvhs_.emplace_back(build<renderer::host_bvh_type>(
                    triangles.data(),
                    triangles.size(),
                    false//builder == Split
                    ));

            tm.flags() = ~(bvhs_.size() - 1);
        }

        instance_indices_.push_back(~tm.flags());
        instance_transforms_.push_back(current_transform_);

        node_visitor::apply(tm);
    }

    void apply(sg::indexed_triangle_mesh& itm)
    {
        if (itm.flags() == 0 && itm.vertices.size() > 0)
        {
            assert(itm.indices.size() % 3 == 0);

            aligned_vector<basic_triangle<3, float>> triangles(itm.indices.size() / 3);

            size_t first_geometric_normal = geometric_normals_.size();
            geometric_normals_.resize(geometric_normals_.size() + itm.indices.size() / 3);

            size_t first_shading_normal = shading_normals_.size();
            if (itm.normals.size() > 0)
            {
                shading_normals_.resize(shading_normals_.size() + itm.indices.size());
            }

            size_t first_tex_coord = tex_coords_.size();
            if (itm.tex_coords.size() > 0)
            {
                tex_coords_.resize(tex_coords_.size() + itm.indices.size());
            }

            size_t first_color = colors_.size();
            if (itm.colors.size() > 0)
            {
                colors_.resize(first_color + itm.indices.size());
            }

            for (size_t i = 0; i < itm.indices.size(); i += 3)
            {
                vec3 v1 = itm.vertices[itm.indices[i]];
                vec3 v2 = itm.vertices[itm.indices[i + 1]];
                vec3 v3 = itm.vertices[itm.indices[i + 2]];

                basic_triangle<3, float> tri(v1, v2 - v1, v3 - v1);
                tri.prim_id = current_prim_id_++;
                tri.geom_id = current_geom_id_;
                triangles[i / 3] = tri;

                vec3 gn = normalize(cross(v2 - v1, v3 - v1));

                geometric_normals_[first_geometric_normal + i / 3] = gn;

                if (itm.normals.size() > 0)
                {
                    shading_normals_[first_shading_normal + i]     = itm.normals[itm.indices[i]];
                    shading_normals_[first_shading_normal + i + 1] = itm.normals[itm.indices[i + 1]];
                    shading_normals_[first_shading_normal + i + 2] = itm.normals[itm.indices[i + 2]];
                }

                if (itm.tex_coords.size() > 0)
                {
                    tex_coords_[first_tex_coord + i]     = itm.tex_coords[itm.indices[i]];
                    tex_coords_[first_tex_coord + i + 1] = itm.tex_coords[itm.indices[i + 1]];
                    tex_coords_[first_tex_coord + i + 2] = itm.tex_coords[itm.indices[i + 2]];
                }

                if (itm.colors.size() > 0)
                {
                    colors_[first_color + i]     = vec3(itm.colors[itm.indices[i]]);
                    colors_[first_color + i + 1] = vec3(itm.colors[itm.indices[i + 1]]);
                    colors_[first_color + i + 2] = vec3(itm.colors[itm.indices[i + 2]]);
                }
            }
{'fraction_non_alphanumeric': 0.13898795678916628, 'fraction_numerical': 0.007494702020985166, 'mean_word_length': 1.7190640151781322, 'pattern_counts': {'":': 0, '<': 298, '<?xml version=': 0, '>': 338, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 60, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '77397', 'n_tokens_mistral': 12014, 'n_tokens_neox': 11405, 'n_words': 2019}