diff --git a/examples/raytrace/README.md b/examples/raytrace/README.md deleted file mode 100644 index 7436d3e..0000000 --- a/examples/raytrace/README.md +++ /dev/null @@ -1,33 +0,0 @@ -# Raytrace example - -Simple raytracing example with OpenGL preview - -## Status - -Not working yet. Still in work in progress. - -## Build - -### Linux or macOS - -``` -$ premake5 gmake -$ make -``` - -### Windows - -``` -$ premake5 vs2015 -``` - - -## Third party libraries and its icenses. - -* picojson : BSD license. -* bt3gui : zlib license. -* glew : BSD/MIT license. -* tinyobjloader : MIT license. -* glm : The Happy Bunny License (Modified MIT License). Copyright (c) 2005 - 2017 G-Truc Creation -* ImGui : The MIT License (MIT). Copyright (c) 2014-2015 Omar Cornut and ImGui contributors -* ImGuizmo : The MIT License (MIT). Copyright (c) 2016 Cedric Guillemet diff --git a/examples/raytrace/config.json b/examples/raytrace/config.json deleted file mode 100644 index e2dcf67..0000000 --- a/examples/raytrace/config.json +++ /dev/null @@ -1,9 +0,0 @@ -{ "gltf_filename" : "../../models/Cube/Cube.gltf", - "scene_scale" : 1.0, - "width" : 512, - "height" : 512, - "eye" : [0, 2.5, 15], - "up" : [0, 1, 0], - "look_at" : [0, 0, 0], - "dummy" : 0 -} diff --git a/examples/raytrace/main.cc b/examples/raytrace/main.cc deleted file mode 100644 index 968c9bc..0000000 --- a/examples/raytrace/main.cc +++ /dev/null @@ -1,1012 +0,0 @@ -/* -The MIT License (MIT) - -Copyright (c) 2015 - 2016 Light Transport Entertainment, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -*/ - -#ifdef _MSC_VER -#pragma warning(disable : 4244) -#endif - -#define USE_OPENGL2 -#include "OpenGLWindow/OpenGLInclude.h" -#ifdef _WIN32 -#include "OpenGLWindow/Win32OpenGLWindow.h" -#elif defined __APPLE__ -#include "OpenGLWindow/MacOpenGLWindow.h" -#else -// assume linux -#include "OpenGLWindow/X11OpenGLWindow.h" -#endif - -#ifdef _WIN32 -#include -#include -#include -#else -#include -#include -#include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include // C++11 -#include // C++11 -#include // C++11 -#include // C++11 - -#include "imgui.h" -#include "imgui_impl_btgui.h" - -#include "ImGuizmo.h" - -#if defined(_MSC_VER) -#pragma warning(push) -#pragma warning(disable: 4201) -#endif - -#include "glm/mat4x4.hpp" -#include "glm/gtc/quaternion.hpp" -#include "glm/gtc/matrix_transform.hpp" -#include "glm/gtc/type_ptr.hpp" - -#if defined(_MSC_VER) -#pragma warning(pop) -#endif - -#include "nanosg.h" -#include "render-config.h" -#include "render.h" -#include "gltf-loader.h" -#include "trackball.h" - -#ifdef WIN32 -#ifndef NOMINMAX -#define NOMINMAX -#endif -#endif - -#define SHOW_BUFFER_COLOR (0) -#define SHOW_BUFFER_NORMAL (1) -#define SHOW_BUFFER_POSITION (2) -#define SHOW_BUFFER_DEPTH (3) -#define SHOW_BUFFER_TEXCOORD (4) -#define SHOW_BUFFER_VARYCOORD (5) - -b3gDefaultOpenGLWindow* window = 0; -int gWidth = 512; -int gHeight = 512; -int gMousePosX = -1, gMousePosY = -1; -bool gMouseLeftDown = false; -int gShowBufferMode = SHOW_BUFFER_COLOR; -bool gTabPressed = false; -bool gShiftPressed = false; -float gShowPositionScale = 1.0f; -float gShowDepthRange[2] = {10.0f, 20.f}; -bool gShowDepthPeseudoColor = true; -float gCurrQuat[4] = {0.0f, 0.0f, 0.0f, 0.0f}; -float gPrevQuat[4] = {0.0f, 0.0f, 0.0f, 0.0f}; - -static nanosg::Scene > gScene; -static example::Asset gAsset; -static std::vector > > gNodes; - -std::atomic gRenderQuit; -std::atomic gRenderRefresh; -std::atomic gRenderCancel; -std::atomic gSceneDirty; -example::RenderConfig gRenderConfig; -std::mutex gMutex; - -struct RenderLayer -{ - std::vector displayRGBA; // Accumurated image. - std::vector rgba; - std::vector auxRGBA; // Auxiliary buffer - std::vector sampleCounts; // Sample num counter for each pixel. - std::vector normalRGBA; // For visualizing normal - std::vector positionRGBA; // For visualizing position - std::vector depthRGBA; // For visualizing depth - std::vector texCoordRGBA; // For visualizing texcoord - std::vector varyCoordRGBA; // For visualizing varycentric coord -}; - -RenderLayer gRenderLayer; - -struct Camera -{ - glm::mat4 view; - glm::mat4 projection; -}; - -struct ManipConfig -{ - glm::vec3 snapTranslation; - glm::vec3 snapRotation; - glm::vec3 snapScale; -}; - -void RequestRender() { - { - std::lock_guard guard(gMutex); - gRenderConfig.pass = 0; - } - - gRenderRefresh = true; - gRenderCancel = true; -} - -void RenderThread() { - { - std::lock_guard guard(gMutex); - gRenderConfig.pass = 0; - } - - while (1) { - if (gRenderQuit) return; - - if (!gRenderRefresh || gRenderConfig.pass >= gRenderConfig.max_passes) { - // Give some cycles to this thread. - std::this_thread::sleep_for(std::chrono::milliseconds(10)); - continue; - } - - auto startT = std::chrono::system_clock::now(); - - // Initialize display buffer for the first pass. - bool initial_pass = false; - { - std::lock_guard guard(gMutex); - if (gRenderConfig.pass == 0) { - initial_pass = true; - } - } - - if (gSceneDirty) { - gScene.Commit(); - gSceneDirty = false; - } - - gRenderCancel = false; - // gRenderCancel may be set to true in main loop. - // Render() will repeatedly check this flag inside the rendering loop. - - bool ret = - example::Renderer::Render(&gRenderLayer.rgba.at(0), &gRenderLayer.auxRGBA.at(0), &gRenderLayer.sampleCounts.at(0), - gCurrQuat, gScene, gAsset, gRenderConfig, gRenderCancel); - - if (ret) { - std::lock_guard guard(gMutex); - - gRenderConfig.pass++; - } - - auto endT = std::chrono::system_clock::now(); - - std::chrono::duration ms = endT - startT; - - // std::cout << ms.count() << " [ms]\n"; - } -} - -void InitRender(example::RenderConfig* rc) { - rc->pass = 0; - - rc->max_passes = 128; - - gRenderLayer.sampleCounts.resize(rc->width * rc->height); - std::fill(gRenderLayer.sampleCounts.begin(), gRenderLayer.sampleCounts.end(), 0.0); - - gRenderLayer.displayRGBA.resize(rc->width * rc->height * 4); - std::fill(gRenderLayer.displayRGBA.begin(), gRenderLayer.displayRGBA.end(), 0.0); - - gRenderLayer.rgba.resize(rc->width * rc->height * 4); - std::fill(gRenderLayer.rgba.begin(), gRenderLayer.rgba.end(), 0.0); - - gRenderLayer.auxRGBA.resize(rc->width * rc->height * 4); - std::fill(gRenderLayer.auxRGBA.begin(), gRenderLayer.auxRGBA.end(), 0.0); - - gRenderLayer.normalRGBA.resize(rc->width * rc->height * 4); - std::fill(gRenderLayer.normalRGBA.begin(), gRenderLayer.normalRGBA.end(), 0.0); - - gRenderLayer.positionRGBA.resize(rc->width * rc->height * 4); - std::fill(gRenderLayer.positionRGBA.begin(), gRenderLayer.positionRGBA.end(), 0.0); - - gRenderLayer.depthRGBA.resize(rc->width * rc->height * 4); - std::fill(gRenderLayer.depthRGBA.begin(), gRenderLayer.depthRGBA.end(), 0.0); - - gRenderLayer.texCoordRGBA.resize(rc->width * rc->height * 4); - std::fill(gRenderLayer.texCoordRGBA.begin(), gRenderLayer.texCoordRGBA.end(), 0.0); - - gRenderLayer.varyCoordRGBA.resize(rc->width * rc->height * 4); - std::fill(gRenderLayer.varyCoordRGBA.begin(), gRenderLayer.varyCoordRGBA.end(), 0.0); - - rc->normalImage = &gRenderLayer.normalRGBA.at(0); - rc->positionImage = &gRenderLayer.positionRGBA.at(0); - rc->depthImage = &gRenderLayer.depthRGBA.at(0); - rc->texcoordImage = &gRenderLayer.texCoordRGBA.at(0); - rc->varycoordImage = &gRenderLayer.varyCoordRGBA.at(0); - - trackball(gCurrQuat, 0.0f, 0.0f, 0.0f, 0.0f); -} - -void checkErrors(std::string desc) { - GLenum e = glGetError(); - if (e != GL_NO_ERROR) { - fprintf(stderr, "OpenGL error in \"%s\": %d (%d)\n", desc.c_str(), e, e); - exit(20); - } -} - - -static int CreateDisplayTextureGL(const float *data, int width, - int height, int components) { - GLuint id; - glGenTextures(1, &id); - - GLint last_texture; - glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); - - glBindTexture(GL_TEXTURE_2D, id); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - - GLenum format = GL_RGBA; - if (components == 1) { - format = GL_LUMINANCE; - } else if (components == 2) { - format = GL_LUMINANCE_ALPHA; - } else if (components == 3) { - format = GL_RGB; - } else if (components == 4) { - format = GL_RGBA; - } else { - assert(0); // "Invalid components" - } - - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, format, - GL_FLOAT, data); - - glBindTexture(GL_TEXTURE_2D, last_texture); - - return static_cast(id); -} - -void keyboardCallback(int keycode, int state) { - printf("hello key %d, state %d(ctrl %d)\n", keycode, state, - window->isModifierKeyPressed(B3G_CONTROL)); - // if (keycode == 'q' && window && window->isModifierKeyPressed(B3G_SHIFT)) { - if (keycode == 27) { - if (window) window->setRequestExit(); - } else if (keycode == ' ') { - // reset. - trackball(gCurrQuat, 0.0f, 0.0f, 0.0f, 0.0f); - // clear buffer. - memset(gRenderLayer.rgba.data(), 0, sizeof(float) * gRenderConfig.width * gRenderConfig.height * 4); - memset(gRenderLayer.sampleCounts.data(), 0, sizeof(int) * gRenderConfig.width * gRenderConfig.height); - } else if (keycode == 9) { - gTabPressed = (state == 1); - } else if (keycode == B3G_SHIFT) { - gShiftPressed = (state == 1); - } - - ImGui_ImplBtGui_SetKeyState(keycode, (state == 1)); - - if (keycode >= 32 && keycode <= 126) { - if (state == 1) { - ImGui_ImplBtGui_SetChar(keycode); - } - } -} - -void mouseMoveCallback(float x, float y) { - - if (gMouseLeftDown) { - if (ImGuizmo::IsOver() || ImGuizmo::IsUsing()) { - } else { - float w = static_cast(gRenderConfig.width); - float h = static_cast(gRenderConfig.height); - - float y_offset = gHeight - h; - - if (gTabPressed) { - const float dolly_scale = 0.1f; - gRenderConfig.eye[2] += dolly_scale * (gMousePosY - y); - gRenderConfig.look_at[2] += dolly_scale * (gMousePosY - y); - } else if (gShiftPressed) { - const float trans_scale = 0.02f; - gRenderConfig.eye[0] += trans_scale * (gMousePosX - x); - gRenderConfig.eye[1] -= trans_scale * (gMousePosY - y); - gRenderConfig.look_at[0] += trans_scale * (gMousePosX - x); - gRenderConfig.look_at[1] -= trans_scale * (gMousePosY - y); - - } else { - // Adjust y. - trackball(gPrevQuat, (2.f * gMousePosX - w) / (float)w, - (h - 2.f * (gMousePosY - y_offset)) / (float)h, - (2.f * x - w) / (float)w, - (h - 2.f * (y - y_offset)) / (float)h); - add_quats(gPrevQuat, gCurrQuat, gCurrQuat); - } - RequestRender(); - } - } - - gMousePosX = (int)x; - gMousePosY = (int)y; -} - -void mouseButtonCallback(int button, int state, float x, float y) { - (void)x; - (void)y; - ImGui_ImplBtGui_SetMouseButtonState(button, (state == 1)); - - ImGuiIO& io = ImGui::GetIO(); - if (io.WantCaptureMouse || io.WantCaptureKeyboard) { - return; - } - - // left button - if (button == 0) { - if (state) { - gMouseLeftDown = true; - if (ImGuizmo::IsOver() || ImGuizmo::IsUsing()) { - } else { - trackball(gPrevQuat, 0.0f, 0.0f, 0.0f, 0.0f); - } - } else { - gMouseLeftDown = false; - if (ImGuizmo::IsOver() || ImGuizmo::IsUsing()) { - gSceneDirty = true; - RequestRender(); - } - } - } -} - -void resizeCallback(float width, float height) { - //GLfloat h = (GLfloat)height / (GLfloat)width; - GLfloat xmax, znear, zfar; - - znear = 1.0f; - zfar = 1000.0f; - xmax = znear * 0.5f; - - gWidth = static_cast(width); - gHeight = static_cast(height); -} - -inline float pesudoColor(float v, int ch) { - if (ch == 0) { // red - if (v <= 0.5f) - return 0.f; - else if (v < 0.75f) - return (v - 0.5f) / 0.25f; - else - return 1.f; - } else if (ch == 1) { // green - if (v <= 0.25f) - return v / 0.25f; - else if (v < 0.75f) - return 1.f; - else - return 1.f - (v - 0.75f) / 0.25f; - } else if (ch == 2) { // blue - if (v <= 0.25f) - return 1.f; - else if (v < 0.5f) - return 1.f - (v - 0.25f) / 0.25f; - else - return 0.f; - } else { // alpha - return 1.f; - } -} - -void UpdateDisplayTextureGL(GLint tex_id, int width, int height) { - if (tex_id < 0) { - // ??? - return; - } - - std::vector buf; - buf.resize(width * height * 4); - - if (gShowBufferMode == SHOW_BUFFER_COLOR) { - // normalize - for (size_t i = 0; i < buf.size() / 4; i++) { - buf[4 * i + 0] = gRenderLayer.rgba[4 * i + 0]; - buf[4 * i + 1] = gRenderLayer.rgba[4 * i + 1]; - buf[4 * i + 2] = gRenderLayer.rgba[4 * i + 2]; - buf[4 * i + 3] = gRenderLayer.rgba[4 * i + 3]; - if (gRenderLayer.sampleCounts[i] > 0) { - buf[4 * i + 0] /= static_cast(gRenderLayer.sampleCounts[i]); - buf[4 * i + 1] /= static_cast(gRenderLayer.sampleCounts[i]); - buf[4 * i + 2] /= static_cast(gRenderLayer.sampleCounts[i]); - buf[4 * i + 3] /= static_cast(gRenderLayer.sampleCounts[i]); - } - } - } else if (gShowBufferMode == SHOW_BUFFER_NORMAL) { - for (size_t i = 0; i < buf.size(); i++) { - buf[i] = gRenderLayer.normalRGBA[i]; - } - } else if (gShowBufferMode == SHOW_BUFFER_POSITION) { - for (size_t i = 0; i < buf.size(); i++) { - buf[i] = gRenderLayer.positionRGBA[i] * gShowPositionScale; - } - } else if (gShowBufferMode == SHOW_BUFFER_DEPTH) { - float d_min = std::min(gShowDepthRange[0], gShowDepthRange[1]); - float d_diff = fabsf(gShowDepthRange[1] - gShowDepthRange[0]); - d_diff = std::max(d_diff, std::numeric_limits::epsilon()); - for (size_t i = 0; i < buf.size(); i++) { - float v = (gRenderLayer.depthRGBA[i] - d_min) / d_diff; - if (gShowDepthPeseudoColor) { - buf[i] = pesudoColor(v, i % 4); - } else { - buf[i] = v; - } - } - } else if (gShowBufferMode == SHOW_BUFFER_TEXCOORD) { - for (size_t i = 0; i < buf.size(); i++) { - buf[i] = gRenderLayer.texCoordRGBA[i]; - } - } else if (gShowBufferMode == SHOW_BUFFER_VARYCOORD) { - for (size_t i = 0; i < buf.size(); i++) { - buf[i] = gRenderLayer.varyCoordRGBA[i]; - } - } - - // Flip Y - std::vector disp; - disp.resize(width * height * 4); - { - for (size_t y = 0; y < height; y++) { - memcpy(&disp[4 * (y * width)], &buf[4 * ((height - y - 1) * width)], sizeof(float) * 4 * width); - } - - } - - glBindTexture(GL_TEXTURE_2D, tex_id); - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_FLOAT, disp.data()); - glBindTexture(GL_TEXTURE_2D, 0); - - //glRasterPos2i(-1, -1); - //glDrawPixels(width, height, GL_RGBA, GL_FLOAT, - // static_cast(&buf.at(0))); -} - -void EditTransform(const ManipConfig &config, const Camera& camera, glm::mat4& matrix) -{ - static ImGuizmo::OPERATION mCurrentGizmoOperation(ImGuizmo::ROTATE); - static ImGuizmo::MODE mCurrentGizmoMode(ImGuizmo::WORLD); - if (ImGui::IsKeyPressed(90)) - mCurrentGizmoOperation = ImGuizmo::TRANSLATE; - if (ImGui::IsKeyPressed(69)) - mCurrentGizmoOperation = ImGuizmo::ROTATE; - if (ImGui::IsKeyPressed(82)) // r Key - mCurrentGizmoOperation = ImGuizmo::SCALE; - if (ImGui::RadioButton("Translate", mCurrentGizmoOperation == ImGuizmo::TRANSLATE)) - mCurrentGizmoOperation = ImGuizmo::TRANSLATE; - ImGui::SameLine(); - if (ImGui::RadioButton("Rotate", mCurrentGizmoOperation == ImGuizmo::ROTATE)) - mCurrentGizmoOperation = ImGuizmo::ROTATE; - ImGui::SameLine(); - if (ImGui::RadioButton("Scale", mCurrentGizmoOperation == ImGuizmo::SCALE)) - mCurrentGizmoOperation = ImGuizmo::SCALE; - float matrixTranslation[3], matrixRotation[3], matrixScale[3]; - ImGuizmo::DecomposeMatrixToComponents(&matrix[0][0], matrixTranslation, matrixRotation, matrixScale); - ImGui::InputFloat3("Tr", matrixTranslation, 3); - ImGui::InputFloat3("Rt", matrixRotation, 3); - ImGui::InputFloat3("Sc", matrixScale, 3); - ImGuizmo::RecomposeMatrixFromComponents(matrixTranslation, matrixRotation, matrixScale, &matrix[0][0]); - - if (mCurrentGizmoOperation != ImGuizmo::SCALE) - { - if (ImGui::RadioButton("Local", mCurrentGizmoMode == ImGuizmo::LOCAL)) - mCurrentGizmoMode = ImGuizmo::LOCAL; - ImGui::SameLine(); - if (ImGui::RadioButton("World", mCurrentGizmoMode == ImGuizmo::WORLD)) - mCurrentGizmoMode = ImGuizmo::WORLD; - } - static bool useSnap(false); - if (ImGui::IsKeyPressed(83)) - useSnap = !useSnap; - ImGui::Checkbox("", &useSnap); - ImGui::SameLine(); - glm::vec3 snap; - switch (mCurrentGizmoOperation) - { - case ImGuizmo::TRANSLATE: - snap = config.snapTranslation; - ImGui::InputFloat3("Snap", &snap.x); - break; - case ImGuizmo::ROTATE: - snap = config.snapRotation; - ImGui::InputFloat("Angle Snap", &snap.x); - break; - case ImGuizmo::SCALE: - snap = config.snapScale; - ImGui::InputFloat("Scale Snap", &snap.x); - break; - } - ImGuiIO& io = ImGui::GetIO(); - ImGuizmo::SetRect(0, 0, io.DisplaySize.x, io.DisplaySize.y); - ImGuizmo::Manipulate(&camera.view[0][0], &camera.projection[0][0], mCurrentGizmoOperation, mCurrentGizmoMode, &matrix[0][0], NULL, useSnap ? &snap.x : NULL); -} - -void DrawMesh(const example::Mesh *mesh) -{ - // TODO(LTE): Use vertex array or use display list. - - glBegin(GL_TRIANGLES); - - if (!mesh->facevarying_normals.empty()) { - for (size_t i = 0; i < mesh->faces.size() / 3; i++) { - unsigned int f0 = mesh->faces[3 * i + 0]; - unsigned int f1 = mesh->faces[3 * i + 1]; - unsigned int f2 = mesh->faces[3 * i + 2]; - - glNormal3f(mesh->facevarying_normals[9 * i + 0], - mesh->facevarying_normals[9 * i + 1], - mesh->facevarying_normals[9 * i + 2]); - glVertex3f(mesh->vertices[3 * f0 + 0], - mesh->vertices[3 * f0 + 1], - mesh->vertices[3 * f0 + 2]); - glNormal3f(mesh->facevarying_normals[9 * i + 3], - mesh->facevarying_normals[9 * i + 4], - mesh->facevarying_normals[9 * i + 5]); - glVertex3f(mesh->vertices[3 * f1 + 0], - mesh->vertices[3 * f1 + 1], - mesh->vertices[3 * f1 + 2]); - glNormal3f(mesh->facevarying_normals[9 * i + 6], - mesh->facevarying_normals[9 * i + 7], - mesh->facevarying_normals[9 * i + 8]); - glVertex3f(mesh->vertices[3 * f2 + 0], - mesh->vertices[3 * f2 + 1], - mesh->vertices[3 * f2 + 2]); - } - - } else { - for (size_t i = 0; i < mesh->faces.size() / 3; i++) { - unsigned int f0 = mesh->faces[3 * i + 0]; - unsigned int f1 = mesh->faces[3 * i + 1]; - unsigned int f2 = mesh->faces[3 * i + 2]; - - glVertex3f(mesh->vertices[3 * f0 + 0], - mesh->vertices[3 * f0 + 1], - mesh->vertices[3 * f0 + 2]); - glVertex3f(mesh->vertices[3 * f1 + 0], - mesh->vertices[3 * f1 + 1], - mesh->vertices[3 * f1 + 2]); - glVertex3f(mesh->vertices[3 * f2 + 0], - mesh->vertices[3 * f2 + 1], - mesh->vertices[3 * f2 + 2]); - } - } - - glEnd(); -} - -void DrawNode(const nanosg::Node > &node) -{ - glPushMatrix(); - glMultMatrixf(node.GetLocalXformPtr()); - - if (node.GetMesh()) { - DrawMesh(node.GetMesh()); - } - - for (size_t i = 0; i < node.GetChildren().size(); i++) { - DrawNode(node.GetChildren()[i]); - } - - glPopMatrix(); -} - -// Draw scene with OpenGL -void DrawScene(const nanosg::Scene > &scene, const Camera &camera) -{ - glEnable(GL_DEPTH_TEST); - - glEnable(GL_LIGHTING); - glEnable(GL_LIGHT0); - glEnable(GL_LIGHT1); - - // FIXME(LTE): Use scene bounding box. - const float light0_pos[4] = {1000.0f, 1000.0f, 1000.0f, 0.0f}; - const float light1_pos[4] = {-1000.0f, -1000.0f, -1000.0f, 0.0f}; - - const float light_diffuse[4] = {0.5f, 0.5f, 0.5f, 1.0f}; - - glLightfv(GL_LIGHT0, GL_POSITION, &light0_pos[0]); - glLightfv(GL_LIGHT0, GL_DIFFUSE, &light_diffuse[0]); - glLightfv(GL_LIGHT1, GL_POSITION, &light1_pos[0]); - glLightfv(GL_LIGHT1, GL_DIFFUSE, &light_diffuse[0]); - - const std::vector > > &root_nodes = scene.GetNodes(); - - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glLoadMatrixf(&camera.projection[0][0]); - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glLoadMatrixf(&camera.view[0][0]); - - for (size_t i = 0; i < root_nodes.size(); i++) { - DrawNode(root_nodes[i]); - } - - glMatrixMode(GL_PROJECTION); - glPopMatrix(); - glMatrixMode(GL_MODELVIEW); - glPopMatrix(); - - glDisable(GL_LIGHT0); - glDisable(GL_LIGHT1); - glDisable(GL_LIGHTING); - glDisable(GL_DEPTH_TEST); - -} - -void BuildSceneItems( - std::vector *display_names, - std::vector *names, - const nanosg::Node > &node, - int indent) -{ - if (node.GetName().empty()) { - // Skip a node with empty name. - return; - } - - std::stringstream ss; - for (int i = 0; i < indent; i++) { - ss << " "; - } - - ss << node.GetName(); - std::string display_name = ss.str(); - - display_names->push_back(display_name); - names->push_back(node.GetName()); - - for (size_t i = 0; i < node.GetChildren().size(); i++) { - BuildSceneItems(display_names, names, node.GetChildren()[i], indent + 1); - } - -} - -int main(int argc, char** argv) { - std::string config_filename = "config.json"; - - if (argc > 1) { - config_filename = argv[1]; - } - - // load config - { - bool ret = - example::LoadRenderConfig(&gRenderConfig, config_filename.c_str()); - if (!ret) { - std::cerr << "Failed to load [ " << config_filename << " ]" << std::endl; - return -1; - } - } - - // construct the scene - { - std::vector > meshes; - std::vector materials; - std::vector textures; - - bool ret = LoadGLTF(gRenderConfig.gltf_filename, gRenderConfig.scene_scale, &meshes, &materials, &textures); - if (!ret) { - std::cerr << "Failed to load glTF [ " << gRenderConfig.gltf_filename << " ]" << std::endl; - return -1; - } - - gAsset.materials = materials; - gAsset.textures = textures; - - for (size_t n = 0; n < meshes.size(); n++) { - size_t mesh_id = gAsset.meshes.size(); - gAsset.meshes.push_back(meshes[mesh_id]); - } - - for (size_t n = 0; n < gAsset.meshes.size(); n++) { - - nanosg::Node > node(&gAsset.meshes[n]); - node.SetName(meshes[n].name); - node.SetLocalXform(meshes[n].pivot_xform); // Use mesh's pivot transform as node's local transform. - gNodes.push_back(node); - - gScene.AddNode(node); - } - - if (!gScene.Commit()) { - std::cerr << "Failed to commit the scene." << std::endl; - return -1; - } - - float bmin[3], bmax[3]; - gScene.GetBoundingBox(bmin, bmax); - printf(" # of nodes : %d\n", int(gNodes.size())); - printf(" Scene Bmin : %f, %f, %f\n", bmin[0], bmin[1], bmin[2]); - printf(" Scene Bmax : %f, %f, %f\n", bmax[0], bmax[1], bmax[2]); - - } - - std::vector imgui_node_names; - std::vector display_node_names; - std::vector node_names; - std::map > *> node_map; - - { - for (size_t i = 0; i < gScene.GetNodes().size(); i++) { - BuildSceneItems(&display_node_names, &node_names, gScene.GetNodes()[i], /* indent */0); - } - - // List of strings for imgui. - // Assume nodes in the scene does not change. - for (size_t i = 0; i < display_node_names.size(); i++) { - //std::cout << "name : " << display_node_names[i] << std::endl; - imgui_node_names.push_back(display_node_names[i].c_str()); - } - - // Construct list index <-> Node ptr map. - for (size_t i = 0; i < node_names.size(); i++) { - nanosg::Node > *node; - - if (gScene.FindNode(node_names[i], &node)) { - //std::cout << "id : " << i << ", name : " << node_names[i] << std::endl; - node_map[i] = node; - } - } - } - - - window = new b3gDefaultOpenGLWindow; - b3gWindowConstructionInfo ci; -#ifdef USE_OPENGL2 - ci.m_openglVersion = 2; -#endif - ci.m_width = 1024; - ci.m_height = 800; - window->createWindow(ci); - - window->setWindowTitle("view"); - -#ifndef __APPLE__ -#ifndef _WIN32 - // some Linux implementations need the 'glewExperimental' to be true - glewExperimental = GL_TRUE; -#endif - if (glewInit() != GLEW_OK) { - fprintf(stderr, "Failed to initialize GLEW\n"); - exit(-1); - } - - if (!GLEW_VERSION_2_1) { - fprintf(stderr, "OpenGL 2.1 is not available\n"); - exit(-1); - } -#endif - - InitRender(&gRenderConfig); - - checkErrors("init"); - - window->setMouseButtonCallback(mouseButtonCallback); - window->setMouseMoveCallback(mouseMoveCallback); - checkErrors("mouse"); - window->setKeyboardCallback(keyboardCallback); - checkErrors("keyboard"); - window->setResizeCallback(resizeCallback); - checkErrors("resize"); - - ImGui_ImplBtGui_Init(window); - - ImGuiIO& io = ImGui::GetIO(); - io.Fonts->AddFontDefault(); - //io.Fonts->AddFontFromFileTTF("./DroidSans.ttf", 15.0f); - - glm::mat4 projection(1.0f); - glm::mat4 view(1.0f); - glm::mat4 matrix(1.0f); - - Camera camera; - - std::thread renderThread(RenderThread); - - // Trigger initial rendering request - RequestRender(); - - while (!window->requestedExit()) { - window->startRendering(); - - checkErrors("begin frame"); - - ImGui_ImplBtGui_NewFrame(gMousePosX, gMousePosY); - - ImGuizmo::BeginFrame(); - ImGuizmo::Enable(true); - - //ImGuiIO &io = ImGui::GetIO(); - ImGuizmo::SetRect(0, 0, io.DisplaySize.x, io.DisplaySize.y); - - ImGui::Begin("UI"); - { - static float col[3] = {0, 0, 0}; - static float f = 0.0f; - // if (ImGui::ColorEdit3("color", col)) { - // RequestRender(); - //} - // ImGui::InputFloat("intensity", &f); - if (ImGui::InputFloat3("eye", gRenderConfig.eye)) { - RequestRender(); - } - if (ImGui::InputFloat3("up", gRenderConfig.up)) { - RequestRender(); - } - if (ImGui::InputFloat3("look_at", gRenderConfig.look_at)) { - RequestRender(); - } - - ImGui::RadioButton("color", &gShowBufferMode, SHOW_BUFFER_COLOR); - ImGui::SameLine(); - ImGui::RadioButton("normal", &gShowBufferMode, SHOW_BUFFER_NORMAL); - ImGui::SameLine(); - ImGui::RadioButton("position", &gShowBufferMode, SHOW_BUFFER_POSITION); - ImGui::SameLine(); - ImGui::RadioButton("depth", &gShowBufferMode, SHOW_BUFFER_DEPTH); - ImGui::SameLine(); - ImGui::RadioButton("texcoord", &gShowBufferMode, SHOW_BUFFER_TEXCOORD); - ImGui::SameLine(); - ImGui::RadioButton("varycoord", &gShowBufferMode, SHOW_BUFFER_VARYCOORD); - - ImGui::InputFloat("show pos scale", &gShowPositionScale); - - ImGui::InputFloat2("show depth range", gShowDepthRange); - ImGui::Checkbox("show depth pesudo color", &gShowDepthPeseudoColor); - } - - ImGui::End(); - - glViewport(0, 0, window->getWidth(), window->getHeight()); - glClearColor(0.0f, 0.1f, 0.2f, 1.0f); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); - - checkErrors("clear"); - - - // Render display window - { - static GLint gl_texid = -1; - if (gl_texid < 0) { - gl_texid = CreateDisplayTextureGL(NULL, gRenderConfig.width, gRenderConfig.height, 4); - } - - // Refresh texture until rendering finishes. - if (gRenderConfig.pass < gRenderConfig.max_passes) { - // FIXME(LTE): Do not update GL texture frequently. - UpdateDisplayTextureGL(gl_texid, gRenderConfig.width, gRenderConfig.height); - } - - ImGui::Begin("Render"); - ImTextureID tex_id = reinterpret_cast( - static_cast(gl_texid)); - ImGui::Image(tex_id, ImVec2(256, 256), ImVec2(0, 0), - ImVec2(1, 1));// Setup camera and draw imguizomo - - ImGui::End(); - - } - - // scene graph tree - glm::mat4 node_matrix; - static int node_selected = 0; - { - ImGui::Begin("Scene"); - - ImGui::ListBox("Node list", &node_selected, imgui_node_names.data(), imgui_node_names.size(), 16); - node_matrix = glm::make_mat4(node_map[node_selected]->GetLocalXformPtr()); - - ImGui::End(); - } - - { - ImGui::Begin("Transform"); - - static ImGuizmo::OPERATION guizmo_op(ImGuizmo::ROTATE); - static ImGuizmo::MODE guizmo_mode(ImGuizmo::WORLD); - - glm::vec3 eye, lookat, up; - eye[0] = gRenderConfig.eye[0]; - eye[1] = gRenderConfig.eye[1]; - eye[2] = gRenderConfig.eye[2]; - - lookat[0] = gRenderConfig.look_at[0]; - lookat[1] = gRenderConfig.look_at[1]; - lookat[2] = gRenderConfig.look_at[2]; - - up[0] = gRenderConfig.up[0]; - up[1] = gRenderConfig.up[1]; - up[2] = gRenderConfig.up[2]; - - // NOTE(LTE): w, then (x,y,z) for glm::quat. - glm::quat rot = glm::quat(gCurrQuat[3], gCurrQuat[0], gCurrQuat[1], gCurrQuat[2]); - glm::mat4 rm = glm::mat4_cast(rot); - - view = glm::lookAt(eye, lookat, up) * glm::inverse(glm::mat4_cast(rot)); - projection = glm::perspective (45.0f, float(window->getWidth()) / float(window->getHeight()), 0.01f, 1000.0f); - - camera.view = view; - camera.projection = projection; - ManipConfig manip_config; - - EditTransform(manip_config, camera, node_matrix); - - float mat[4][4]; - memcpy(mat, &node_matrix[0][0], sizeof(float) * 16); - node_map[node_selected]->SetLocalXform(mat); - - checkErrors("edit_transform"); - - ImGui::End(); - } - - // Draw scene in OpenGL - DrawScene(gScene, camera); - - // Draw imgui - ImGui::Render(); - - checkErrors("im render"); - - window->endRendering(); - - // Give some cycles to this thread. - std::this_thread::sleep_for(std::chrono::milliseconds(16)); - } - - { - gRenderCancel = true; - gRenderQuit = true; - renderThread.join(); - } - - ImGui_ImplBtGui_Shutdown(); - delete window; - - return EXIT_SUCCESS; -} diff --git a/examples/raytrace/material.h b/examples/raytrace/material.h deleted file mode 100644 index ec3a330..0000000 --- a/examples/raytrace/material.h +++ /dev/null @@ -1,69 +0,0 @@ -#ifndef EXAMPLE_MATERIAL_H_ -#define EXAMPLE_MATERIAL_H_ - -#include - -namespace example { - -// TODO(syoyo): Support PBR material. - -struct Material { - // float ambient[3]; - float diffuse[3]; - float specular[3]; - // float reflection[3]; - // float refraction[3]; - int id; - int diffuse_texid; - int specular_texid; - // int reflection_texid; - // int transparency_texid; - // int bump_texid; - // int normal_texid; // normal map - // int alpha_texid; // alpha map - - Material() { - // ambient[0] = 0.0; - // ambient[1] = 0.0; - // ambient[2] = 0.0; - diffuse[0] = 0.5; - diffuse[1] = 0.5; - diffuse[2] = 0.5; - specular[0] = 0.5; - specular[1] = 0.5; - specular[2] = 0.5; - // reflection[0] = 0.0; - // reflection[1] = 0.0; - // reflection[2] = 0.0; - // refraction[0] = 0.0; - // refraction[1] = 0.0; - // refraction[2] = 0.0; - id = -1; - diffuse_texid = -1; - specular_texid = -1; - // reflection_texid = -1; - // transparency_texid = -1; - // bump_texid = -1; - // normal_texid = -1; - // alpha_texid = -1; - } -}; - -struct Texture { - int width; - int height; - int components; - unsigned char* image; - - Texture() { - width = -1; - height = -1; - components = -1; - image = NULL; - } -}; - -} // namespace example - - -#endif // EXAMPLE_MATERIAL_H_ diff --git a/examples/raytrace/matrix.cc b/examples/raytrace/matrix.cc deleted file mode 100644 index b6670a9..0000000 --- a/examples/raytrace/matrix.cc +++ /dev/null @@ -1,216 +0,0 @@ -#include -#include - -#include "matrix.h" - -//using namespace mallie; - -static inline float vdot(float a[3], float b[3]) { - return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; -} - -static inline void vcross(float c[3], float a[3], float b[3]) { - c[0] = a[1] * b[2] - a[2] * b[1]; - c[1] = a[2] * b[0] - a[0] * b[2]; - c[2] = a[0] * b[1] - a[1] * b[0]; -} - -static inline float vlength(float v[3]) { - float len2 = vdot(v, v); - if (std::abs(len2) > 1.0e-30) { - return sqrt(len2); - } - return 0.0f; -} - -static void vnormalize(float v[3]) { - float len = vlength(v); - if (std::abs(len) > 1.0e-30) { - float inv_len = 1.0f / len; - v[0] *= inv_len; - v[1] *= inv_len; - v[2] *= inv_len; - } -} - -void Matrix::Print(float m[4][4]) { - for (int i = 0; i < 4; i++) { - printf("m[%d] = %f, %f, %f, %f\n", i, m[i][0], m[i][1], m[i][2], m[i][3]); - } -} - -void Matrix::LookAt(float m[4][4], float eye[3], float lookat[3], - float up[3]) { - - float u[3], v[3]; - float look[3]; - look[0] = lookat[0] - eye[0]; - look[1] = lookat[1] - eye[1]; - look[2] = lookat[2] - eye[2]; - vnormalize(look); - - vcross(u, look, up); - vnormalize(u); - - vcross(v, u, look); - vnormalize(v); - -#if 0 - m[0][0] = u[0]; - m[0][1] = v[0]; - m[0][2] = -look[0]; - m[0][3] = 0.0; - - m[1][0] = u[1]; - m[1][1] = v[1]; - m[1][2] = -look[1]; - m[1][3] = 0.0; - - m[2][0] = u[2]; - m[2][1] = v[2]; - m[2][2] = -look[2]; - m[2][3] = 0.0; - - m[3][0] = eye[0]; - m[3][1] = eye[1]; - m[3][2] = eye[2]; - m[3][3] = 1.0; -#else - m[0][0] = u[0]; - m[1][0] = v[0]; - m[2][0] = -look[0]; - m[3][0] = eye[0]; - - m[0][1] = u[1]; - m[1][1] = v[1]; - m[2][1] = -look[1]; - m[3][1] = eye[1]; - - m[0][2] = u[2]; - m[1][2] = v[2]; - m[2][2] = -look[2]; - m[3][2] = eye[2]; - - m[0][3] = 0.0; - m[1][3] = 0.0; - m[2][3] = 0.0; - m[3][3] = 1.0; - -#endif -} - -void Matrix::Inverse(float m[4][4]) { - /* - * codes from intel web - * cramer's rule version - */ - int i, j; - float tmp[12]; /* tmp array for pairs */ - float tsrc[16]; /* array of transpose source matrix */ - float det; /* determinant */ - - /* transpose matrix */ - for (i = 0; i < 4; i++) { - tsrc[i] = m[i][0]; - tsrc[i + 4] = m[i][1]; - tsrc[i + 8] = m[i][2]; - tsrc[i + 12] = m[i][3]; - } - - /* calculate pair for first 8 elements(cofactors) */ - tmp[0] = tsrc[10] * tsrc[15]; - tmp[1] = tsrc[11] * tsrc[14]; - tmp[2] = tsrc[9] * tsrc[15]; - tmp[3] = tsrc[11] * tsrc[13]; - tmp[4] = tsrc[9] * tsrc[14]; - tmp[5] = tsrc[10] * tsrc[13]; - tmp[6] = tsrc[8] * tsrc[15]; - tmp[7] = tsrc[11] * tsrc[12]; - tmp[8] = tsrc[8] * tsrc[14]; - tmp[9] = tsrc[10] * tsrc[12]; - tmp[10] = tsrc[8] * tsrc[13]; - tmp[11] = tsrc[9] * tsrc[12]; - - /* calculate first 8 elements(cofactors) */ - m[0][0] = tmp[0] * tsrc[5] + tmp[3] * tsrc[6] + tmp[4] * tsrc[7]; - m[0][0] -= tmp[1] * tsrc[5] + tmp[2] * tsrc[6] + tmp[5] * tsrc[7]; - m[0][1] = tmp[1] * tsrc[4] + tmp[6] * tsrc[6] + tmp[9] * tsrc[7]; - m[0][1] -= tmp[0] * tsrc[4] + tmp[7] * tsrc[6] + tmp[8] * tsrc[7]; - m[0][2] = tmp[2] * tsrc[4] + tmp[7] * tsrc[5] + tmp[10] * tsrc[7]; - m[0][2] -= tmp[3] * tsrc[4] + tmp[6] * tsrc[5] + tmp[11] * tsrc[7]; - m[0][3] = tmp[5] * tsrc[4] + tmp[8] * tsrc[5] + tmp[11] * tsrc[6]; - m[0][3] -= tmp[4] * tsrc[4] + tmp[9] * tsrc[5] + tmp[10] * tsrc[6]; - m[1][0] = tmp[1] * tsrc[1] + tmp[2] * tsrc[2] + tmp[5] * tsrc[3]; - m[1][0] -= tmp[0] * tsrc[1] + tmp[3] * tsrc[2] + tmp[4] * tsrc[3]; - m[1][1] = tmp[0] * tsrc[0] + tmp[7] * tsrc[2] + tmp[8] * tsrc[3]; - m[1][1] -= tmp[1] * tsrc[0] + tmp[6] * tsrc[2] + tmp[9] * tsrc[3]; - m[1][2] = tmp[3] * tsrc[0] + tmp[6] * tsrc[1] + tmp[11] * tsrc[3]; - m[1][2] -= tmp[2] * tsrc[0] + tmp[7] * tsrc[1] + tmp[10] * tsrc[3]; - m[1][3] = tmp[4] * tsrc[0] + tmp[9] * tsrc[1] + tmp[10] * tsrc[2]; - m[1][3] -= tmp[5] * tsrc[0] + tmp[8] * tsrc[1] + tmp[11] * tsrc[2]; - - /* calculate pairs for second 8 elements(cofactors) */ - tmp[0] = tsrc[2] * tsrc[7]; - tmp[1] = tsrc[3] * tsrc[6]; - tmp[2] = tsrc[1] * tsrc[7]; - tmp[3] = tsrc[3] * tsrc[5]; - tmp[4] = tsrc[1] * tsrc[6]; - tmp[5] = tsrc[2] * tsrc[5]; - tmp[6] = tsrc[0] * tsrc[7]; - tmp[7] = tsrc[3] * tsrc[4]; - tmp[8] = tsrc[0] * tsrc[6]; - tmp[9] = tsrc[2] * tsrc[4]; - tmp[10] = tsrc[0] * tsrc[5]; - tmp[11] = tsrc[1] * tsrc[4]; - - /* calculate second 8 elements(cofactors) */ - m[2][0] = tmp[0] * tsrc[13] + tmp[3] * tsrc[14] + tmp[4] * tsrc[15]; - m[2][0] -= tmp[1] * tsrc[13] + tmp[2] * tsrc[14] + tmp[5] * tsrc[15]; - m[2][1] = tmp[1] * tsrc[12] + tmp[6] * tsrc[14] + tmp[9] * tsrc[15]; - m[2][1] -= tmp[0] * tsrc[12] + tmp[7] * tsrc[14] + tmp[8] * tsrc[15]; - m[2][2] = tmp[2] * tsrc[12] + tmp[7] * tsrc[13] + tmp[10] * tsrc[15]; - m[2][2] -= tmp[3] * tsrc[12] + tmp[6] * tsrc[13] + tmp[11] * tsrc[15]; - m[2][3] = tmp[5] * tsrc[12] + tmp[8] * tsrc[13] + tmp[11] * tsrc[14]; - m[2][3] -= tmp[4] * tsrc[12] + tmp[9] * tsrc[13] + tmp[10] * tsrc[14]; - m[3][0] = tmp[2] * tsrc[10] + tmp[5] * tsrc[11] + tmp[1] * tsrc[9]; - m[3][0] -= tmp[4] * tsrc[11] + tmp[0] * tsrc[9] + tmp[3] * tsrc[10]; - m[3][1] = tmp[8] * tsrc[11] + tmp[0] * tsrc[8] + tmp[7] * tsrc[10]; - m[3][1] -= tmp[6] * tsrc[10] + tmp[9] * tsrc[11] + tmp[1] * tsrc[8]; - m[3][2] = tmp[6] * tsrc[9] + tmp[11] * tsrc[11] + tmp[3] * tsrc[8]; - m[3][2] -= tmp[10] * tsrc[11] + tmp[2] * tsrc[8] + tmp[7] * tsrc[9]; - m[3][3] = tmp[10] * tsrc[10] + tmp[4] * tsrc[8] + tmp[9] * tsrc[9]; - m[3][3] -= tmp[8] * tsrc[9] + tmp[11] * tsrc[0] + tmp[5] * tsrc[8]; - - /* calculate determinant */ - det = tsrc[0] * m[0][0] + tsrc[1] * m[0][1] + tsrc[2] * m[0][2] + - tsrc[3] * m[0][3]; - - /* calculate matrix inverse */ - det = 1.0f / det; - - for (j = 0; j < 4; j++) { - for (i = 0; i < 4; i++) { - m[j][i] *= det; - } - } -} - -void Matrix::Mult(float dst[4][4], float m0[4][4], float m1[4][4]) { - for (int i = 0; i < 4; ++i) { - for (int j = 0; j < 4; ++j) { - dst[i][j] = 0; - for (int k = 0; k < 4; ++k) { - dst[i][j] += m0[k][j] * m1[i][k]; - } - } - } -} - -void Matrix::MultV(float dst[3], float m[4][4], float v[3]) { - // printf("v = %f, %f, %f\n", v[0], v[1], v[2]); - dst[0] = m[0][0] * v[0] + m[1][0] * v[1] + m[2][0] * v[2] + m[3][0]; - dst[1] = m[0][1] * v[0] + m[1][1] * v[1] + m[2][1] * v[2] + m[3][1]; - dst[2] = m[0][2] * v[0] + m[1][2] * v[1] + m[2][2] * v[2] + m[3][2]; - // printf("m = %f, %f, %f\n", m[3][0], m[3][1], m[3][2]); - // printf("dst = %f, %f, %f\n", dst[0], dst[1], dst[2]); -} diff --git a/examples/raytrace/mesh.h b/examples/raytrace/mesh.h deleted file mode 100644 index 90b1087..0000000 --- a/examples/raytrace/mesh.h +++ /dev/null @@ -1,182 +0,0 @@ -#ifndef EXAMPLE_MESH_H_ -#define EXAMPLE_MESH_H_ - -#include -#include -#include - -namespace example { - - -template -inline void lerp(T dst[3], const T v0[3], const T v1[3], const T v2[3], float u, float v) { - dst[0] = (static_cast(1.0) - u - v) * v0[0] + u * v1[0] + v * v2[0]; - dst[1] = (static_cast(1.0) - u - v) * v0[1] + u * v1[1] + v * v2[1]; - dst[2] = (static_cast(1.0) - u - v) * v0[2] + u * v1[2] + v * v2[2]; -} - -template -inline T vlength(const T v[3]) { - const T d = v[0] * v[0] + v[1] * v[1] + v[2] * v[2]; - if (std::fabs(d) > std::numeric_limits::epsilon()) { - return std::sqrt(d); - } else { - return static_cast(0.0); - } -} - -template -inline void vnormalize(T dst[3], const T v[3]) { - dst[0] = v[0]; - dst[1] = v[1]; - dst[2] = v[2]; - const T len = vlength(v); - if (std::fabs(len) > std::numeric_limits::epsilon()) { - const T inv_len = static_cast(1.0) / len; - dst[0] *= inv_len; - dst[1] *= inv_len; - dst[2] *= inv_len; - } -} - -template -inline void vcross(T dst[3], const T a[3], const T b[3]) { - dst[0] = a[1] * b[2] - a[2] * b[1]; - dst[1] = a[2] * b[0] - a[0] * b[2]; - dst[2] = a[0] * b[1] - a[1] * b[0]; -} - -template -inline void vsub(T dst[3], const T a[3], const T b[3]) { - dst[0] = a[0] - b[0]; - dst[1] = a[1] - b[1]; - dst[2] = a[2] - b[2]; -} - -template -inline void calculate_normal(T Nn[3], const T v0[3], const T v1[3], const T v2[3]) { - T v10[3]; - T v20[3]; - - vsub(v10, v1, v0); - vsub(v20, v2, v0); - - T N[3]; - vcross(N, v20, v10); - vnormalize(Nn, N); -} - -template -class Mesh { - public: - std::string name; - - std::vector vertices; /// [xyz] * num_vertices - std::vector facevarying_normals; /// [xyz] * 3(triangle) * num_faces - std::vector facevarying_tangents; /// [xyz] * 3(triangle) * num_faces - std::vector facevarying_binormals; /// [xyz] * 3(triangle) * num_faces - std::vector facevarying_uvs; /// [xy] * 3(triangle) * num_faces - std::vector - facevarying_vertex_colors; /// [xyz] * 3(triangle) * num_faces - std::vector faces; /// triangle x num_faces - std::vector material_ids; /// index x num_faces - - T pivot_xform[4][4]; - - // --- Required methods in Scene::Traversal. --- - - /// - /// Get the geometric normal and the shading normal at `face_idx' th face. - /// - void GetNormal(T Ng[3], T Ns[3], const unsigned int face_idx, const T u, const T v) const { - // Compute geometric normal. - unsigned int f0, f1, f2; - T v0[3], v1[3], v2[3]; - - f0 = faces[3 * face_idx + 0]; - f1 = faces[3 * face_idx + 1]; - f2 = faces[3 * face_idx + 2]; - - v0[0] = vertices[3 * f0 + 0]; - v0[1] = vertices[3 * f0 + 1]; - v0[2] = vertices[3 * f0 + 2]; - - v1[0] = vertices[3 * f1 + 0]; - v1[1] = vertices[3 * f1 + 1]; - v1[2] = vertices[3 * f1 + 2]; - - v2[0] = vertices[3 * f2 + 0]; - v2[1] = vertices[3 * f2 + 1]; - v2[2] = vertices[3 * f2 + 2]; - - calculate_normal(Ng, v0, v1, v2); - - if (facevarying_normals.size() > 0) { - - T n0[3], n1[3], n2[3]; - - n0[0] = facevarying_normals[9 * face_idx + 0]; - n0[1] = facevarying_normals[9 * face_idx + 1]; - n0[2] = facevarying_normals[9 * face_idx + 2]; - - n1[0] = facevarying_normals[9 * face_idx + 3]; - n1[1] = facevarying_normals[9 * face_idx + 4]; - n1[2] = facevarying_normals[9 * face_idx + 5]; - - n2[0] = facevarying_normals[9 * face_idx + 6]; - n2[1] = facevarying_normals[9 * face_idx + 7]; - n2[2] = facevarying_normals[9 * face_idx + 8]; - - lerp(Ns, n0, n1, n2, u, v); - - } else { - - // Use geometric normal. - Ns[0] = Ng[0]; - Ns[1] = Ng[1]; - Ns[2] = Ng[2]; - - } - - } - - // --- end of required methods in Scene::Traversal. --- - - /// - /// Get texture coordinate at `face_idx' th face. - /// - void GetTexCoord(T tcoord[3], const unsigned int face_idx, const T u, const T v) { - - if (facevarying_uvs.size() > 0) { - - T t0[3], t1[3], t2[3]; - - t0[0] = facevarying_uvs[6 * face_idx + 0]; - t0[1] = facevarying_uvs[6 * face_idx + 1]; - t0[2] = static_cast(0.0); - - t1[0] = facevarying_uvs[6 * face_idx + 2]; - t1[1] = facevarying_uvs[6 * face_idx + 3]; - t1[2] = static_cast(0.0); - - t2[0] = facevarying_uvs[6 * face_idx + 4]; - t2[1] = facevarying_uvs[6 * face_idx + 5]; - t2[2] = static_cast(0.0); - - lerp(tcoord, t0, t1, t2, u, v); - - } else { - - tcoord[0] = static_cast(0.0); - tcoord[1] = static_cast(0.0); - tcoord[2] = static_cast(0.0); - - } - - } - -}; - -} // namespace example - -#endif // EXAMPLE_MESH_H_ diff --git a/examples/raytrace/nanort.h b/examples/raytrace/nanort.h deleted file mode 100644 index 10f486c..0000000 --- a/examples/raytrace/nanort.h +++ /dev/null @@ -1,2230 +0,0 @@ -// -// NanoRT, single header only modern ray tracing kernel. -// - -/* -The MIT License (MIT) - -Copyright (c) 2015 - 2016 Light Transport Entertainment, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -*/ - -#ifndef NANORT_H_ -#define NANORT_H_ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace nanort { - -// Parallelized BVH build is not yet fully tested, -// thus turn off if you face a problem when building BVH. -#define NANORT_ENABLE_PARALLEL_BUILD (1) - -// ---------------------------------------------------------------------------- -// Small vector class useful for multi-threaded environment. -// -// stack_container.h -// -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// This allocator can be used with STL containers to provide a stack buffer -// from which to allocate memory and overflows onto the heap. This stack buffer -// would be allocated on the stack and allows us to avoid heap operations in -// some situations. -// -// STL likes to make copies of allocators, so the allocator itself can't hold -// the data. Instead, we make the creator responsible for creating a -// StackAllocator::Source which contains the data. Copying the allocator -// merely copies the pointer to this shared source, so all allocators created -// based on our allocator will share the same stack buffer. -// -// This stack buffer implementation is very simple. The first allocation that -// fits in the stack buffer will use the stack buffer. Any subsequent -// allocations will not use the stack buffer, even if there is unused room. -// This makes it appropriate for array-like containers, but the caller should -// be sure to reserve() in the container up to the stack buffer size. Otherwise -// the container will allocate a small array which will "use up" the stack -// buffer. -template -class StackAllocator : public std::allocator { - public: - typedef typename std::allocator::pointer pointer; - typedef typename std::allocator::size_type size_type; - - // Backing store for the allocator. The container owner is responsible for - // maintaining this for as long as any containers using this allocator are - // live. - struct Source { - Source() : used_stack_buffer_(false) {} - - // Casts the buffer in its right type. - T *stack_buffer() { return reinterpret_cast(stack_buffer_); } - const T *stack_buffer() const { - return reinterpret_cast(stack_buffer_); - } - - // - // IMPORTANT: Take care to ensure that stack_buffer_ is aligned - // since it is used to mimic an array of T. - // Be careful while declaring any unaligned types (like bool) - // before stack_buffer_. - // - - // The buffer itself. It is not of type T because we don't want the - // constructors and destructors to be automatically called. Define a POD - // buffer of the right size instead. - char stack_buffer_[sizeof(T[stack_capacity])]; - - // Set when the stack buffer is used for an allocation. We do not track - // how much of the buffer is used, only that somebody is using it. - bool used_stack_buffer_; - }; - - // Used by containers when they want to refer to an allocator of type U. - template - struct rebind { - typedef StackAllocator other; - }; - - // For the straight up copy c-tor, we can share storage. - StackAllocator(const StackAllocator &rhs) - : source_(rhs.source_) {} - - // ISO C++ requires the following constructor to be defined, - // and std::vector in VC++2008SP1 Release fails with an error - // in the class _Container_base_aux_alloc_real (from ) - // if the constructor does not exist. - // For this constructor, we cannot share storage; there's - // no guarantee that the Source buffer of Ts is large enough - // for Us. - // TODO(Google): If we were fancy pants, perhaps we could share storage - // iff sizeof(T) == sizeof(U). - template - StackAllocator(const StackAllocator &other) - : source_(NULL) { - (void)other; - } - - explicit StackAllocator(Source *source) : source_(source) {} - - // Actually do the allocation. Use the stack buffer if nobody has used it yet - // and the size requested fits. Otherwise, fall through to the standard - // allocator. - pointer allocate(size_type n, void *hint = 0) { - if (source_ != NULL && !source_->used_stack_buffer_ && - n <= stack_capacity) { - source_->used_stack_buffer_ = true; - return source_->stack_buffer(); - } else { - return std::allocator::allocate(n, hint); - } - } - - // Free: when trying to free the stack buffer, just mark it as free. For - // non-stack-buffer pointers, just fall though to the standard allocator. - void deallocate(pointer p, size_type n) { - if (source_ != NULL && p == source_->stack_buffer()) - source_->used_stack_buffer_ = false; - else - std::allocator::deallocate(p, n); - } - - private: - Source *source_; -}; - -// A wrapper around STL containers that maintains a stack-sized buffer that the -// initial capacity of the vector is based on. Growing the container beyond the -// stack capacity will transparently overflow onto the heap. The container must -// support reserve(). -// -// WATCH OUT: the ContainerType MUST use the proper StackAllocator for this -// type. This object is really intended to be used only internally. You'll want -// to use the wrappers below for different types. -template -class StackContainer { - public: - typedef TContainerType ContainerType; - typedef typename ContainerType::value_type ContainedType; - typedef StackAllocator Allocator; - - // Allocator must be constructed before the container! - StackContainer() : allocator_(&stack_data_), container_(allocator_) { - // Make the container use the stack allocation by reserving our buffer size - // before doing anything else. - container_.reserve(stack_capacity); - } - - // Getters for the actual container. - // - // Danger: any copies of this made using the copy constructor must have - // shorter lifetimes than the source. The copy will share the same allocator - // and therefore the same stack buffer as the original. Use std::copy to - // copy into a "real" container for longer-lived objects. - ContainerType &container() { return container_; } - const ContainerType &container() const { return container_; } - - // Support operator-> to get to the container. This allows nicer syntax like: - // StackContainer<...> foo; - // std::sort(foo->begin(), foo->end()); - ContainerType *operator->() { return &container_; } - const ContainerType *operator->() const { return &container_; } - -#ifdef UNIT_TEST - // Retrieves the stack source so that that unit tests can verify that the - // buffer is being used properly. - const typename Allocator::Source &stack_data() const { return stack_data_; } -#endif - - protected: - typename Allocator::Source stack_data_; - unsigned char pad_[7]; - Allocator allocator_; - ContainerType container_; - - // DISALLOW_EVIL_CONSTRUCTORS(StackContainer); - StackContainer(const StackContainer &); - void operator=(const StackContainer &); -}; - -// StackVector -// -// Example: -// StackVector foo; -// foo->push_back(22); // we have overloaded operator-> -// foo[0] = 10; // as well as operator[] -template -class StackVector - : public StackContainer >, - stack_capacity> { - public: - StackVector() - : StackContainer >, - stack_capacity>() {} - - // We need to put this in STL containers sometimes, which requires a copy - // constructor. We can't call the regular copy constructor because that will - // take the stack buffer from the original. Here, we create an empty object - // and make a stack buffer of its own. - StackVector(const StackVector &other) - : StackContainer >, - stack_capacity>() { - this->container().assign(other->begin(), other->end()); - } - - StackVector &operator=( - const StackVector &other) { - this->container().assign(other->begin(), other->end()); - return *this; - } - - // Vectors are commonly indexed, which isn't very convenient even with - // operator-> (using "->at()" does exception stuff we don't want). - T &operator[](size_t i) { return this->container().operator[](i); } - const T &operator[](size_t i) const { - return this->container().operator[](i); - } -}; - -// ---------------------------------------------------------------------------- - -template -class real3 { - public: - real3() {} - real3(T x) { - v[0] = x; - v[1] = x; - v[2] = x; - } - real3(T xx, T yy, T zz) { - v[0] = xx; - v[1] = yy; - v[2] = zz; - } - explicit real3(const T *p) { - v[0] = p[0]; - v[1] = p[1]; - v[2] = p[2]; - } - - inline T x() const { return v[0]; } - inline T y() const { return v[1]; } - inline T z() const { return v[2]; } - - real3 operator*(T f) const { return real3(x() * f, y() * f, z() * f); } - real3 operator-(const real3 &f2) const { - return real3(x() - f2.x(), y() - f2.y(), z() - f2.z()); - } - real3 operator*(const real3 &f2) const { - return real3(x() * f2.x(), y() * f2.y(), z() * f2.z()); - } - real3 operator+(const real3 &f2) const { - return real3(x() + f2.x(), y() + f2.y(), z() + f2.z()); - } - real3 &operator+=(const real3 &f2) { - v[0] += f2.x(); - v[1] += f2.y(); - v[2] += f2.z(); - return (*this); - } - real3 operator/(const real3 &f2) const { - return real3(x() / f2.x(), y() / f2.y(), z() / f2.z()); - } - real3 operator-() const { - return real3(-x(), -y(), -z()); - } - T operator[](int i) const { return v[i]; } - T &operator[](int i) { return v[i]; } - - T v[3]; - // T pad; // for alignment(when T = float) -}; - -template -inline real3 operator*(T f, const real3 &v) { - return real3(v.x() * f, v.y() * f, v.z() * f); -} - -template -inline real3 vneg(const real3 &rhs) { - return real3(-rhs.x(), -rhs.y(), -rhs.z()); -} - -template -inline T vlength(const real3 &rhs) { - return std::sqrt(rhs.x() * rhs.x() + rhs.y() * rhs.y() + rhs.z() * rhs.z()); -} - -template -inline real3 vnormalize(const real3 &rhs) { - real3 v = rhs; - T len = vlength(rhs); - if (fabs(len) > 1.0e-6f) { - float inv_len = 1.0f / len; - v.v[0] *= inv_len; - v.v[1] *= inv_len; - v.v[2] *= inv_len; - } - return v; -} - -template -inline real3 vcross(real3 a, real3 b) { - real3 c; - c[0] = a[1] * b[2] - a[2] * b[1]; - c[1] = a[2] * b[0] - a[0] * b[2]; - c[2] = a[0] * b[1] - a[1] * b[0]; - return c; -} - -template -inline T vdot(real3 a, real3 b) { - return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; -} - -template -inline const real *get_vertex_addr(const real *p, const size_t idx, - const size_t stride_bytes) { - return reinterpret_cast( - reinterpret_cast(p) + idx * stride_bytes); -} - -template -class Ray { - public: - Ray() - : min_t(static_cast(0.0)) - , max_t(std::numeric_limits::max()) { - org[0] = static_cast(0.0); - org[1] = static_cast(0.0); - org[2] = static_cast(0.0); - dir[0] = static_cast(0.0); - dir[1] = static_cast(0.0); - dir[2] = static_cast(-1.0); - } - - T org[3]; // must set - T dir[3]; // must set - T min_t; // minium ray hit distance. - T max_t; // maximum ray hit distance. - T inv_dir[3]; // filled internally - int dir_sign[3]; // filled internally -}; - -template -class BVHNode { - public: - BVHNode() {} - ~BVHNode() {} - - T bmin[3]; - T bmax[3]; - - int flag; // 1 = leaf node, 0 = branch node - int axis; - - // leaf - // data[0] = npoints - // data[1] = index - // - // branch - // data[0] = child[0] - // data[1] = child[1] - unsigned int data[2]; -}; - -template -class IntersectComparator { - public: - bool operator()(const H &a, const H &b) const { return a.t < b.t; } -}; - -/// BVH build option. -template -struct BVHBuildOptions { - T cost_t_aabb; - unsigned int min_leaf_primitives; - unsigned int max_tree_depth; - unsigned int bin_size; - unsigned int shallow_depth; - unsigned int min_primitives_for_parallel_build; - - // Cache bounding box computation. - // Requires more memory, but BVHbuild can be faster. - bool cache_bbox; - unsigned char pad[3]; - - // Set default value: Taabb = 0.2 - BVHBuildOptions() - : cost_t_aabb(0.2f), - min_leaf_primitives(4), - max_tree_depth(256), - bin_size(64), - shallow_depth(3), - min_primitives_for_parallel_build(1024 * 128), - cache_bbox(false) {} -}; - -/// BVH build statistics. -class BVHBuildStatistics { - public: - unsigned int max_tree_depth; - unsigned int num_leaf_nodes; - unsigned int num_branch_nodes; - float build_secs; - - // Set default value: Taabb = 0.2 - BVHBuildStatistics() - : max_tree_depth(0), - num_leaf_nodes(0), - num_branch_nodes(0), - build_secs(0.0f) {} -}; - -/// BVH trace option. -class BVHTraceOptions { - public: - // Hit only for face IDs in indexRange. - // This feature is good to mimic something like glDrawArrays() - unsigned int prim_ids_range[2]; - bool cull_back_face; - unsigned char pad[3]; ///< Padding(not used) - - BVHTraceOptions() { - prim_ids_range[0] = 0; - prim_ids_range[1] = 0x7FFFFFFF; // Up to 2G face IDs. - cull_back_face = false; - } -}; - -template -class BBox { - public: - real3 bmin; - real3 bmax; - - BBox() { - bmin[0] = bmin[1] = bmin[2] = std::numeric_limits::max(); - bmax[0] = bmax[1] = bmax[2] = -std::numeric_limits::max(); - } -}; - -template -class NodeHit -{ - public: - NodeHit() - : t_min(std::numeric_limits::max()) - , t_max(-std::numeric_limits::max()) - , node_id(static_cast(-1)) { - } - - ~NodeHit() {} - - T t_min; - T t_max; - unsigned int node_id; -}; - -template -class NodeHitComparator -{ - public: - inline bool operator()(const NodeHit &a, const NodeHit &b) { - return a.t_min < b.t_min; - } -}; - -template -class BVHAccel { - public: - BVHAccel() : pad0_(0) { (void)pad0_; } - ~BVHAccel() {} - - /// Build BVH for input primitives. - template - bool Build(const unsigned int num_primitives, - const P &p, const Pred &pred, const BVHBuildOptions &options = BVHBuildOptions()); - - /// Get statistics of built BVH tree. Valid after Build() - BVHBuildStatistics GetStatistics() const { return stats_; } - - /// Dump built BVH to the file. - bool Dump(const char *filename); - - /// Load BVH binary - bool Load(const char *filename); - - void Debug(); - - /// Traverse into BVH along ray and find closest hit point & primitive if - /// found - template - bool Traverse(const Ray &ray, - const I &intersector, - H *isect, - const BVHTraceOptions& options = BVHTraceOptions()) const; - -#if 0 - /// Multi-hit ray traversal - /// Returns `max_intersections` frontmost intersections - template - bool MultiHitTraverse(const Ray &ray, - int max_intersections, - const I &intersector, - StackVector *isects, - const BVHTraceOptions &options = BVHTraceOptions()) const; -#endif - - /// - /// List up nodes which intersects along the ray. - /// This function is useful for two-level BVH traversal. - /// - template - bool ListNodeIntersections(const Ray &ray, - int max_intersections, - const I &intersector, - StackVector, 128> *hits) const; - - const std::vector > &GetNodes() const { return nodes_; } - const std::vector &GetIndices() const { return indices_; } - - /// Returns bounding box of built BVH. - void BoundingBox(T bmin[3], T bmax[3]) const { - if (nodes_.empty()) { - bmin[0] = bmin[1] = bmin[2] = std::numeric_limits::max(); - bmax[0] = bmax[1] = bmax[2] = -std::numeric_limits::max(); - } else { - bmin[0] = nodes_[0].bmin[0]; - bmin[1] = nodes_[0].bmin[1]; - bmin[2] = nodes_[0].bmin[2]; - bmax[0] = nodes_[0].bmax[0]; - bmax[1] = nodes_[0].bmax[1]; - bmax[2] = nodes_[0].bmax[2]; - } - } - - bool IsValid() const { return nodes_.size() > 0; } - - private: -#if NANORT_ENABLE_PARALLEL_BUILD - typedef struct { - unsigned int left_idx; - unsigned int right_idx; - unsigned int offset; - } ShallowNodeInfo; - - // Used only during BVH construction - std::vector shallow_node_infos_; - - /// Builds shallow BVH tree recursively. - template - unsigned int BuildShallowTree(std::vector > *out_nodes, - unsigned int left_idx, unsigned int right_idx, - unsigned int depth, - unsigned int max_shallow_depth, const P &p, - const Pred &pred); -#endif - - /// Builds BVH tree recursively. - template - unsigned int BuildTree(BVHBuildStatistics *out_stat, - std::vector > *out_nodes, - unsigned int left_idx, unsigned int right_idx, - unsigned int depth, const P &p, const Pred &pred); - - template - bool TestLeafNode(const BVHNode &node, const Ray &ray, - const I &intersector) const; - - template - bool TestLeafNodeIntersections(const BVHNode &node, - const Ray &ray, - const int max_intersections, - const I &intersector, - std::priority_queue, std::vector >, NodeHitComparator > *isect_pq) const; - -#if 0 - template - bool MultiHitTestLeafNode(std::priority_queue, Comp> *isect_pq, - int max_intersections, - const BVHNode &node, const Ray &ray, - const I &intersector) const; -#endif - - std::vector > nodes_; - std::vector indices_; // max 4G triangles. - std::vector > bboxes_; - BVHBuildOptions options_; - BVHBuildStatistics stats_; - unsigned int pad0_; -}; - -// Predefined SAH predicator for triangle. -template -class TriangleSAHPred { - public: - TriangleSAHPred( - const T *vertices, const unsigned int *faces, - size_t vertex_stride_bytes) // e.g. 12 for sizeof(float) * XYZ - : axis_(0), - pos_(0.0f), - vertices_(vertices), - faces_(faces), - vertex_stride_bytes_(vertex_stride_bytes) {} - - void Set(int axis, T pos) const { - axis_ = axis; - pos_ = pos; - } - - bool operator()(unsigned int i) const { - int axis = axis_; - T pos = pos_; - - unsigned int i0 = faces_[3 * i + 0]; - unsigned int i1 = faces_[3 * i + 1]; - unsigned int i2 = faces_[3 * i + 2]; - - real3 p0(get_vertex_addr(vertices_, i0, vertex_stride_bytes_)); - real3 p1(get_vertex_addr(vertices_, i1, vertex_stride_bytes_)); - real3 p2(get_vertex_addr(vertices_, i2, vertex_stride_bytes_)); - - T center = p0[axis] + p1[axis] + p2[axis]; - - return (center < pos * 3.0); - } - - private: - mutable int axis_; - mutable T pos_; - const T *vertices_; - const unsigned int *faces_; - const size_t vertex_stride_bytes_; -}; - -// Predefined Triangle mesh geometry. -template -class TriangleMesh { - public: - TriangleMesh( - const T *vertices, const unsigned int *faces, - const size_t vertex_stride_bytes) // e.g. 12 for sizeof(float) * XYZ - : vertices_(vertices), - faces_(faces), - vertex_stride_bytes_(vertex_stride_bytes) {} - - /// Compute bounding box for `prim_index`th triangle. - /// This function is called for each primitive in BVH build. - void BoundingBox(real3 *bmin, real3 *bmax, - unsigned int prim_index) const { - (*bmin)[0] = get_vertex_addr(vertices_, faces_[3 * prim_index + 0], - vertex_stride_bytes_)[0]; - (*bmin)[1] = get_vertex_addr(vertices_, faces_[3 * prim_index + 0], - vertex_stride_bytes_)[1]; - (*bmin)[2] = get_vertex_addr(vertices_, faces_[3 * prim_index + 0], - vertex_stride_bytes_)[2]; - (*bmax)[0] = get_vertex_addr(vertices_, faces_[3 * prim_index + 0], - vertex_stride_bytes_)[0]; - (*bmax)[1] = get_vertex_addr(vertices_, faces_[3 * prim_index + 0], - vertex_stride_bytes_)[1]; - (*bmax)[2] = get_vertex_addr(vertices_, faces_[3 * prim_index + 0], - vertex_stride_bytes_)[2]; - - for (unsigned int i = 1; i < 3; i++) { - for (unsigned int k = 0; k < 3; k++) { - if ((*bmin)[static_cast(k)] > - get_vertex_addr(vertices_, faces_[3 * prim_index + i], - vertex_stride_bytes_)[k]) { - (*bmin)[static_cast(k)] = get_vertex_addr( - vertices_, faces_[3 * prim_index + i], vertex_stride_bytes_)[k]; - } - if ((*bmax)[static_cast(k)] < - get_vertex_addr(vertices_, faces_[3 * prim_index + i], - vertex_stride_bytes_)[k]) { - (*bmax)[static_cast(k)] = get_vertex_addr( - vertices_, faces_[3 * prim_index + i], vertex_stride_bytes_)[k]; - } - } - } - } - - const T *vertices_; - const unsigned int *faces_; - const size_t vertex_stride_bytes_; -}; - -template -class TriangleIntersection { - public: - T u; - T v; - - // Required member variables. - T t; - unsigned int prim_id; -}; - -template > -class TriangleIntersector { - public: - TriangleIntersector(const T *vertices, const unsigned int *faces, - const size_t vertex_stride_bytes) // e.g. - // vertex_stride_bytes - // = 12 = sizeof(float) - // * 3 - : vertices_(vertices), - faces_(faces), - vertex_stride_bytes_(vertex_stride_bytes) {} - - // For Watertight Ray/Triangle Intersection. - typedef struct { - T Sx; - T Sy; - T Sz; - int kx; - int ky; - int kz; - } RayCoeff; - - /// Do ray interesection stuff for `prim_index` th primitive and return hit - /// distance `t`, - /// varycentric coordinate `u` and `v`. - /// Returns true if there's intersection. - bool Intersect(T *t_inout, unsigned int prim_index) const { - if ((prim_index < trace_options_.prim_ids_range[0]) || - (prim_index >= trace_options_.prim_ids_range[1])) { - return false; - } - - const unsigned int f0 = faces_[3 * prim_index + 0]; - const unsigned int f1 = faces_[3 * prim_index + 1]; - const unsigned int f2 = faces_[3 * prim_index + 2]; - - const real3 p0(get_vertex_addr(vertices_, f0 + 0, vertex_stride_bytes_)); - const real3 p1(get_vertex_addr(vertices_, f1 + 0, vertex_stride_bytes_)); - const real3 p2(get_vertex_addr(vertices_, f2 + 0, vertex_stride_bytes_)); - - const real3 A = p0 - ray_org_; - const real3 B = p1 - ray_org_; - const real3 C = p2 - ray_org_; - - const T Ax = A[ray_coeff_.kx] - ray_coeff_.Sx * A[ray_coeff_.kz]; - const T Ay = A[ray_coeff_.ky] - ray_coeff_.Sy * A[ray_coeff_.kz]; - const T Bx = B[ray_coeff_.kx] - ray_coeff_.Sx * B[ray_coeff_.kz]; - const T By = B[ray_coeff_.ky] - ray_coeff_.Sy * B[ray_coeff_.kz]; - const T Cx = C[ray_coeff_.kx] - ray_coeff_.Sx * C[ray_coeff_.kz]; - const T Cy = C[ray_coeff_.ky] - ray_coeff_.Sy * C[ray_coeff_.kz]; - - T U = Cx * By - Cy * Bx; - T V = Ax * Cy - Ay * Cx; - T W = Bx * Ay - By * Ax; - - // Fall back to test against edges using double precision. - if (U == 0.0 || V == 0.0 || W == 0.0) { - double CxBy = static_cast(Cx) * static_cast(By); - double CyBx = static_cast(Cy) * static_cast(Bx); - U = static_cast(CxBy - CyBx); - - double AxCy = static_cast(Ax) * static_cast(Cy); - double AyCx = static_cast(Ay) * static_cast(Cx); - V = static_cast(AxCy - AyCx); - - double BxAy = static_cast(Bx) * static_cast(Ay); - double ByAx = static_cast(By) * static_cast(Ax); - W = static_cast(BxAy - ByAx); - } - - if (trace_options_.cull_back_face) { - if (U < 0.0 || V < 0.0 || W < 0.0) return false; - } else { - if ((U < 0.0 || V < 0.0 || W < 0.0) && (U > 0.0 || V > 0.0 || W > 0.0)) { - return false; - } - } - - T det = U + V + W; - if (det == 0.0) return false; - - const T Az = ray_coeff_.Sz * A[ray_coeff_.kz]; - const T Bz = ray_coeff_.Sz * B[ray_coeff_.kz]; - const T Cz = ray_coeff_.Sz * C[ray_coeff_.kz]; - const T D = U * Az + V * Bz + W * Cz; - - const T rcpDet = static_cast(1.0) / det; - T tt = D * rcpDet; - - if (tt > (*t_inout)) { - return false; - } - - (*t_inout) = tt; - // Use Thomas-Mueller style barycentric coord. - // U + V + W = 1.0 and interp(p) = U * p0 + V * p1 + W * p2 - // We want interp(p) = (1 - u - v) * p0 + u * v1 + v * p2; - // => u = V, v = W. - u_ = V * rcpDet; - v_ = W * rcpDet; - - return true; - } - - /// Returns the nearest hit distance. - T GetT() const { return t_; } - - /// Update is called when initializing intesection and nearest hit is found. - void Update(T t, unsigned int prim_idx) const { - t_ = t; - prim_id_ = prim_idx; - } - - /// Prepare BVH traversal(e.g. compute inverse ray direction) - /// This function is called only once in BVH traversal. - void PrepareTraversal(const Ray &ray, - const BVHTraceOptions &trace_options) const { - ray_org_[0] = ray.org[0]; - ray_org_[1] = ray.org[1]; - ray_org_[2] = ray.org[2]; - - // Calculate dimension where the ray direction is maximal. - ray_coeff_.kz = 0; - T absDir = std::fabs(ray.dir[0]); - if (absDir < std::fabs(ray.dir[1])) { - ray_coeff_.kz = 1; - absDir = std::fabs(ray.dir[1]); - } - if (absDir < std::fabs(ray.dir[2])) { - ray_coeff_.kz = 2; - absDir = std::fabs(ray.dir[2]); - } - - ray_coeff_.kx = ray_coeff_.kz + 1; - if (ray_coeff_.kx == 3) ray_coeff_.kx = 0; - ray_coeff_.ky = ray_coeff_.kx + 1; - if (ray_coeff_.ky == 3) ray_coeff_.ky = 0; - - // Swap kx and ky dimention to preserve widing direction of triangles. - if (ray.dir[ray_coeff_.kz] < 0.0f) std::swap(ray_coeff_.kx, ray_coeff_.ky); - - // Claculate shear constants. - ray_coeff_.Sx = ray.dir[ray_coeff_.kx] / ray.dir[ray_coeff_.kz]; - ray_coeff_.Sy = ray.dir[ray_coeff_.ky] / ray.dir[ray_coeff_.kz]; - ray_coeff_.Sz = 1.0f / ray.dir[ray_coeff_.kz]; - - trace_options_ = trace_options; - - u_ = 0.0f; - v_ = 0.0f; - } - - /// Post BVH traversal stuff. - /// Fill `isect` if there is a hit. - void PostTraversal(const Ray &ray, bool hit, H *isect) const { - if (hit && isect) { - (*isect).t = t_; - (*isect).u = u_; - (*isect).v = v_; - (*isect).prim_id = prim_id_; - } - (void)ray; - } - - private: - - const T *vertices_; - const unsigned int *faces_; - const size_t vertex_stride_bytes_; - - mutable real3 ray_org_; - mutable RayCoeff ray_coeff_; - mutable BVHTraceOptions trace_options_; - - mutable T t_; - mutable T u_; - mutable T v_; - mutable unsigned int prim_id_; -}; - -// -// Robust BVH Ray Traversal : http://jcgt.org/published/0002/02/02/paper.pdf -// - -// NaN-safe min and max function. -template -const T &safemin(const T &a, const T &b) { - return (a < b) ? a : b; -} -template -const T &safemax(const T &a, const T &b) { - return (a > b) ? a : b; -} - -// -// SAH functions -// -struct BinBuffer { - explicit BinBuffer(unsigned int size) { - bin_size = size; - bin.resize(2 * 3 * size); - clear(); - } - - void clear() { memset(&bin[0], 0, sizeof(size_t) * 2 * 3 * bin_size); } - - std::vector bin; // (min, max) * xyz * binsize - unsigned int bin_size; - unsigned int pad0; -}; - -template -inline T CalculateSurfaceArea(const real3 &min, const real3 &max) { - real3 box = max - min; - return static_cast(2.0) * (box[0] * box[1] + box[1] * box[2] + box[2] * box[0]); -} - -template -inline void GetBoundingBoxOfTriangle(real3 *bmin, real3 *bmax, - const T *vertices, - const unsigned int *faces, - unsigned int index) { - unsigned int f0 = faces[3 * index + 0]; - unsigned int f1 = faces[3 * index + 1]; - unsigned int f2 = faces[3 * index + 2]; - - real3 p[3]; - - p[0] = real3(&vertices[3 * f0]); - p[1] = real3(&vertices[3 * f1]); - p[2] = real3(&vertices[3 * f2]); - - (*bmin) = p[0]; - (*bmax) = p[0]; - - for (int i = 1; i < 3; i++) { - (*bmin)[0] = std::min((*bmin)[0], p[i][0]); - (*bmin)[1] = std::min((*bmin)[1], p[i][1]); - (*bmin)[2] = std::min((*bmin)[2], p[i][2]); - - (*bmax)[0] = std::max((*bmax)[0], p[i][0]); - (*bmax)[1] = std::max((*bmax)[1], p[i][1]); - (*bmax)[2] = std::max((*bmax)[2], p[i][2]); - } -} - -template -inline void ContributeBinBuffer(BinBuffer *bins, // [out] - const real3 &scene_min, - const real3 &scene_max, - unsigned int *indices, unsigned int left_idx, - unsigned int right_idx, const P &p) { - T bin_size = static_cast(bins->bin_size); - - // Calculate extent - real3 scene_size, scene_inv_size; - scene_size = scene_max - scene_min; - for (int i = 0; i < 3; ++i) { - assert(scene_size[i] >= 0.0); - - if (scene_size[i] > 0.0) { - scene_inv_size[i] = bin_size / scene_size[i]; - } else { - scene_inv_size[i] = 0.0; - } - } - - // Clear bin data - std::fill(bins->bin.begin(), bins->bin.end(), 0); - // memset(&bins->bin[0], 0, sizeof(2 * 3 * bins->bin_size)); - - size_t idx_bmin[3]; - size_t idx_bmax[3]; - - for (size_t i = left_idx; i < right_idx; i++) { - // - // Quantize the position into [0, BIN_SIZE) - // - // q[i] = (int)(p[i] - scene_bmin) / scene_size - // - real3 bmin; - real3 bmax; - - p.BoundingBox(&bmin, &bmax, indices[i]); - // GetBoundingBoxOfTriangle(&bmin, &bmax, vertices, faces, indices[i]); - - real3 quantized_bmin = (bmin - scene_min) * scene_inv_size; - real3 quantized_bmax = (bmax - scene_min) * scene_inv_size; - - // idx is now in [0, BIN_SIZE) - for (int j = 0; j < 3; ++j) { - int q0 = static_cast(quantized_bmin[j]); - if (q0 < 0) q0 = 0; - int q1 = static_cast(quantized_bmax[j]); - if (q1 < 0) q1 = 0; - - idx_bmin[j] = static_cast(q0); - idx_bmax[j] = static_cast(q1); - - if (idx_bmin[j] >= bin_size) - idx_bmin[j] = static_cast(bin_size) - 1; - if (idx_bmax[j] >= bin_size) - idx_bmax[j] = static_cast(bin_size) - 1; - - assert(idx_bmin[j] < bin_size); - assert(idx_bmax[j] < bin_size); - - // Increment bin counter - bins->bin[0 * (bins->bin_size * 3) + - static_cast(j) * bins->bin_size + idx_bmin[j]] += 1; - bins->bin[1 * (bins->bin_size * 3) + - static_cast(j) * bins->bin_size + idx_bmax[j]] += 1; - } - } -} - -template -inline T SAH(size_t ns1, T leftArea, size_t ns2, T rightArea, T invS, T Taabb, - T Ttri) { - T sah; - - sah = static_cast(2.0) * Taabb + (leftArea * invS) * static_cast(ns1) * Ttri + - (rightArea * invS) * static_cast(ns2) * Ttri; - - return sah; -} - -template -inline bool FindCutFromBinBuffer(T *cut_pos, // [out] xyz - int *minCostAxis, // [out] - const BinBuffer *bins, const real3 &bmin, - const real3 &bmax, size_t num_primitives, - T costTaabb) { // should be in [0.0, 1.0] - const T kEPS = std::numeric_limits::epsilon(); // * epsScale; - - size_t left, right; - real3 bsize, bstep; - real3 bminLeft, bmaxLeft; - real3 bminRight, bmaxRight; - T saLeft, saRight, saTotal; - T pos; - T minCost[3]; - - T costTtri = 1.0f - costTaabb; - - (*minCostAxis) = 0; - - bsize = bmax - bmin; - bstep = bsize * (1.0f / bins->bin_size); - saTotal = CalculateSurfaceArea(bmin, bmax); - - T invSaTotal = 0.0f; - if (saTotal > kEPS) { - invSaTotal = 1.0f / saTotal; - } - - for (int j = 0; j < 3; ++j) { - // - // Compute SAH cost for right side of each cell of the bbox. - // Exclude both extreme side of the bbox. - // - // i: 0 1 2 3 - // +----+----+----+----+----+ - // | | | | | | - // +----+----+----+----+----+ - // - - T minCostPos = bmin[j] + 0.5f * bstep[j]; - minCost[j] = std::numeric_limits::max(); - - left = 0; - right = num_primitives; - bminLeft = bminRight = bmin; - bmaxLeft = bmaxRight = bmax; - - for (int i = 0; i < static_cast(bins->bin_size) - 1; ++i) { - left += bins->bin[0 * (3 * bins->bin_size) + - static_cast(j) * bins->bin_size + - static_cast(i)]; - right -= bins->bin[1 * (3 * bins->bin_size) + - static_cast(j) * bins->bin_size + - static_cast(i)]; - - assert(left <= num_primitives); - assert(right <= num_primitives); - - // - // Split pos bmin + (i + 1) * (bsize / BIN_SIZE) - // +1 for i since we want a position on right side of the cell. - // - - pos = bmin[j] + (i + 0.5f) * bstep[j]; - bmaxLeft[j] = pos; - bminRight[j] = pos; - - saLeft = CalculateSurfaceArea(bminLeft, bmaxLeft); - saRight = CalculateSurfaceArea(bminRight, bmaxRight); - - T cost = - SAH(left, saLeft, right, saRight, invSaTotal, costTaabb, costTtri); - if (cost < minCost[j]) { - // - // Update the min cost - // - minCost[j] = cost; - minCostPos = pos; - // minCostAxis = j; - } - } - - cut_pos[j] = minCostPos; - } - - // cut_axis = minCostAxis; - // cut_pos = minCostPos; - - // Find min cost axis - T cost = minCost[0]; - (*minCostAxis) = 0; - if (cost > minCost[1]) { - (*minCostAxis) = 1; - cost = minCost[1]; - } - if (cost > minCost[2]) { - (*minCostAxis) = 2; - cost = minCost[2]; - } - - return true; -} - -#ifdef _OPENMP -template -void ComputeBoundingBoxOMP(real3 *bmin, real3 *bmax, - const unsigned int *indices, unsigned int left_index, - unsigned int right_index, const P &p) { - { p.BoundingBox(bmin, bmax, indices[left_index]); } - - T local_bmin[3] = {(*bmin)[0], (*bmin)[1], (*bmin)[2]}; - T local_bmax[3] = {(*bmax)[0], (*bmax)[1], (*bmax)[2]}; - - unsigned int n = right_index - left_index; - -#pragma omp parallel firstprivate(local_bmin, local_bmax) if (n > (1024 * 128)) - { -#pragma omp for - for (int i = left_index; i < right_index; i++) { // for each faces - unsigned int idx = indices[i]; - - real3 bbox_min, bbox_max; - p.BoundingBox(&bbox_min, &bbox_max, idx); - for (int k = 0; k < 3; k++) { // xyz - if ((*bmin)[k] > bbox_min[k]) (*bmin)[k] = bbox_min[k]; - if ((*bmax)[k] < bbox_max[k]) (*bmax)[k] = bbox_max[k]; - } - } - -#pragma omp critical - { - for (int k = 0; k < 3; k++) { - if (local_bmin[k] < (*bmin)[k]) { - { - if (local_bmin[k] < (*bmin)[k]) (*bmin)[k] = local_bmin[k]; - } - } - - if (local_bmax[k] > (*bmax)[k]) { - { - if (local_bmax[k] > (*bmax)[k]) (*bmax)[k] = local_bmax[k]; - } - } - } - } - } -} -#endif - -template -inline void ComputeBoundingBox(real3 *bmin, real3 *bmax, - const unsigned int *indices, - unsigned int left_index, - unsigned int right_index, const P &p) { - { - unsigned int idx = indices[left_index]; - p.BoundingBox(bmin, bmax, idx); - } - - { - for (unsigned int i = left_index + 1; i < right_index; - i++) { // for each primitives - unsigned int idx = indices[i]; - real3 bbox_min, bbox_max; - p.BoundingBox(&bbox_min, &bbox_max, idx); - for (int k = 0; k < 3; k++) { // xyz - if ((*bmin)[k] > bbox_min[k]) (*bmin)[k] = bbox_min[k]; - if ((*bmax)[k] < bbox_max[k]) (*bmax)[k] = bbox_max[k]; - } - } - } -} - -template -inline void GetBoundingBox(real3 *bmin, real3 *bmax, - const std::vector > &bboxes, - unsigned int *indices, unsigned int left_index, - unsigned int right_index) { - { - unsigned int i = left_index; - unsigned int idx = indices[i]; - (*bmin)[0] = bboxes[idx].bmin[0]; - (*bmin)[1] = bboxes[idx].bmin[1]; - (*bmin)[2] = bboxes[idx].bmin[2]; - (*bmax)[0] = bboxes[idx].bmax[0]; - (*bmax)[1] = bboxes[idx].bmax[1]; - (*bmax)[2] = bboxes[idx].bmax[2]; - } - - T local_bmin[3] = {(*bmin)[0], (*bmin)[1], (*bmin)[2]}; - T local_bmax[3] = {(*bmax)[0], (*bmax)[1], (*bmax)[2]}; - - { - for (unsigned int i = left_index; i < right_index; i++) { // for each faces - unsigned int idx = indices[i]; - - for (int k = 0; k < 3; k++) { // xyz - T minval = bboxes[idx].bmin[k]; - T maxval = bboxes[idx].bmax[k]; - if (local_bmin[k] > minval) local_bmin[k] = minval; - if (local_bmax[k] < maxval) local_bmax[k] = maxval; - } - } - - for (int k = 0; k < 3; k++) { - (*bmin)[k] = local_bmin[k]; - (*bmax)[k] = local_bmax[k]; - } - } -} - -// -// -- -// - -#if NANORT_ENABLE_PARALLEL_BUILD -template template -unsigned int BVHAccel::BuildShallowTree( - std::vector > *out_nodes, unsigned int left_idx, - unsigned int right_idx, unsigned int depth, unsigned int max_shallow_depth, - const P &p, const Pred &pred) { - assert(left_idx <= right_idx); - - unsigned int offset = static_cast(out_nodes->size()); - - if (stats_.max_tree_depth < depth) { - stats_.max_tree_depth = depth; - } - - real3 bmin, bmax; - ComputeBoundingBox(&bmin, &bmax, &indices_.at(0), left_idx, right_idx, p); - - unsigned int n = right_idx - left_idx; - if ((n <= options_.min_leaf_primitives) || - (depth >= options_.max_tree_depth)) { - // Create leaf node. - BVHNode leaf; - - leaf.bmin[0] = bmin[0]; - leaf.bmin[1] = bmin[1]; - leaf.bmin[2] = bmin[2]; - - leaf.bmax[0] = bmax[0]; - leaf.bmax[1] = bmax[1]; - leaf.bmax[2] = bmax[2]; - - assert(left_idx < std::numeric_limits::max()); - - leaf.flag = 1; // leaf - leaf.data[0] = n; - leaf.data[1] = left_idx; - - out_nodes->push_back(leaf); // atomic update - - stats_.num_leaf_nodes++; - - return offset; - } - - // - // Create branch node. - // - if (depth >= max_shallow_depth) { - // Delay to build tree - ShallowNodeInfo info; - info.left_idx = left_idx; - info.right_idx = right_idx; - info.offset = offset; - shallow_node_infos_.push_back(info); - - // Add dummy node. - BVHNode node; - node.axis = -1; - node.flag = -1; - out_nodes->push_back(node); - - return offset; - - } else { - // - // Compute SAH and find best split axis and position - // - int min_cut_axis = 0; - T cut_pos[3] = {0.0, 0.0, 0.0}; - - BinBuffer bins(options_.bin_size); - ContributeBinBuffer(&bins, bmin, bmax, &indices_.at(0), left_idx, right_idx, - p); - FindCutFromBinBuffer(cut_pos, &min_cut_axis, &bins, bmin, bmax, n, - options_.cost_t_aabb); - - // Try all 3 axis until good cut position avaiable. - unsigned int mid_idx = left_idx; - int cut_axis = min_cut_axis; - for (int axis_try = 0; axis_try < 3; axis_try++) { - unsigned int *begin = &indices_[left_idx]; - unsigned int *end = - &indices_[right_idx - 1] + 1; // mimics end() iterator. - unsigned int *mid = 0; - - // try min_cut_axis first. - cut_axis = (min_cut_axis + axis_try) % 3; - - // @fixme { We want some thing like: std::partition(begin, end, - // pred(cut_axis, cut_pos[cut_axis])); } - pred.Set(cut_axis, cut_pos[cut_axis]); - // - // Split at (cut_axis, cut_pos) - // indices_ will be modified. - // - mid = std::partition(begin, end, pred); - - mid_idx = left_idx + static_cast((mid - begin)); - if ((mid_idx == left_idx) || (mid_idx == right_idx)) { - // Can't split well. - // Switch to object median(which may create unoptimized tree, but - // stable) - mid_idx = left_idx + (n >> 1); - - // Try another axis if there's axis to try. - - } else { - // Found good cut. exit loop. - break; - } - } - - BVHNode node; - node.axis = cut_axis; - node.flag = 0; // 0 = branch - - out_nodes->push_back(node); - - unsigned int left_child_index = 0; - unsigned int right_child_index = 0; - - left_child_index = BuildShallowTree(out_nodes, left_idx, mid_idx, depth + 1, - max_shallow_depth, p, pred); - - right_child_index = BuildShallowTree(out_nodes, mid_idx, right_idx, - depth + 1, max_shallow_depth, p, pred); - - (*out_nodes)[offset].data[0] = left_child_index; - (*out_nodes)[offset].data[1] = right_child_index; - - (*out_nodes)[offset].bmin[0] = bmin[0]; - (*out_nodes)[offset].bmin[1] = bmin[1]; - (*out_nodes)[offset].bmin[2] = bmin[2]; - - (*out_nodes)[offset].bmax[0] = bmax[0]; - (*out_nodes)[offset].bmax[1] = bmax[1]; - (*out_nodes)[offset].bmax[2] = bmax[2]; - } - - stats_.num_branch_nodes++; - - return offset; -} -#endif - -template template -unsigned int BVHAccel::BuildTree( - BVHBuildStatistics *out_stat, std::vector > *out_nodes, - unsigned int left_idx, unsigned int right_idx, unsigned int depth, - const P &p, const Pred &pred) { - assert(left_idx <= right_idx); - - unsigned int offset = static_cast(out_nodes->size()); - - if (out_stat->max_tree_depth < depth) { - out_stat->max_tree_depth = depth; - } - - real3 bmin, bmax; - if (!bboxes_.empty()) { - GetBoundingBox(&bmin, &bmax, bboxes_, &indices_.at(0), left_idx, right_idx); - } else { - ComputeBoundingBox(&bmin, &bmax, &indices_.at(0), left_idx, right_idx, p); - } - - unsigned int n = right_idx - left_idx; - if ((n <= options_.min_leaf_primitives) || - (depth >= options_.max_tree_depth)) { - // Create leaf node. - BVHNode leaf; - - leaf.bmin[0] = bmin[0]; - leaf.bmin[1] = bmin[1]; - leaf.bmin[2] = bmin[2]; - - leaf.bmax[0] = bmax[0]; - leaf.bmax[1] = bmax[1]; - leaf.bmax[2] = bmax[2]; - - assert(left_idx < std::numeric_limits::max()); - - leaf.flag = 1; // leaf - leaf.data[0] = n; - leaf.data[1] = left_idx; - - out_nodes->push_back(leaf); // atomic update - - out_stat->num_leaf_nodes++; - - return offset; - } - - // - // Create branch node. - // - - // - // Compute SAH and find best split axis and position - // - int min_cut_axis = 0; - T cut_pos[3] = {0.0, 0.0, 0.0}; - - BinBuffer bins(options_.bin_size); - ContributeBinBuffer(&bins, bmin, bmax, &indices_.at(0), left_idx, right_idx, - p); - FindCutFromBinBuffer(cut_pos, &min_cut_axis, &bins, bmin, bmax, n, - options_.cost_t_aabb); - - // Try all 3 axis until good cut position avaiable. - unsigned int mid_idx = left_idx; - int cut_axis = min_cut_axis; - for (int axis_try = 0; axis_try < 3; axis_try++) { - unsigned int *begin = &indices_[left_idx]; - unsigned int *end = &indices_[right_idx - 1] + 1; // mimics end() iterator. - unsigned int *mid = 0; - - // try min_cut_axis first. - cut_axis = (min_cut_axis + axis_try) % 3; - - pred.Set(cut_axis, cut_pos[cut_axis]); - - // - // Split at (cut_axis, cut_pos) - // indices_ will be modified. - // - mid = std::partition(begin, end, pred); - - mid_idx = left_idx + static_cast((mid - begin)); - if ((mid_idx == left_idx) || (mid_idx == right_idx)) { - // Can't split well. - // Switch to object median(which may create unoptimized tree, but - // stable) - mid_idx = left_idx + (n >> 1); - - // Try another axis to find better cut. - - } else { - // Found good cut. exit loop. - break; - } - } - - BVHNode node; - node.axis = cut_axis; - node.flag = 0; // 0 = branch - - out_nodes->push_back(node); - - unsigned int left_child_index = 0; - unsigned int right_child_index = 0; - - left_child_index = - BuildTree(out_stat, out_nodes, left_idx, mid_idx, depth + 1, p, pred); - - right_child_index = - BuildTree(out_stat, out_nodes, mid_idx, right_idx, depth + 1, p, pred); - - { - (*out_nodes)[offset].data[0] = left_child_index; - (*out_nodes)[offset].data[1] = right_child_index; - - (*out_nodes)[offset].bmin[0] = bmin[0]; - (*out_nodes)[offset].bmin[1] = bmin[1]; - (*out_nodes)[offset].bmin[2] = bmin[2]; - - (*out_nodes)[offset].bmax[0] = bmax[0]; - (*out_nodes)[offset].bmax[1] = bmax[1]; - (*out_nodes)[offset].bmax[2] = bmax[2]; - } - - out_stat->num_branch_nodes++; - - return offset; -} - -template template -bool BVHAccel::Build(unsigned int num_primitives, - const P &p, const Pred &pred, - const BVHBuildOptions &options) { - options_ = options; - stats_ = BVHBuildStatistics(); - - nodes_.clear(); - bboxes_.clear(); - - assert(options_.bin_size > 1); - - unsigned int n = num_primitives; - - // - // 1. Create triangle indices(this will be permutated in BuildTree) - // - indices_.resize(n); - -#ifdef _OPENMP -#pragma omp parallel for -#endif - for (int i = 0; i < static_cast(n); i++) { - indices_[static_cast(i)] = static_cast(i); - } - - // - // 2. Compute bounding box(optional). - // - real3 bmin, bmax; - if (options.cache_bbox) { - bmin[0] = bmin[1] = bmin[2] = std::numeric_limits::max(); - bmax[0] = bmax[1] = bmax[2] = -std::numeric_limits::max(); - - bboxes_.resize(n); - for (size_t i = 0; i < n; i++) { // for each primitived - unsigned int idx = indices_[i]; - - BBox bbox; - p.BoundingBox(&(bbox.bmin), &(bbox.bmax), static_cast(i)); - bboxes_[idx] = bbox; - - for (int k = 0; k < 3; k++) { // xyz - if (bmin[k] > bbox.bmin[k]) { - bmin[k] = bbox.bmin[k]; - } - if (bmax[k] < bbox.bmax[k]) { - bmax[k] = bbox.bmax[k]; - } - } - } - - } else { -#ifdef _OPENMP - ComputeBoundingBoxOMP(&bmin, &bmax, &indices_.at(0), 0, n, p); -#else - ComputeBoundingBox(&bmin, &bmax, &indices_.at(0), 0, n, p); -#endif - } - -// -// 3. Build tree -// -#ifdef _OPENMP -#if NANORT_ENABLE_PARALLEL_BUILD - - // Do parallel build for enoughly large dataset. - if (n > options.min_primitives_for_parallel_build) { - BuildShallowTree(&nodes_, 0, n, /* root depth */ 0, options.shallow_depth, - p, pred); // [0, n) - - assert(shallow_node_infos_.size() > 0); - - // Build deeper tree in parallel - std::vector > > local_nodes( - shallow_node_infos_.size()); - std::vector local_stats(shallow_node_infos_.size()); - -#pragma omp parallel for - for (int i = 0; i < static_cast(shallow_node_infos_.size()); i++) { - unsigned int left_idx = shallow_node_infos_[i].left_idx; - unsigned int right_idx = shallow_node_infos_[i].right_idx; - BuildTree(&(local_stats[i]), &(local_nodes[i]), left_idx, right_idx, - options.shallow_depth, p, pred); - } - - // Join local nodes - for (int i = 0; i < static_cast(local_nodes.size()); i++) { - assert(!local_nodes[i].empty()); - size_t offset = nodes_.size(); - - // Add offset to child index(for branch node). - for (size_t j = 0; j < local_nodes[i].size(); j++) { - if (local_nodes[i][j].flag == 0) { // branch - local_nodes[i][j].data[0] += offset - 1; - local_nodes[i][j].data[1] += offset - 1; - } - } - - // replace - nodes_[shallow_node_infos_[i].offset] = local_nodes[i][0]; - - // Skip root element of the local node. - nodes_.insert(nodes_.end(), local_nodes[i].begin() + 1, - local_nodes[i].end()); - } - - // Join statistics - for (int i = 0; i < static_cast(local_nodes.size()); i++) { - stats_.max_tree_depth = - std::max(stats_.max_tree_depth, local_stats[i].max_tree_depth); - stats_.num_leaf_nodes += local_stats[i].num_leaf_nodes; - stats_.num_branch_nodes += local_stats[i].num_branch_nodes; - } - - } else { - BuildTree(&stats_, &nodes_, 0, n, - /* root depth */ 0, p, pred); // [0, n) - } - -#else // !NANORT_ENABLE_PARALLEL_BUILD - { - BuildTree(&stats_, &nodes_, 0, n, - /* root depth */ 0, p, pred); // [0, n) - } -#endif -#else // !_OPENMP - { - BuildTree(&stats_, &nodes_, 0, n, - /* root depth */ 0, p, pred); // [0, n) - } -#endif - - return true; -} - -template -void BVHAccel::Debug() { - for (size_t i = 0; i < indices_.size(); i++) { - printf("index[%d] = %d\n", int(i), int(indices_[i])); - } - - for (size_t i = 0; i < nodes_.size(); i++) { - printf("node[%d] : bmin %f, %f, %f, bmax %f, %f, %f\n", - int(i), - nodes_[i].bmin[0], - nodes_[i].bmin[1], - nodes_[i].bmin[1], - nodes_[i].bmax[0], - nodes_[i].bmax[1], - nodes_[i].bmax[1]); - - } -} - -template -bool BVHAccel::Dump(const char *filename) { - FILE *fp = fopen(filename, "wb"); - if (!fp) { - //fprintf(stderr, "[BVHAccel] Cannot write a file: %s\n", filename); - return false; - } - - size_t numNodes = nodes_.size(); - assert(nodes_.size() > 0); - - size_t numIndices = indices_.size(); - - size_t r = 0; - r = fwrite(&numNodes, sizeof(size_t), 1, fp); - assert(r == 1); - - r = fwrite(&nodes_.at(0), sizeof(BVHNode), numNodes, fp); - assert(r == numNodes); - - r = fwrite(&numIndices, sizeof(size_t), 1, fp); - assert(r == 1); - - r = fwrite(&indices_.at(0), sizeof(unsigned int), numIndices, fp); - assert(r == numIndices); - - fclose(fp); - - return true; -} - -template -bool BVHAccel::Load(const char *filename) { - FILE *fp = fopen(filename, "rb"); - if (!fp) { - //fprintf(stderr, "Cannot open file: %s\n", filename); - return false; - } - - size_t numNodes; - size_t numIndices; - - size_t r = 0; - r = fread(&numNodes, sizeof(size_t), 1, fp); - assert(r == 1); - assert(numNodes > 0); - - nodes_.resize(numNodes); - r = fread(&nodes_.at(0), sizeof(BVHNode), numNodes, fp); - assert(r == numNodes); - - r = fread(&numIndices, sizeof(size_t), 1, fp); - assert(r == 1); - - indices_.resize(numIndices); - - r = fread(&indices_.at(0), sizeof(unsigned int), numIndices, fp); - assert(r == numIndices); - - fclose(fp); - - return true; -} - -template -inline bool IntersectRayAABB(T *tminOut, // [out] - T *tmaxOut, // [out] - T min_t, T max_t, const T bmin[3], const T bmax[3], - real3 ray_org, real3 ray_inv_dir, - int ray_dir_sign[3]) { - T tmin, tmax; - - const T min_x = ray_dir_sign[0] ? bmax[0] : bmin[0]; - const T min_y = ray_dir_sign[1] ? bmax[1] : bmin[1]; - const T min_z = ray_dir_sign[2] ? bmax[2] : bmin[2]; - const T max_x = ray_dir_sign[0] ? bmin[0] : bmax[0]; - const T max_y = ray_dir_sign[1] ? bmin[1] : bmax[1]; - const T max_z = ray_dir_sign[2] ? bmin[2] : bmax[2]; - - // X - const T tmin_x = (min_x - ray_org[0]) * ray_inv_dir[0]; - // MaxMult robust BVH traversal(up to 4 ulp). - // 1.0000000000000004 for double precision. - const T tmax_x = (max_x - ray_org[0]) * ray_inv_dir[0] * 1.00000024f; - - // Y - const T tmin_y = (min_y - ray_org[1]) * ray_inv_dir[1]; - const T tmax_y = (max_y - ray_org[1]) * ray_inv_dir[1] * 1.00000024f; - - // Z - const T tmin_z = (min_z - ray_org[2]) * ray_inv_dir[2]; - const T tmax_z = (max_z - ray_org[2]) * ray_inv_dir[2] * 1.00000024f; - - tmin = safemax(tmin_z, safemax(tmin_y, safemax(tmin_x, min_t))); - tmax = safemin(tmax_z, safemin(tmax_y, safemin(tmax_x, max_t))); - - if (tmin <= tmax) { - (*tminOut) = tmin; - (*tmaxOut) = tmax; - - return true; - } - - return false; // no hit -} - -template template -inline bool BVHAccel::TestLeafNode(const BVHNode &node, - const Ray &ray, - const I &intersector) const { - bool hit = false; - - unsigned int num_primitives = node.data[0]; - unsigned int offset = node.data[1]; - - T t = intersector.GetT(); // current hit distance - - real3 ray_org; - ray_org[0] = ray.org[0]; - ray_org[1] = ray.org[1]; - ray_org[2] = ray.org[2]; - - real3 ray_dir; - ray_dir[0] = ray.dir[0]; - ray_dir[1] = ray.dir[1]; - ray_dir[2] = ray.dir[2]; - - for (unsigned int i = 0; i < num_primitives; i++) { - unsigned int prim_idx = indices_[i + offset]; - - T local_t = t; - if (intersector.Intersect(&local_t, prim_idx)) { - if (local_t > ray.min_t) { - // Update isect state - t = local_t; - - intersector.Update(t, prim_idx); - hit = true; - } - } - } - - return hit; -} - -#if 0 // TODO(LTE): Implement -template template -bool BVHAccel::MultiHitTestLeafNode( - std::priority_queue, Comp> *isect_pq, - int max_intersections, - const BVHNode &node, - const Ray &ray, - const I &intersector) const { - bool hit = false; - - unsigned int num_primitives = node.data[0]; - unsigned int offset = node.data[1]; - - T t = std::numeric_limits::max(); - if (isect_pq->size() >= static_cast(max_intersections)) { - t = isect_pq->top().t; // current furthest hit distance - } - - real3 ray_org; - ray_org[0] = ray.org[0]; - ray_org[1] = ray.org[1]; - ray_org[2] = ray.org[2]; - - real3 ray_dir; - ray_dir[0] = ray.dir[0]; - ray_dir[1] = ray.dir[1]; - ray_dir[2] = ray.dir[2]; - - for (unsigned int i = 0; i < num_primitives; i++) { - unsigned int prim_idx = indices_[i + offset]; - - T local_t = t, u = 0.0f, v = 0.0f; - if (intersector.Intersect(&local_t, &u, &v, prim_idx)) { - // Update isect state - if ((local_t > ray.min_t)) { - if (isect_pq->size() < static_cast(max_intersections)) { - H isect; - t = local_t; - isect.t = t; - isect.u = u; - isect.v = v; - isect.prim_id = prim_idx; - isect_pq->push(isect); - - // Update t to furthest distance. - t = ray.max_t; - - hit = true; - } else { - if (local_t < isect_pq->top().t) { - // delete furthest intersection and add new intersection. - isect_pq->pop(); - - H hit; - hit.t = local_t; - hit.u = u; - hit.v = v; - hit.prim_id = prim_idx; - isect_pq->push(hit); - - // Update furthest hit distance - t = isect_pq->top().t; - - hit = true; - } - } - } - } - } - - return hit; -} -#endif - -template template -bool BVHAccel::Traverse(const Ray &ray, - const I &intersector, - H *isect, - const BVHTraceOptions &options) const { - const int kMaxStackDepth = 512; - - T hit_t = ray.max_t; - - int node_stack_index = 0; - unsigned int node_stack[512]; - node_stack[0] = 0; - - // Init isect info as no hit - intersector.Update(hit_t, static_cast(-1)); - - intersector.PrepareTraversal(ray, options); - - int dir_sign[3]; - dir_sign[0] = ray.dir[0] < 0.0f ? 1 : 0; - dir_sign[1] = ray.dir[1] < 0.0f ? 1 : 0; - dir_sign[2] = ray.dir[2] < 0.0f ? 1 : 0; - - // @fixme { Check edge case; i.e., 1/0 } - real3 ray_inv_dir; - ray_inv_dir[0] = 1.0f / (ray.dir[0] + 1.0e-12f); - ray_inv_dir[1] = 1.0f / (ray.dir[1] + 1.0e-12f); - ray_inv_dir[2] = 1.0f / (ray.dir[2] + 1.0e-12f); - - real3 ray_org; - ray_org[0] = ray.org[0]; - ray_org[1] = ray.org[1]; - ray_org[2] = ray.org[2]; - - T min_t = std::numeric_limits::max(); - T max_t = -std::numeric_limits::max(); - - while (node_stack_index >= 0) { - unsigned int index = node_stack[node_stack_index]; - const BVHNode &node = nodes_[index]; - - node_stack_index--; - - bool hit = IntersectRayAABB(&min_t, &max_t, ray.min_t, hit_t, node.bmin, - node.bmax, ray_org, ray_inv_dir, dir_sign); - - if (node.flag == 0) { // branch node - if (hit) { - int order_near = dir_sign[node.axis]; - int order_far = 1 - order_near; - - // Traverse near first. - node_stack[++node_stack_index] = node.data[order_far]; - node_stack[++node_stack_index] = node.data[order_near]; - } - } else { // leaf node - if (hit) { - if (TestLeafNode(node, ray, intersector)) { - hit_t = intersector.GetT(); - } - } - } - } - - assert(node_stack_index < kMaxStackDepth); - - bool hit = (intersector.GetT() < ray.max_t); - intersector.PostTraversal(ray, hit, isect); - - return hit; -} - -template template -inline bool BVHAccel::TestLeafNodeIntersections(const BVHNode &node, - const Ray &ray, - const int max_intersections, - const I &intersector, - std::priority_queue, std::vector >, NodeHitComparator > *isect_pq) const { - bool hit = false; - - unsigned int num_primitives = node.data[0]; - unsigned int offset = node.data[1]; - - real3 ray_org; - ray_org[0] = ray.org[0]; - ray_org[1] = ray.org[1]; - ray_org[2] = ray.org[2]; - - real3 ray_dir; - ray_dir[0] = ray.dir[0]; - ray_dir[1] = ray.dir[1]; - ray_dir[2] = ray.dir[2]; - - intersector.PrepareTraversal(ray); - - for (unsigned int i = 0; i < num_primitives; i++) { - unsigned int prim_idx = indices_[i + offset]; - - T min_t, max_t; - if (intersector.Intersect(&min_t, &max_t, prim_idx)) { - // Always add to isect lists. - NodeHit isect; - isect.t_min = min_t; - isect.t_max = max_t; - isect.node_id = prim_idx; - - if (isect_pq->size() < static_cast(max_intersections)) { - isect_pq->push(isect); - - } else { - if (min_t < isect_pq->top().t_min) { - // delete the furthest intersection and add a new intersection. - isect_pq->pop(); - - isect_pq->push(isect); - - } - } - } - } - - return hit; -} - -template template -bool BVHAccel::ListNodeIntersections(const Ray &ray, - int max_intersections, - const I &intersector, - StackVector, 128> *hits) const { - const int kMaxStackDepth = 512; - - T hit_t = ray.max_t; - - int node_stack_index = 0; - unsigned int node_stack[512]; - node_stack[0] = 0; - - // Stores furthest intersection at top - std::priority_queue, std::vector >, NodeHitComparator > isect_pq; - - (*hits)->clear(); - - int dir_sign[3]; - dir_sign[0] = ray.dir[0] < static_cast(0.0) ? static_cast(1) : static_cast(0); - dir_sign[1] = ray.dir[1] < static_cast(0.0) ? static_cast(1) : static_cast(0); - dir_sign[2] = ray.dir[2] < static_cast(0.0) ? static_cast(1) : static_cast(0); - - // @fixme { Check edge case; i.e., 1/0 } - real3 ray_inv_dir; - ray_inv_dir[0] = static_cast(1.0) / ray.dir[0]; - ray_inv_dir[1] = static_cast(1.0) / ray.dir[1]; - ray_inv_dir[2] = static_cast(1.0) / ray.dir[2]; - - real3 ray_org; - ray_org[0] = ray.org[0]; - ray_org[1] = ray.org[1]; - ray_org[2] = ray.org[2]; - - T min_t, max_t; - while (node_stack_index >= 0) { - unsigned int index = node_stack[node_stack_index]; - const BVHNode &node = nodes_[static_cast(index)]; - - node_stack_index--; - - bool hit = IntersectRayAABB(&min_t, &max_t, ray.min_t, hit_t, node.bmin, - node.bmax, ray_org, ray_inv_dir, dir_sign); - - if (node.flag == 0) { // branch node - if (hit) { - int order_near = dir_sign[node.axis]; - int order_far = 1 - order_near; - - // Traverse near first. - node_stack[++node_stack_index] = node.data[order_far]; - node_stack[++node_stack_index] = node.data[order_near]; - } - - } else { // leaf node - if (hit) { - TestLeafNodeIntersections(node, ray, max_intersections, intersector, &isect_pq); - } - } - } - - assert(node_stack_index < kMaxStackDepth); - (void)kMaxStackDepth; - - if (!isect_pq.empty()) { - // Store intesection in reverse order(make it frontmost order) - size_t n = isect_pq.size(); - (*hits)->resize(n); - for (size_t i = 0; i < n; i++) { - const NodeHit &isect = isect_pq.top(); - (*hits)[n - i - 1] = isect; - isect_pq.pop(); - } - - return true; - } - - return false; -} - -#if 0 // TODO(LTE): Implement -template template -bool BVHAccel::MultiHitTraverse(const Ray &ray, - int max_intersections, - const I &intersector, - StackVector *hits, - const BVHTraceOptions& options) const { - const int kMaxStackDepth = 512; - - T hit_t = ray.max_t; - - int node_stack_index = 0; - unsigned int node_stack[512]; - node_stack[0] = 0; - - // Stores furthest intersection at top - std::priority_queue, Comp> isect_pq; - - (*hits)->clear(); - - // Init isect info as no hit - intersector.Update(hit_t, static_cast(-1)); - - intersector.PrepareTraversal(ray, options); - - int dir_sign[3]; - dir_sign[0] = ray.dir[0] < static_cast(0.0) ? static_cast(1) : static_cast(0); - dir_sign[1] = ray.dir[1] < static_cast(0.0) ? static_cast(1) : static_cast(0); - dir_sign[2] = ray.dir[2] < static_cast(0.0) ? static_cast(1) : static_cast(0); - - // @fixme { Check edge case; i.e., 1/0 } - real3 ray_inv_dir; - ray_inv_dir[0] = static_cast(1.0) / ray.dir[0]; - ray_inv_dir[1] = static_cast(1.0) / ray.dir[1]; - ray_inv_dir[2] = static_cast(1.0) / ray.dir[2]; - - real3 ray_org; - ray_org[0] = ray.org[0]; - ray_org[1] = ray.org[1]; - ray_org[2] = ray.org[2]; - - T min_t, max_t; - while (node_stack_index >= 0) { - unsigned int index = node_stack[node_stack_index]; - const BVHNode &node = nodes_[static_cast(index)]; - - node_stack_index--; - - bool hit = IntersectRayAABB(&min_t, &max_t, ray.min_t, hit_t, node.bmin, - node.bmax, ray_org, ray_inv_dir, dir_sign); - - if (node.flag == 0) { // branch node - if (hit) { - int order_near = dir_sign[node.axis]; - int order_far = 1 - order_near; - - // Traverse near first. - node_stack[++node_stack_index] = node.data[order_far]; - node_stack[++node_stack_index] = node.data[order_near]; - } - - } else { // leaf node - if (hit) { - if (MultiHitTestLeafNode(&isect_pq, max_intersections, node, ray, intersector)) { - // Only update `hit_t` when queue is full. - if (isect_pq.size() >= static_cast(max_intersections)) { - hit_t = isect_pq.top().t; - } - } - } - } - } - - assert(node_stack_index < kMaxStackDepth); - (void)kMaxStackDepth; - - if (!isect_pq.empty()) { - // Store intesection in reverse order(make it frontmost order) - size_t n = isect_pq.size(); - (*hits)->resize(n); - for (size_t i = 0; i < n; i++) { - const H &isect = isect_pq.top(); - (*hits)[n - i - 1] = isect; - isect_pq.pop(); - } - - return true; - } - - return false; -} -#endif - -} // namespace nanort - -#endif // NANORT_H_ diff --git a/examples/raytrace/nanosg.h b/examples/raytrace/nanosg.h deleted file mode 100644 index 9020fa8..0000000 --- a/examples/raytrace/nanosg.h +++ /dev/null @@ -1,900 +0,0 @@ -/* -The MIT License (MIT) - -Copyright (c) 2017 Light Transport Entertainment, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -*/ - -#ifndef NANOSG_H_ -#define NANOSG_H_ - -#ifdef _WIN32 -#ifndef NOMINMAX -#define NOMINMAX -#endif -#endif - -#include -#include -#include - -#include "nanort.h" - -namespace nanosg { - -template -class PrimitiveInterface; - -template -class PrimitiveInterface{ -public: - void print(){ static_cast(this)->print(); } -}; - -class SpherePrimitive : PrimitiveInterface { -public: - void print(){ std::cout << "Sphere" << std::endl; } -}; - -// 4x4 matrix -template class Matrix { -public: - Matrix(); - ~Matrix(); - - static void Print(T m[4][4]) { - for (int i = 0; i < 4; i++) { - printf("m[%d] = %f, %f, %f, %f\n", i, m[i][0], m[i][1], m[i][2], m[i][3]); - } - } - - static void Identity(T m[4][4]) { - m[0][0] = static_cast(1); - m[0][1] = static_cast(0); - m[0][2] = static_cast(0); - m[0][3] = static_cast(0); - m[1][0] = static_cast(0); - m[1][1] = static_cast(1); - m[1][2] = static_cast(0); - m[1][3] = static_cast(0); - m[2][0] = static_cast(0); - m[2][1] = static_cast(0); - m[2][2] = static_cast(1); - m[2][3] = static_cast(0); - m[3][0] = static_cast(0); - m[3][1] = static_cast(0); - m[3][2] = static_cast(0); - m[3][3] = static_cast(1); - } - - static void Copy(T dst[4][4], const T src[4][4]) { - memcpy(dst, src, sizeof(T) * 16); - } - - static void Inverse(T m[4][4]) { - /* - * codes from intel web - * cramer's rule version - */ - int i, j; - T tmp[12]; /* tmp array for pairs */ - T tsrc[16]; /* array of transpose source matrix */ - T det; /* determinant */ - - /* transpose matrix */ - for (i = 0; i < 4; i++) { - tsrc[i] = m[i][0]; - tsrc[i + 4] = m[i][1]; - tsrc[i + 8] = m[i][2]; - tsrc[i + 12] = m[i][3]; - } - - /* calculate pair for first 8 elements(cofactors) */ - tmp[0] = tsrc[10] * tsrc[15]; - tmp[1] = tsrc[11] * tsrc[14]; - tmp[2] = tsrc[9] * tsrc[15]; - tmp[3] = tsrc[11] * tsrc[13]; - tmp[4] = tsrc[9] * tsrc[14]; - tmp[5] = tsrc[10] * tsrc[13]; - tmp[6] = tsrc[8] * tsrc[15]; - tmp[7] = tsrc[11] * tsrc[12]; - tmp[8] = tsrc[8] * tsrc[14]; - tmp[9] = tsrc[10] * tsrc[12]; - tmp[10] = tsrc[8] * tsrc[13]; - tmp[11] = tsrc[9] * tsrc[12]; - - /* calculate first 8 elements(cofactors) */ - m[0][0] = tmp[0] * tsrc[5] + tmp[3] * tsrc[6] + tmp[4] * tsrc[7]; - m[0][0] -= tmp[1] * tsrc[5] + tmp[2] * tsrc[6] + tmp[5] * tsrc[7]; - m[0][1] = tmp[1] * tsrc[4] + tmp[6] * tsrc[6] + tmp[9] * tsrc[7]; - m[0][1] -= tmp[0] * tsrc[4] + tmp[7] * tsrc[6] + tmp[8] * tsrc[7]; - m[0][2] = tmp[2] * tsrc[4] + tmp[7] * tsrc[5] + tmp[10] * tsrc[7]; - m[0][2] -= tmp[3] * tsrc[4] + tmp[6] * tsrc[5] + tmp[11] * tsrc[7]; - m[0][3] = tmp[5] * tsrc[4] + tmp[8] * tsrc[5] + tmp[11] * tsrc[6]; - m[0][3] -= tmp[4] * tsrc[4] + tmp[9] * tsrc[5] + tmp[10] * tsrc[6]; - m[1][0] = tmp[1] * tsrc[1] + tmp[2] * tsrc[2] + tmp[5] * tsrc[3]; - m[1][0] -= tmp[0] * tsrc[1] + tmp[3] * tsrc[2] + tmp[4] * tsrc[3]; - m[1][1] = tmp[0] * tsrc[0] + tmp[7] * tsrc[2] + tmp[8] * tsrc[3]; - m[1][1] -= tmp[1] * tsrc[0] + tmp[6] * tsrc[2] + tmp[9] * tsrc[3]; - m[1][2] = tmp[3] * tsrc[0] + tmp[6] * tsrc[1] + tmp[11] * tsrc[3]; - m[1][2] -= tmp[2] * tsrc[0] + tmp[7] * tsrc[1] + tmp[10] * tsrc[3]; - m[1][3] = tmp[4] * tsrc[0] + tmp[9] * tsrc[1] + tmp[10] * tsrc[2]; - m[1][3] -= tmp[5] * tsrc[0] + tmp[8] * tsrc[1] + tmp[11] * tsrc[2]; - - /* calculate pairs for second 8 elements(cofactors) */ - tmp[0] = tsrc[2] * tsrc[7]; - tmp[1] = tsrc[3] * tsrc[6]; - tmp[2] = tsrc[1] * tsrc[7]; - tmp[3] = tsrc[3] * tsrc[5]; - tmp[4] = tsrc[1] * tsrc[6]; - tmp[5] = tsrc[2] * tsrc[5]; - tmp[6] = tsrc[0] * tsrc[7]; - tmp[7] = tsrc[3] * tsrc[4]; - tmp[8] = tsrc[0] * tsrc[6]; - tmp[9] = tsrc[2] * tsrc[4]; - tmp[10] = tsrc[0] * tsrc[5]; - tmp[11] = tsrc[1] * tsrc[4]; - - /* calculate second 8 elements(cofactors) */ - m[2][0] = tmp[0] * tsrc[13] + tmp[3] * tsrc[14] + tmp[4] * tsrc[15]; - m[2][0] -= tmp[1] * tsrc[13] + tmp[2] * tsrc[14] + tmp[5] * tsrc[15]; - m[2][1] = tmp[1] * tsrc[12] + tmp[6] * tsrc[14] + tmp[9] * tsrc[15]; - m[2][1] -= tmp[0] * tsrc[12] + tmp[7] * tsrc[14] + tmp[8] * tsrc[15]; - m[2][2] = tmp[2] * tsrc[12] + tmp[7] * tsrc[13] + tmp[10] * tsrc[15]; - m[2][2] -= tmp[3] * tsrc[12] + tmp[6] * tsrc[13] + tmp[11] * tsrc[15]; - m[2][3] = tmp[5] * tsrc[12] + tmp[8] * tsrc[13] + tmp[11] * tsrc[14]; - m[2][3] -= tmp[4] * tsrc[12] + tmp[9] * tsrc[13] + tmp[10] * tsrc[14]; - m[3][0] = tmp[2] * tsrc[10] + tmp[5] * tsrc[11] + tmp[1] * tsrc[9]; - m[3][0] -= tmp[4] * tsrc[11] + tmp[0] * tsrc[9] + tmp[3] * tsrc[10]; - m[3][1] = tmp[8] * tsrc[11] + tmp[0] * tsrc[8] + tmp[7] * tsrc[10]; - m[3][1] -= tmp[6] * tsrc[10] + tmp[9] * tsrc[11] + tmp[1] * tsrc[8]; - m[3][2] = tmp[6] * tsrc[9] + tmp[11] * tsrc[11] + tmp[3] * tsrc[8]; - m[3][2] -= tmp[10] * tsrc[11] + tmp[2] * tsrc[8] + tmp[7] * tsrc[9]; - m[3][3] = tmp[10] * tsrc[10] + tmp[4] * tsrc[8] + tmp[9] * tsrc[9]; - m[3][3] -= tmp[8] * tsrc[9] + tmp[11] * tsrc[0] + tmp[5] * tsrc[8]; - - /* calculate determinant */ - det = tsrc[0] * m[0][0] + tsrc[1] * m[0][1] + tsrc[2] * m[0][2] + - tsrc[3] * m[0][3]; - - /* calculate matrix inverse */ - det = static_cast(1.0) / det; - - for (j = 0; j < 4; j++) { - for (i = 0; i < 4; i++) { - m[j][i] *= det; - } - } - } - - static void Transpose(T m[4][4]) { - T t[4][4]; - - // Transpose - for (int j = 0; j < 4; j++) { - for (int i = 0; i < 4; i++) { - t[j][i] = m[i][j]; - } - } - - // Copy - for (int j = 0; j < 4; j++) { - for (int i = 0; i < 4; i++) { - m[j][i] = t[j][i]; - } - } - } - - static void Mult(T dst[4][4], const T m0[4][4], const T m1[4][4]) { - for (int i = 0; i < 4; ++i) { - for (int j = 0; j < 4; ++j) { - dst[i][j] = 0; - for (int k = 0; k < 4; ++k) { - dst[i][j] += m0[k][j] * m1[i][k]; - } - } - } - } - - static void MultV(T dst[3], const T m[4][4], const T v[3]) { - T tmp[3]; - tmp[0] = m[0][0] * v[0] + m[1][0] * v[1] + m[2][0] * v[2] + m[3][0]; - tmp[1] = m[0][1] * v[0] + m[1][1] * v[1] + m[2][1] * v[2] + m[3][1]; - tmp[2] = m[0][2] * v[0] + m[1][2] * v[1] + m[2][2] * v[2] + m[3][2]; - dst[0] = tmp[0]; - dst[1] = tmp[1]; - dst[2] = tmp[2]; - } - - static void MultV(nanort::real3 &dst, const T m[4][4], const T v[3]) { - T tmp[3]; - tmp[0] = m[0][0] * v[0] + m[1][0] * v[1] + m[2][0] * v[2] + m[3][0]; - tmp[1] = m[0][1] * v[0] + m[1][1] * v[1] + m[2][1] * v[2] + m[3][1]; - tmp[2] = m[0][2] * v[0] + m[1][2] * v[1] + m[2][2] * v[2] + m[3][2]; - dst[0] = tmp[0]; - dst[1] = tmp[1]; - dst[2] = tmp[2]; - } - -}; - -//typedef Matrix Matrixf; -//typedef Matrix Matrixd; - -template -static void XformBoundingBox(T xbmin[3], // out - T xbmax[3], // out - T bmin[3], T bmax[3], - T m[4][4]) { - - // create bounding vertex from (bmin, bmax) - T b[8][3]; - - b[0][0] = bmin[0]; - b[0][1] = bmin[1]; - b[0][2] = bmin[2]; - b[1][0] = bmax[0]; - b[1][1] = bmin[1]; - b[1][2] = bmin[2]; - b[2][0] = bmin[0]; - b[2][1] = bmax[1]; - b[2][2] = bmin[2]; - b[3][0] = bmax[0]; - b[3][1] = bmax[1]; - b[3][2] = bmin[2]; - - b[4][0] = bmin[0]; - b[4][1] = bmin[1]; - b[4][2] = bmax[2]; - b[5][0] = bmax[0]; - b[5][1] = bmin[1]; - b[5][2] = bmax[2]; - b[6][0] = bmin[0]; - b[6][1] = bmax[1]; - b[6][2] = bmax[2]; - b[7][0] = bmax[0]; - b[7][1] = bmax[1]; - b[7][2] = bmax[2]; - - T xb[8][3]; - for (int i = 0; i < 8; i++) { - Matrix::MultV(xb[i], m, b[i]); - } - - xbmin[0] = xb[0][0]; - xbmin[1] = xb[0][1]; - xbmin[2] = xb[0][2]; - xbmax[0] = xb[0][0]; - xbmax[1] = xb[0][1]; - xbmax[2] = xb[0][2]; - - for (int i = 1; i < 8; i++) { - - xbmin[0] = std::min(xb[i][0], xbmin[0]); - xbmin[1] = std::min(xb[i][1], xbmin[1]); - xbmin[2] = std::min(xb[i][2], xbmin[2]); - - xbmax[0] = std::max(xb[i][0], xbmax[0]); - xbmax[1] = std::max(xb[i][1], xbmax[1]); - xbmax[2] = std::max(xb[i][2], xbmax[2]); - } -} - -template -struct Intersection -{ - // required fields. - T t; // hit distance - unsigned int prim_id; // primitive ID of the hit - float u; - float v; - - unsigned int node_id; // node ID of the hit. - nanort::real3 P; // intersection point - nanort::real3 Ns; // shading normal - nanort::real3 Ng; // geometric normal -}; - -/// -/// Renderable node -/// -template -class Node -{ - public: - typedef Node type; - - explicit Node(const M *mesh) - : mesh_(mesh) - { - xbmin_[0] = xbmin_[1] = xbmin_[2] = std::numeric_limits::max(); - xbmax_[0] = xbmax_[1] = xbmax_[2] = -std::numeric_limits::max(); - - lbmin_[0] = lbmin_[1] = lbmin_[2] = std::numeric_limits::max(); - lbmax_[0] = lbmax_[1] = lbmax_[2] = -std::numeric_limits::max(); - - Matrix::Identity(local_xform_); - Matrix::Identity(xform_); - Matrix::Identity(inv_xform_); - Matrix::Identity(inv_xform33_); inv_xform33_[3][3] = static_cast(0.0); - Matrix::Identity(inv_transpose_xform33_); inv_transpose_xform33_[3][3] = static_cast(0.0); - - } - - ~Node() {} - - void Copy(const type &rhs) { - Matrix::Copy(local_xform_, rhs.local_xform_); - Matrix::Copy(xform_, rhs.xform_); - Matrix::Copy(inv_xform_, rhs.inv_xform_); - Matrix::Copy(inv_xform33_, rhs.inv_xform33_); - Matrix::Copy(inv_transpose_xform33_, rhs.inv_transpose_xform33_); - - lbmin_[0] = rhs.lbmin_[0]; - lbmin_[1] = rhs.lbmin_[1]; - lbmin_[2] = rhs.lbmin_[2]; - - lbmax_[0] = rhs.lbmax_[0]; - lbmax_[1] = rhs.lbmax_[1]; - lbmax_[2] = rhs.lbmax_[2]; - - xbmin_[0] = rhs.xbmin_[0]; - xbmin_[1] = rhs.xbmin_[1]; - xbmin_[2] = rhs.xbmin_[2]; - - xbmax_[0] = rhs.xbmax_[0]; - xbmax_[1] = rhs.xbmax_[1]; - xbmax_[2] = rhs.xbmax_[2]; - - mesh_ = rhs.mesh_; - name_ = rhs.name_; - - children_ = rhs.children_; - } - - Node(const type &rhs) { - Copy(rhs); - } - - const type &operator=(const type &rhs) { - Copy(rhs); - return (*this); - } - - void SetName(const std::string &name) { - name_ = name; - } - - const std::string &GetName() const { - return name_; - } - - /// - /// Add child node. - /// - void AddChild(const type &child) { - children_.push_back(child); - } - - /// - /// Get chidren - /// - const std::vector &GetChildren() const { - return children_; - } - - std::vector &GetChildren() { - return children_; - } - - /// - /// Update internal state. - /// - void Update(const T parent_xform[4][4]) { - - if (!accel_.IsValid() && mesh_ && (mesh_->vertices.size() > 3) && (mesh_->faces.size() >= 3)) { - - // Assume mesh is composed of triangle faces only. - nanort::TriangleMesh triangle_mesh(mesh_->vertices.data(), mesh_->faces.data(), sizeof(float) * 3); - nanort::TriangleSAHPred triangle_pred(mesh_->vertices.data(), mesh_->faces.data(), sizeof(float) * 3); - - bool ret = accel_.Build(int(mesh_->faces.size()) / 3, triangle_mesh, triangle_pred); - - // Update local bbox. - if (ret) { - accel_.BoundingBox(lbmin_, lbmax_); - } - } - - // xform = parent_xform x local_xform - Matrix::Mult(xform_, parent_xform, local_xform_); - - // Compute the bounding box in world coordinate. - XformBoundingBox(xbmin_, xbmax_, lbmin_, lbmax_, xform_); - - // Inverse(xform) - Matrix::Copy(inv_xform_, xform_); - Matrix::Inverse(inv_xform_); - - // Clear translation, then inverse(xform) - Matrix::Copy(inv_xform33_, xform_); - inv_xform33_[3][0] = static_cast(0.0); - inv_xform33_[3][1] = static_cast(0.0); - inv_xform33_[3][2] = static_cast(0.0); - Matrix::Inverse(inv_xform33_); - - // Inverse transpose of xform33 - Matrix::Copy(inv_transpose_xform33_, inv_xform33_); - Matrix::Transpose(inv_transpose_xform33_); - - // Update children nodes - for (size_t i = 0; i < children_.size(); i++) { - children_[i].Update(xform_); - } - } - - /// - /// Set local transformation. - /// - void SetLocalXform(const T xform[4][4]) { - memcpy(local_xform_, xform, sizeof(float) * 16); - } - - const T *GetLocalXformPtr() const { - return &local_xform_[0][0]; - } - - const T *GetXformPtr() const { - return &xform_[0][0]; - } - - const M *GetMesh() const { - return mesh_; - } - - const nanort::BVHAccel &GetAccel() const { - return accel_; - } - - inline void GetWorldBoundingBox(T bmin[3], T bmax[3]) const { - bmin[0] = xbmin_[0]; - bmin[1] = xbmin_[1]; - bmin[2] = xbmin_[2]; - - bmax[0] = xbmax_[0]; - bmax[1] = xbmax_[1]; - bmax[2] = xbmax_[2]; - } - - inline void GetLocalBoundingBox(T bmin[3], T bmax[3]) const { - bmin[0] = lbmin_[0]; - bmin[1] = lbmin_[1]; - bmin[2] = lbmin_[2]; - - bmax[0] = lbmax_[0]; - bmax[1] = lbmax_[1]; - bmax[2] = lbmax_[2]; - } - - T local_xform_[4][4]; // Node's local transformation matrix. - T xform_[4][4]; // Parent xform x local_xform. - T inv_xform_[4][4]; // inverse(xform). world -> local - T inv_xform33_[4][4]; // inverse(xform0 with upper-left 3x3 elemets only(for transforming direction vector) - T inv_transpose_xform33_[4][4]; // inverse(transpose(xform)) with upper-left 3x3 elements only(for transforming normal vector) - - private: - - // bounding box(local space) - T lbmin_[3]; - T lbmax_[3]; - - // bounding box after xform(world space) - T xbmin_[3]; - T xbmax_[3]; - - nanort::BVHAccel accel_; - - std::string name_; - - const M *mesh_; - - std::vector children_; - -}; - -// ------------------------------------------------- - -// Predefined SAH predicator for cube. -template -class NodeBBoxPred { - public: - NodeBBoxPred(const std::vector >* nodes) : axis_(0), pos_(0.0f), nodes_(nodes) {} - - void Set(int axis, float pos) const { - axis_ = axis; - pos_ = pos; - } - - bool operator()(unsigned int i) const { - int axis = axis_; - float pos = pos_; - - T bmin[3], bmax[3]; - - (*nodes_)[i].GetWorldBoundingBox(bmin, bmax); - - T center = bmax[axis] - bmin[axis]; - - return (center < pos); - } - - private: - mutable int axis_; - mutable float pos_; - const std::vector > *nodes_; -}; - -template -class NodeBBoxGeometry { - public: - NodeBBoxGeometry(const std::vector >* nodes) - : nodes_(nodes) {} - - /// Compute bounding box for `prim_index`th cube. - /// This function is called for each primitive in BVH build. - void BoundingBox(nanort::real3* bmin, nanort::real3* bmax, unsigned int prim_index) const { - T a[3], b[3]; - (*nodes_)[prim_index].GetWorldBoundingBox(a, b); - (*bmin)[0] = a[0]; - (*bmin)[1] = a[1]; - (*bmin)[2] = a[2]; - (*bmax)[0] = b[0]; - (*bmax)[1] = b[1]; - (*bmax)[2] = b[2]; - } - - const std::vector >* nodes_; - mutable nanort::real3 ray_org_; - mutable nanort::real3 ray_dir_; - mutable nanort::BVHTraceOptions trace_options_; -}; - -class NodeBBoxIntersection { - public: - NodeBBoxIntersection() {} - - float normal[3]; - - // Required member variables. - float t; - unsigned int prim_id; -}; - -template -class NodeBBoxIntersector { - public: - NodeBBoxIntersector(const std::vector >* nodes) - : nodes_(nodes) {} - - bool Intersect(float* out_t_min, float *out_t_max, unsigned int prim_index) const { - - T bmin[3], bmax[3]; - - (*nodes_)[prim_index].GetWorldBoundingBox(bmin, bmax); - - float tmin, tmax; - - const float min_x = ray_dir_sign_[0] ? bmax[0] : bmin[0]; - const float min_y = ray_dir_sign_[1] ? bmax[1] : bmin[1]; - const float min_z = ray_dir_sign_[2] ? bmax[2] : bmin[2]; - const float max_x = ray_dir_sign_[0] ? bmin[0] : bmax[0]; - const float max_y = ray_dir_sign_[1] ? bmin[1] : bmax[1]; - const float max_z = ray_dir_sign_[2] ? bmin[2] : bmax[2]; - - // X - const float tmin_x = (min_x - ray_org_[0]) * ray_inv_dir_[0]; - const float tmax_x = (max_x - ray_org_[0]) * ray_inv_dir_[0]; - - // Y - const float tmin_y = (min_y - ray_org_[1]) * ray_inv_dir_[1]; - const float tmax_y = (max_y - ray_org_[1]) * ray_inv_dir_[1]; - - // Z - const float tmin_z = (min_z - ray_org_[2]) * ray_inv_dir_[2]; - const float tmax_z = (max_z - ray_org_[2]) * ray_inv_dir_[2]; - - tmin = nanort::safemax(tmin_z, nanort::safemax(tmin_y, tmin_x)); - tmax = nanort::safemin(tmax_z, nanort::safemin(tmax_y, tmax_x)); - - if (tmin <= tmax) { - (*out_t_min) = tmin; - (*out_t_max) = tmax; - return true; - } - - return false; - } - - /// Prepare BVH traversal(e.g. compute inverse ray direction) - /// This function is called only once in BVH traversal. - void PrepareTraversal(const nanort::Ray& ray) const { - ray_org_[0] = ray.org[0]; - ray_org_[1] = ray.org[1]; - ray_org_[2] = ray.org[2]; - - ray_dir_[0] = ray.dir[0]; - ray_dir_[1] = ray.dir[1]; - ray_dir_[2] = ray.dir[2]; - - // FIXME(syoyo): Consider zero div case. - ray_inv_dir_[0] = static_cast(1.0) / ray.dir[0]; - ray_inv_dir_[1] = static_cast(1.0) / ray.dir[1]; - ray_inv_dir_[2] = static_cast(1.0) / ray.dir[2]; - - ray_dir_sign_[0] = ray.dir[0] < static_cast(0.0) ? static_cast(1) : static_cast(0); - ray_dir_sign_[1] = ray.dir[1] < static_cast(0.0) ? static_cast(1) : static_cast(0); - ray_dir_sign_[2] = ray.dir[2] < static_cast(0.0) ? static_cast(1) : static_cast(0); - } - - const std::vector >* nodes_; - mutable nanort::real3 ray_org_; - mutable nanort::real3 ray_dir_; - mutable nanort::real3 ray_inv_dir_; - mutable int ray_dir_sign_[3]; -}; - -template -class Scene -{ - public: - Scene() { - bmin_[0] = bmin_[1] = bmin_[2] = std::numeric_limits::max(); - bmax_[0] = bmax_[1] = bmax_[2] = -std::numeric_limits::max(); - } - - ~Scene() {}; - - /// - /// Add intersectable node to the scene. - /// - bool AddNode(const Node &node) { - nodes_.push_back(node); - return true; - } - - const std::vector > &GetNodes() const { - return nodes_; - } - - bool FindNode(const std::string &name, Node **found_node) { - if (!found_node) { - return false; - } - - if (name.empty()) { - return false; - } - - // Simple exhaustive search. - for (size_t i = 0; i < nodes_.size(); i++) { - if (FindNodeRecursive(name, &(nodes_[i]), found_node)) { - return true; - } - } - - return false; - } - - - /// - /// Commit the scene. Must be called before tracing rays into the scene. - /// - bool Commit() { - - // Update nodes. - for (size_t i = 0; i < nodes_.size(); i++) { - T ident[4][4]; - Matrix::Identity(ident); - - nodes_[i].Update(ident); - } - - // Build toplevel BVH. - NodeBBoxGeometry geom(&nodes_); - NodeBBoxPred pred(&nodes_); - - // FIXME(LTE): Limit one leaf contains one node bbox primitive. This would work, but would be inefficient. - // e.g. will miss some node when constructed BVH depth is larger than the value of BVHBuildOptions. - // Implement more better and efficient BVH build and traverse for Toplevel BVH. - nanort::BVHBuildOptions build_options; - build_options.min_leaf_primitives = 1; - - bool ret = toplevel_accel_.Build(static_cast(nodes_.size()), geom, pred, build_options); - - nanort::BVHBuildStatistics stats = toplevel_accel_.GetStatistics(); - - //toplevel_accel_.Debug(); - - if (ret) { - toplevel_accel_.BoundingBox(bmin_, bmax_); - } else { - // Set invalid bbox value. - bmin_[0] = std::numeric_limits::max(); - bmin_[1] = std::numeric_limits::max(); - bmin_[2] = std::numeric_limits::max(); - - bmax_[0] = -std::numeric_limits::max(); - bmax_[1] = -std::numeric_limits::max(); - bmax_[2] = -std::numeric_limits::max(); - } - - return ret; - } - - /// - /// Get the scene bounding box. - /// - void GetBoundingBox(T bmin[3], T bmax[3]) const { - bmin[0] = bmin_[0]; - bmin[1] = bmin_[1]; - bmin[2] = bmin_[2]; - - bmax[0] = bmax_[0]; - bmax[1] = bmax_[1]; - bmax[2] = bmax_[2]; - } - - /// - /// Trace the ray into the scene. - /// First find the intersection of nodes' bounding box using toplevel BVH. - /// Then, trace into the hit node to find the intersection of the primitive. - /// - template - bool Traverse(nanort::Ray &ray, H *isect, const bool cull_back_face = false) const { - - if (!toplevel_accel_.IsValid()) { - return false; - } - - const int kMaxIntersections = 64; - - bool has_hit = false; - - NodeBBoxIntersector isector(&nodes_); - nanort::StackVector, 128> node_hits; - bool may_hit = toplevel_accel_.ListNodeIntersections(ray, kMaxIntersections, isector, &node_hits); - - if (may_hit) { - - T t_max = std::numeric_limits::max(); - T t_nearest = t_max; - - nanort::BVHTraceOptions trace_options; - trace_options.cull_back_face = cull_back_face; - - // Find actual intersection point. - for (size_t i = 0; i < node_hits->size(); i++) { - - // Early cull test. - if (t_nearest < node_hits[i].t_min) { - //printf("near: %f, t_min: %f, t_max: %f\n", t_nearest, node_hits[i].t_min, node_hits[i].t_max); - continue; - } - - assert(node_hits[i].node_id < nodes_.size()); - const Node &node = nodes_[node_hits[i].node_id]; - - // Transform ray into node's local space - // TODO(LTE): Set ray tmin and tmax - nanort::Ray local_ray; - Matrix::MultV(local_ray.org, node.inv_xform_, ray.org); - Matrix::MultV(local_ray.dir, node.inv_xform33_, ray.dir); - - nanort::TriangleIntersector triangle_intersector(node.GetMesh()->vertices.data(), node.GetMesh()->faces.data(), sizeof(T) * 3); - H local_isect; - - bool hit = node.GetAccel().Traverse(local_ray, triangle_intersector, &local_isect); - - if (hit) { - // Calulcate hit distance in world coordiante. - T local_P[3]; - local_P[0] = local_ray.org[0] + local_isect.t * local_ray.dir[0]; - local_P[1] = local_ray.org[1] + local_isect.t * local_ray.dir[1]; - local_P[2] = local_ray.org[2] + local_isect.t * local_ray.dir[2]; - - T world_P[3]; - Matrix::MultV(world_P, node.xform_, local_P); - - nanort::real3 po; - po[0] = world_P[0] - ray.org[0]; - po[1] = world_P[1] - ray.org[1]; - po[2] = world_P[2] - ray.org[2]; - - float t_world = vlength(po); - - if (t_world < t_nearest) { - t_nearest = t_world; - has_hit = true; - //(*isect) = local_isect; - isect->node_id = node_hits[i].node_id; - isect->prim_id = local_isect.prim_id; - isect->u = local_isect.u; - isect->v = local_isect.v; - - // TODO(LTE): Implement - T Ng[3], Ns[3]; // geometric normal, shading normal. - - node.GetMesh()->GetNormal(Ng, Ns, isect->prim_id, isect->u, isect->v); - - // Convert position and normal into world coordinate. - isect->t = t_world; - Matrix::MultV(isect->P, node.xform_, local_P); - Matrix::MultV(isect->Ng, node.inv_transpose_xform33_, - Ng); - Matrix::MultV(isect->Ns, node.inv_transpose_xform33_, - Ns); - } - - } - - } - - } - - return has_hit; - - } - - private: - - /// - /// Find a node by name. - /// - bool FindNodeRecursive(const std::string &name, Node *root, Node **found_node) { - - if (root->GetName().compare(name) == 0) { - (*found_node) = root; - return true; - } - - // Simple exhaustive search. - for (size_t i = 0; i < root->GetChildren().size(); i++) { - if (FindNodeRecursive(name, &(root->GetChildren()[i]), found_node)) { - return true; - } - } - - return false; - - } - - // Scene bounding box. - // Valid after calling `Commit()`. - T bmin_[3]; - T bmax_[3]; - - // Toplevel BVH accel. - nanort::BVHAccel toplevel_accel_; - std::vector > nodes_; -}; - -} // namespace nanosg - -#endif // NANOSG_H_ diff --git a/examples/raytrace/premake5.lua b/examples/raytrace/premake5.lua deleted file mode 100644 index cd802cc..0000000 --- a/examples/raytrace/premake5.lua +++ /dev/null @@ -1,109 +0,0 @@ -newoption { - trigger = "asan", - description = "Enable Address Sanitizer(gcc5+ ang clang only)" -} - -sources = { - "main.cc", - "render.cc", - "render-config.cc", - "gltf-loader.cc", - "matrix.cc", - "../common/trackball.cc", - "../common/imgui/imgui.cpp", - "../common/imgui/imgui_draw.cpp", - "../common/imgui/imgui_impl_btgui.cpp", - "../common/imgui/ImGuizmo.cpp", - } - -solution "RaytraceSolution" - configurations { "Release", "Debug" } - - if os.is("Windows") then - platforms { "x64", "x32" } - else - platforms { "native", "x64", "x32" } - end - - - -- RootDir for OpenGLWindow - projectRootDir = os.getcwd() .. "/../common/" - dofile ("../common/findOpenGLGlewGlut.lua") - initOpenGL() - initGlew() - - -- Use c++11 - flags { "c++11" } - - -- A project defines one build target - project "viwewer" - kind "ConsoleApp" - language "C++" - files { sources } - - includedirs { "./", "../../" } - includedirs { "../common" } - includedirs { "../common/imgui" } - includedirs { "../common/glm" } - --includedirs { "../common/nativefiledialog/src/include" } - - if _OPTIONS['asan'] then - buildoptions { "-fsanitize=address" } - linkoptions { "-fsanitize=address" } - end - - if os.is("Windows") then - flags { "FatalCompileWarnings" } - warnings "Extra" -- /W4 - - defines { "NOMINMAX" } - defines { "USE_NATIVEFILEDIALOG" } - buildoptions { "/W4" } -- raise compile error level. - files{ - "../common/OpenGLWindow/Win32OpenGLWindow.cpp", - "../common/OpenGLWindow/Win32OpenGLWindow.h", - "../common/OpenGLWindow/Win32Window.cpp", - "../common/OpenGLWindow/Win32Window.h", - } - includedirs { "./../common/nativefiledialog/src/include" } - files { "../common/nativefiledialog/src/nfd_common.c", - "../common/nativefiledialog/src/nfd_win.cpp" } - end - if os.is("Linux") then - files { - "../common/OpenGLWindow/X11OpenGLWindow.cpp", - "../common/OpenGLWindow/X11OpenGLWindows.h" - } - links {"X11", "pthread", "dl"} - if _OPTIONS["with-gtk3nfd"] then - defines { "USE_NATIVEFILEDIALOG" } - includedirs { "./../common/nativefiledialog/src/include" } - files { "../common/nativefiledialog/src/nfd_gtk.c", - "../common/nativefiledialog/src/nfd_common.c" - } - buildoptions { "`pkg-config --cflags gtk+-3.0`" } - linkoptions { "`pkg-config --libs gtk+-3.0`" } - end - end - if os.is("MacOSX") then - defines { "USE_NATIVEFILEDIALOG" } - links {"Cocoa.framework"} - files { - "../common/OpenGLWindow/MacOpenGLWindow.h", - "../common/OpenGLWindow/MacOpenGLWindow.mm", - } - includedirs { "./../common/nativefiledialog/src/include" } - files { "../common/nativefiledialog/src/nfd_cocoa.m", - "../common/nativefiledialog/src/nfd_common.c" } - end - - configuration "Debug" - defines { "DEBUG" } -- -DDEBUG - symbols "On" - targetname "view_debug" - - configuration "Release" - -- defines { "NDEBUG" } -- -NDEBUG - symbols "On" - optimize "On" - targetname "view" diff --git a/examples/raytrace/render-config.cc b/examples/raytrace/render-config.cc deleted file mode 100644 index 1322edd..0000000 --- a/examples/raytrace/render-config.cc +++ /dev/null @@ -1,110 +0,0 @@ -#include "render-config.h" - -#include "picojson.h" - -#include -#include - -namespace example { - -bool LoadRenderConfig(example::RenderConfig* config, const char* filename) { - std::ifstream is(filename); - if (is.fail()) { - std::cerr << "Cannot open " << filename << std::endl; - return false; - } - - std::istream_iterator input(is); - std::string err; - picojson::value v; - input = picojson::parse(v, input, std::istream_iterator(), &err); - if (!err.empty()) { - std::cerr << err << std::endl; - } - - if (!v.is()) { - std::cerr << "Not a JSON object" << std::endl; - return false; - } - - picojson::object o = v.get(); - - if (o.find("gltf_filename") != o.end()) { - if (o["gltf_filename"].is()) { - config->gltf_filename = o["gltf_filename"].get(); - } - } - - config->scene_scale = 1.0f; - if (o.find("scene_scale") != o.end()) { - if (o["scene_scale"].is()) { - config->scene_scale = static_cast(o["scene_scale"].get()); - } - } - - config->eye[0] = 0.0f; - config->eye[1] = 0.0f; - config->eye[2] = 5.0f; - if (o.find("eye") != o.end()) { - if (o["eye"].is()) { - picojson::array arr = o["eye"].get(); - if (arr.size() == 3) { - config->eye[0] = static_cast(arr[0].get()); - config->eye[1] = static_cast(arr[1].get()); - config->eye[2] = static_cast(arr[2].get()); - } - } - } - - config->up[0] = 0.0f; - config->up[1] = 1.0f; - config->up[2] = 0.0f; - if (o.find("up") != o.end()) { - if (o["up"].is()) { - picojson::array arr = o["up"].get(); - if (arr.size() == 3) { - config->up[0] = static_cast(arr[0].get()); - config->up[1] = static_cast(arr[1].get()); - config->up[2] = static_cast(arr[2].get()); - } - } - } - - config->look_at[0] = 0.0f; - config->look_at[1] = 0.0f; - config->look_at[2] = 0.0f; - if (o.find("look_at") != o.end()) { - if (o["look_at"].is()) { - picojson::array arr = o["look_at"].get(); - if (arr.size() == 3) { - config->look_at[0] = static_cast(arr[0].get()); - config->look_at[1] = static_cast(arr[1].get()); - config->look_at[2] = static_cast(arr[2].get()); - } - } - } - - config->fov = 45.0f; - if (o.find("fov") != o.end()) { - if (o["fov"].is()) { - config->fov = static_cast(o["fov"].get()); - } - } - - config->width = 512; - if (o.find("width") != o.end()) { - if (o["width"].is()) { - config->width = static_cast(o["width"].get()); - } - } - - config->height = 512; - if (o.find("height") != o.end()) { - if (o["height"].is()) { - config->height = static_cast(o["height"].get()); - } - } - - return true; -} -} diff --git a/examples/raytrace/render-config.h b/examples/raytrace/render-config.h deleted file mode 100644 index 7eb7537..0000000 --- a/examples/raytrace/render-config.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef RENDER_CONFIG_H -#define RENDER_CONFIG_H - -#include - -namespace example { - -typedef struct { - // framebuffer - int width; - int height; - - // camera - float eye[3]; - float up[3]; - float look_at[3]; - float fov; // vertical fov in degree. - - // render pass - int pass; - int max_passes; - - // For debugging. Array size = width * height * 4. - float *normalImage; - float *positionImage; - float *depthImage; - float *texcoordImage; - float *varycoordImage; - - // Scene input info - std::string gltf_filename; - float scene_scale; - -} RenderConfig; - -/// Loads config from JSON file. -bool LoadRenderConfig(example::RenderConfig *config, const char *filename); - -} // namespace - -#endif // RENDER_CONFIG_H diff --git a/examples/raytrace/render.cc b/examples/raytrace/render.cc deleted file mode 100644 index f8de8ee..0000000 --- a/examples/raytrace/render.cc +++ /dev/null @@ -1,512 +0,0 @@ -/* -The MIT License (MIT) - -Copyright (c) 2015 - 2016 Light Transport Entertainment, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -*/ - - -#ifdef _MSC_VER -#pragma warning(disable : 4018) -#pragma warning(disable : 4244) -#pragma warning(disable : 4189) -#pragma warning(disable : 4996) -#pragma warning(disable : 4267) -#pragma warning(disable : 4477) -#endif - -#include "render.h" - -#include // C++11 -#include -#include // C++11 -#include - -#include - -#include "nanort.h" -#include "matrix.h" -#include "material.h" -#include "mesh.h" - - -#include "trackball.h" - - -#ifdef WIN32 -#undef min -#undef max -#endif - -namespace example { - -// PCG32 code / (c) 2014 M.E. O'Neill / pcg-random.org -// Licensed under Apache License 2.0 (NO WARRANTY, etc. see website) -// http://www.pcg-random.org/ -typedef struct { - unsigned long long state; - unsigned long long inc; // not used? -} pcg32_state_t; - -#define PCG32_INITIALIZER \ - { 0x853c49e6748fea9bULL, 0xda3e39cb94b95bdbULL } - -float pcg32_random(pcg32_state_t* rng) { - unsigned long long oldstate = rng->state; - rng->state = oldstate * 6364136223846793005ULL + rng->inc; - unsigned int xorshifted = ((oldstate >> 18u) ^ oldstate) >> 27u; - unsigned int rot = oldstate >> 59u; - unsigned int ret = (xorshifted >> rot) | (xorshifted << ((-static_cast(rot)) & 31)); - - return (float)((double)ret / (double)4294967296.0); -} - -void pcg32_srandom(pcg32_state_t* rng, uint64_t initstate, uint64_t initseq) { - rng->state = 0U; - rng->inc = (initseq << 1U) | 1U; - pcg32_random(rng); - rng->state += initstate; - pcg32_random(rng); -} - -const float kPI = 3.141592f; - -typedef nanort::real3 float3; - -inline float3 Lerp3(float3 v0, float3 v1, float3 v2, float u, float v) { - return (1.0f - u - v) * v0 + u * v1 + v * v2; -} - -inline void CalcNormal(float3& N, float3 v0, float3 v1, float3 v2) { - float3 v10 = v1 - v0; - float3 v20 = v2 - v0; - - N = vcross(v20, v10); - N = vnormalize(N); -} - -void BuildCameraFrame(float3* origin, float3* corner, float3* u, float3* v, - float quat[4], float eye[3], float lookat[3], float up[3], - float fov, int width, int height) { - float e[4][4]; - - Matrix::LookAt(e, eye, lookat, up); - - float r[4][4]; - build_rotmatrix(r, quat); - - float3 lo; - lo[0] = lookat[0] - eye[0]; - lo[1] = lookat[1] - eye[1]; - lo[2] = lookat[2] - eye[2]; - float dist = vlength(lo); - - float dir[3]; - dir[0] = 0.0; - dir[1] = 0.0; - dir[2] = dist; - - Matrix::Inverse(r); - - float rr[4][4]; - float re[4][4]; - float zero[3] = {0.0f, 0.0f, 0.0f}; - float localUp[3] = {0.0f, 1.0f, 0.0f}; - Matrix::LookAt(re, dir, zero, localUp); - - // translate - re[3][0] += eye[0]; // 0.0; //lo[0]; - re[3][1] += eye[1]; // 0.0; //lo[1]; - re[3][2] += (eye[2] - dist); - - // rot -> trans - Matrix::Mult(rr, r, re); - - float m[4][4]; - for (int j = 0; j < 4; j++) { - for (int i = 0; i < 4; i++) { - m[j][i] = rr[j][i]; - } - } - - float vzero[3] = {0.0f, 0.0f, 0.0f}; - float eye1[3]; - Matrix::MultV(eye1, m, vzero); - - float lookat1d[3]; - dir[2] = -dir[2]; - Matrix::MultV(lookat1d, m, dir); - float3 lookat1(lookat1d[0], lookat1d[1], lookat1d[2]); - - float up1d[3]; - Matrix::MultV(up1d, m, up); - - float3 up1(up1d[0], up1d[1], up1d[2]); - - // absolute -> relative - up1[0] -= eye1[0]; - up1[1] -= eye1[1]; - up1[2] -= eye1[2]; - // printf("up1(after) = %f, %f, %f\n", up1[0], up1[1], up1[2]); - - // Use original up vector - // up1[0] = up[0]; - // up1[1] = up[1]; - // up1[2] = up[2]; - - { - float flen = - (0.5f * (float)height / tanf(0.5f * (float)(fov * kPI / 180.0f))); - float3 look1; - look1[0] = lookat1[0] - eye1[0]; - look1[1] = lookat1[1] - eye1[1]; - look1[2] = lookat1[2] - eye1[2]; - // vcross(u, up1, look1); - // flip - (*u) = nanort::vcross(look1, up1); - (*u) = vnormalize((*u)); - - (*v) = vcross(look1, (*u)); - (*v) = vnormalize((*v)); - - look1 = vnormalize(look1); - look1[0] = flen * look1[0] + eye1[0]; - look1[1] = flen * look1[1] + eye1[1]; - look1[2] = flen * look1[2] + eye1[2]; - (*corner)[0] = look1[0] - 0.5f * (width * (*u)[0] + height * (*v)[0]); - (*corner)[1] = look1[1] - 0.5f * (width * (*u)[1] + height * (*v)[1]); - (*corner)[2] = look1[2] - 0.5f * (width * (*u)[2] + height * (*v)[2]); - - (*origin)[0] = eye1[0]; - (*origin)[1] = eye1[1]; - (*origin)[2] = eye1[2]; - } -} - -nanort::Ray GenerateRay(const float3& origin, const float3& corner, - const float3& du, const float3& dv, float u, - float v) { - float3 dir; - - dir[0] = (corner[0] + u * du[0] + v * dv[0]) - origin[0]; - dir[1] = (corner[1] + u * du[1] + v * dv[1]) - origin[1]; - dir[2] = (corner[2] + u * du[2] + v * dv[2]) - origin[2]; - dir = vnormalize(dir); - - float3 org; - - nanort::Ray ray; - ray.org[0] = origin[0]; - ray.org[1] = origin[1]; - ray.org[2] = origin[2]; - ray.dir[0] = dir[0]; - - return ray; -} - -void FetchTexture(const Texture &texture, float u, float v, float* col) { - int tx = u * texture.width; - int ty = (1.0f - v) * texture.height; - int idx_offset = (ty * texture.width + tx) * texture.components; - col[0] = texture.image[idx_offset + 0] / 255.f; - col[1] = texture.image[idx_offset + 1] / 255.f; - col[2] = texture.image[idx_offset + 2] / 255.f; -} - -bool Renderer::Render(float* rgba, float* aux_rgba, int* sample_counts, - float quat[4], - const nanosg::Scene> &scene, - const example::Asset &asset, - const RenderConfig& config, - std::atomic& cancelFlag) { - //if (!gAccel.IsValid()) { - // return false; - //} - - int width = config.width; - int height = config.height; - - // camera - float eye[3] = {config.eye[0], config.eye[1], config.eye[2]}; - float look_at[3] = {config.look_at[0], config.look_at[1], config.look_at[2]}; - float up[3] = {config.up[0], config.up[1], config.up[2]}; - float fov = config.fov; - float3 origin, corner, u, v; - BuildCameraFrame(&origin, &corner, &u, &v, quat, eye, look_at, up, fov, width, - height); - - auto kCancelFlagCheckMilliSeconds = 300; - - std::vector workers; - std::atomic i(0); - - uint32_t num_threads = std::max(1U, std::thread::hardware_concurrency()); - - auto startT = std::chrono::system_clock::now(); - - // Initialize RNG. - - for (auto t = 0; t < num_threads; t++) { - workers.emplace_back(std::thread([&, t]() { - pcg32_state_t rng; - pcg32_srandom(&rng, config.pass, - t); // seed = combination of render pass + thread no. - - int y = 0; - while ((y = i++) < config.height) { - auto currT = std::chrono::system_clock::now(); - - std::chrono::duration ms = currT - startT; - // Check cancel flag - if (ms.count() > kCancelFlagCheckMilliSeconds) { - if (cancelFlag) { - break; - } - } - - // draw dash line to aux buffer for progress. - // for (int x = 0; x < config.width; x++) { - // float c = (x / 8) % 2; - // aux_rgba[4*(y*config.width+x)+0] = c; - // aux_rgba[4*(y*config.width+x)+1] = c; - // aux_rgba[4*(y*config.width+x)+2] = c; - // aux_rgba[4*(y*config.width+x)+3] = 0.0f; - //} - - for (int x = 0; x < config.width; x++) { - nanort::Ray ray; - ray.org[0] = origin[0]; - ray.org[1] = origin[1]; - ray.org[2] = origin[2]; - - float u0 = pcg32_random(&rng); - float u1 = pcg32_random(&rng); - - float3 dir; - dir = corner + (float(x) + u0) * u + - (float(config.height - y - 1) + u1) * v; - dir = vnormalize(dir); - ray.dir[0] = dir[0]; - ray.dir[1] = dir[1]; - ray.dir[2] = dir[2]; - - float kFar = 1.0e+30f; - ray.min_t = 0.0f; - ray.max_t = kFar; - - - nanosg::Intersection isect; - bool hit = scene.Traverse(ray, &isect, /* cull_back_face */false); - - if (hit) { - - const std::vector &materials = asset.materials; - const std::vector &textures = asset.textures; - const Mesh &mesh = asset.meshes[isect.node_id]; - - float3 p; - p[0] = - ray.org[0] + isect.t * ray.dir[0]; - p[1] = - ray.org[1] + isect.t * ray.dir[1]; - p[2] = - ray.org[2] + isect.t * ray.dir[2]; - - config.positionImage[4 * (y * config.width + x) + 0] = p.x(); - config.positionImage[4 * (y * config.width + x) + 1] = p.y(); - config.positionImage[4 * (y * config.width + x) + 2] = p.z(); - config.positionImage[4 * (y * config.width + x) + 3] = 1.0f; - - config.varycoordImage[4 * (y * config.width + x) + 0] = - isect.u; - config.varycoordImage[4 * (y * config.width + x) + 1] = - isect.v; - config.varycoordImage[4 * (y * config.width + x) + 2] = 0.0f; - config.varycoordImage[4 * (y * config.width + x) + 3] = 1.0f; - - unsigned int prim_id = isect.prim_id; - - float3 N; - if (mesh.facevarying_normals.size() > 0) { - float3 n0, n1, n2; - n0[0] = mesh.facevarying_normals[9 * prim_id + 0]; - n0[1] = mesh.facevarying_normals[9 * prim_id + 1]; - n0[2] = mesh.facevarying_normals[9 * prim_id + 2]; - n1[0] = mesh.facevarying_normals[9 * prim_id + 3]; - n1[1] = mesh.facevarying_normals[9 * prim_id + 4]; - n1[2] = mesh.facevarying_normals[9 * prim_id + 5]; - n2[0] = mesh.facevarying_normals[9 * prim_id + 6]; - n2[1] = mesh.facevarying_normals[9 * prim_id + 7]; - n2[2] = mesh.facevarying_normals[9 * prim_id + 8]; - N = Lerp3(n0, n1, n2, isect.u, isect.v); - } else { - unsigned int f0, f1, f2; - f0 = mesh.faces[3 * prim_id + 0]; - f1 = mesh.faces[3 * prim_id + 1]; - f2 = mesh.faces[3 * prim_id + 2]; - - float3 v0, v1, v2; - v0[0] = mesh.vertices[3 * f0 + 0]; - v0[1] = mesh.vertices[3 * f0 + 1]; - v0[2] = mesh.vertices[3 * f0 + 2]; - v1[0] = mesh.vertices[3 * f1 + 0]; - v1[1] = mesh.vertices[3 * f1 + 1]; - v1[2] = mesh.vertices[3 * f1 + 2]; - v2[0] = mesh.vertices[3 * f2 + 0]; - v2[1] = mesh.vertices[3 * f2 + 1]; - v2[2] = mesh.vertices[3 * f2 + 2]; - CalcNormal(N, v0, v1, v2); - } - - config.normalImage[4 * (y * config.width + x) + 0] = - 0.5f * N[0] + 0.5f; - config.normalImage[4 * (y * config.width + x) + 1] = - 0.5f * N[1] + 0.5f; - config.normalImage[4 * (y * config.width + x) + 2] = - 0.5f * N[2] + 0.5f; - config.normalImage[4 * (y * config.width + x) + 3] = 1.0f; - - config.depthImage[4 * (y * config.width + x) + 0] = - isect.t; - config.depthImage[4 * (y * config.width + x) + 1] = - isect.t; - config.depthImage[4 * (y * config.width + x) + 2] = - isect.t; - config.depthImage[4 * (y * config.width + x) + 3] = 1.0f; - - float3 UV; - if (mesh.facevarying_uvs.size() > 0) { - float3 uv0, uv1, uv2; - uv0[0] = mesh.facevarying_uvs[6 * prim_id + 0]; - uv0[1] = mesh.facevarying_uvs[6 * prim_id + 1]; - uv1[0] = mesh.facevarying_uvs[6 * prim_id + 2]; - uv1[1] = mesh.facevarying_uvs[6 * prim_id + 3]; - uv2[0] = mesh.facevarying_uvs[6 * prim_id + 4]; - uv2[1] = mesh.facevarying_uvs[6 * prim_id + 5]; - - UV = Lerp3(uv0, uv1, uv2, isect.u, isect.v); - - config.texcoordImage[4 * (y * config.width + x) + 0] = UV[0]; - config.texcoordImage[4 * (y * config.width + x) + 1] = UV[1]; - } - - // Fetch texture - unsigned int material_id = - mesh.material_ids[isect.prim_id]; - - float diffuse_col[3]; - int diffuse_texid = materials[material_id].diffuse_texid; - if (diffuse_texid >= 0) { - FetchTexture(textures[diffuse_texid], UV[0], UV[1], diffuse_col); - } else { - diffuse_col[0] = materials[material_id].diffuse[0]; - diffuse_col[1] = materials[material_id].diffuse[1]; - diffuse_col[2] = materials[material_id].diffuse[2]; - } - - float specular_col[3]; - int specular_texid = materials[material_id].specular_texid; - if (specular_texid >= 0) { - FetchTexture(textures[specular_texid], UV[0], UV[1], specular_col); - } else { - specular_col[0] = materials[material_id].specular[0]; - specular_col[1] = materials[material_id].specular[1]; - specular_col[2] = materials[material_id].specular[2]; - } - - // Simple shading - float NdotV = fabsf(vdot(N, dir)); - - if (config.pass == 0) { - rgba[4 * (y * config.width + x) + 0] = NdotV * diffuse_col[0]; - rgba[4 * (y * config.width + x) + 1] = NdotV * diffuse_col[1]; - rgba[4 * (y * config.width + x) + 2] = NdotV * diffuse_col[2]; - rgba[4 * (y * config.width + x) + 3] = 1.0f; - sample_counts[y * config.width + x] = - 1; // Set 1 for the first pass - } else { // additive. - rgba[4 * (y * config.width + x) + 0] += NdotV * diffuse_col[0]; - rgba[4 * (y * config.width + x) + 1] += NdotV * diffuse_col[1]; - rgba[4 * (y * config.width + x) + 2] += NdotV * diffuse_col[2]; - rgba[4 * (y * config.width + x) + 3] += 1.0f; - sample_counts[y * config.width + x]++; - } - - } else { - { - if (config.pass == 0) { - // clear pixel - rgba[4 * (y * config.width + x) + 0] = 0.0f; - rgba[4 * (y * config.width + x) + 1] = 0.0f; - rgba[4 * (y * config.width + x) + 2] = 0.0f; - rgba[4 * (y * config.width + x) + 3] = 0.0f; - aux_rgba[4 * (y * config.width + x) + 0] = 0.0f; - aux_rgba[4 * (y * config.width + x) + 1] = 0.0f; - aux_rgba[4 * (y * config.width + x) + 2] = 0.0f; - aux_rgba[4 * (y * config.width + x) + 3] = 0.0f; - sample_counts[y * config.width + x] = - 1; // Set 1 for the first pass - } else { - sample_counts[y * config.width + x]++; - } - - // No super sampling - config.normalImage[4 * (y * config.width + x) + 0] = 0.0f; - config.normalImage[4 * (y * config.width + x) + 1] = 0.0f; - config.normalImage[4 * (y * config.width + x) + 2] = 0.0f; - config.normalImage[4 * (y * config.width + x) + 3] = 0.0f; - config.positionImage[4 * (y * config.width + x) + 0] = 0.0f; - config.positionImage[4 * (y * config.width + x) + 1] = 0.0f; - config.positionImage[4 * (y * config.width + x) + 2] = 0.0f; - config.positionImage[4 * (y * config.width + x) + 3] = 0.0f; - config.depthImage[4 * (y * config.width + x) + 0] = 0.0f; - config.depthImage[4 * (y * config.width + x) + 1] = 0.0f; - config.depthImage[4 * (y * config.width + x) + 2] = 0.0f; - config.depthImage[4 * (y * config.width + x) + 3] = 0.0f; - config.texcoordImage[4 * (y * config.width + x) + 0] = 0.0f; - config.texcoordImage[4 * (y * config.width + x) + 1] = 0.0f; - config.texcoordImage[4 * (y * config.width + x) + 2] = 0.0f; - config.texcoordImage[4 * (y * config.width + x) + 3] = 0.0f; - config.varycoordImage[4 * (y * config.width + x) + 0] = 0.0f; - config.varycoordImage[4 * (y * config.width + x) + 1] = 0.0f; - config.varycoordImage[4 * (y * config.width + x) + 2] = 0.0f; - config.varycoordImage[4 * (y * config.width + x) + 3] = 0.0f; - } - } - } - - for (int x = 0; x < config.width; x++) { - aux_rgba[4 * (y * config.width + x) + 0] = 0.0f; - aux_rgba[4 * (y * config.width + x) + 1] = 0.0f; - aux_rgba[4 * (y * config.width + x) + 2] = 0.0f; - aux_rgba[4 * (y * config.width + x) + 3] = 0.0f; - } - } - })); - } - - for (auto& t : workers) { - t.join(); - } - - return (!cancelFlag); -}; - -} // namespace example diff --git a/examples/raytrace/render.h b/examples/raytrace/render.h deleted file mode 100644 index 8c24164..0000000 --- a/examples/raytrace/render.h +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef EXAMPLE_RENDER_H_ -#define EXAMPLE_RENDER_H_ - -#include // C++11 - -#include "render-config.h" -#include "nanosg.h" -#include "mesh.h" -#include "material.h" - -namespace example { - -struct Asset { - std::vector > meshes; - std::vector materials; - std::vector textures; -}; - -class Renderer { - public: - Renderer() {} - ~Renderer() {} - - /// Returns false when the rendering was canceled. - static bool Render(float* rgba, float* aux_rgba, int *sample_counts, float quat[4], - const nanosg::Scene> &scene, const Asset &asset, const RenderConfig& config, std::atomic& cancel_flag); -}; -}; - -#endif // EXAMPLE_RENDER_H_