diff --git a/examples/raytrace/README.md b/examples/raytrace/README.md new file mode 100644 index 0000000..bfe2e7f --- /dev/null +++ b/examples/raytrace/README.md @@ -0,0 +1,130 @@ +# NanoSG + +Simple, minimal and header-only scene graph library for NanoRT. + +NanoSG itself shoud be compiled with C++-03 compiler, but demo code uses C++11 features. + +![](images/nanosg-demo.png) + +![](https://media.giphy.com/media/l3JDO29fMFndyObHW/giphy.gif) + +## Build + +### Linux or macOS + +``` +$ premake5 gmake +$ make +``` + +### Windows + +``` +$ premake5 vs2015 +``` + +## Data structure + +### Node + +Node represents scene graph node. Tansformation node or Mesh(shape) node. +Node is interpreted as transformation node when passing `nullptr` to Node class constructure. + +Node can contain multiple children. + +### Scene + +Scene contains root nodes and provides the method to find an intersection of nodes. + +## User defined data structure + +Following are required in user application. + +### Mesh class + +Current example code assumes mesh is all composed of triangle meshes. + +Following method must be implemented for `Scene::Traversal`. + +``` +/// +/// Get the geometric normal and the shading normal at `face_idx' th face. +/// +template +void GetNormal(T Ng[3], T Ns[3], const unsigned int face_idx, const T u, const T v) const; +``` + +### Intersection class + +Represents intersection(hit) information. + +### Transform + +Transformation is done in the following procedure. + +`M' = parent_xform x local_xform x local_pivot` + +## Memory management + +`Scene` and `Node` does not create a copy of asset data(e.g. vertices, indices). Thus user must care about memory management of scene assets in user side. + +## API + +API is still subject to change. + +### Node + +``` +void Node::SetName(const std::string &name); +``` + +Set (unique) name for the node. + +``` +void Node::AddChild(const type &child); +``` + +Add node as child node. + +``` +void Node::SetLocalXform(const T xform[4][4]) { +``` + +Set local transformation matrix. Default is identity matrix. + +### Scene + +``` +bool Scene::AddNode(const Node &node); +``` + +Add a node to the scene. + +``` +bool Scene::Commit() { +``` + +Commit the scene. After adding nodes to the scene or changed transformation matrix, call this `Commit` before tracing rays. +`Commit` triggers BVH build in each nodes and updates node's transformation matrix. + +``` +template +bool Scene::Traverse(nanort::Ray &ray, H *isect, const bool cull_back_face = false) const; +``` + +Trace ray into the scene and find an intersection. +Returns `true` when there is an intersection and hit information is stored in `isect`. + +## TODO + +* [ ] Compute pivot point of each node(mesh). + +## 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/bin/native/Release/view b/examples/raytrace/bin/native/Release/view new file mode 100755 index 0000000..bd3a59f Binary files /dev/null and b/examples/raytrace/bin/native/Release/view differ diff --git a/examples/raytrace/config.json b/examples/raytrace/config.json new file mode 100644 index 0000000..b64a131 --- /dev/null +++ b/examples/raytrace/config.json @@ -0,0 +1,9 @@ +{ "obj_filename" : "../common/cornellbox_suzanne.obj", + "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/images/nanosg-demo.png b/examples/raytrace/images/nanosg-demo.png new file mode 100644 index 0000000..5715e58 Binary files /dev/null and b/examples/raytrace/images/nanosg-demo.png differ diff --git a/examples/raytrace/imgui.ini b/examples/raytrace/imgui.ini new file mode 100644 index 0000000..d72fd2f --- /dev/null +++ b/examples/raytrace/imgui.ini @@ -0,0 +1,25 @@ +[Debug] +Pos=60,60 +Size=400,400 +Collapsed=0 + +[UI] +Pos=-41,636 +Size=469,191 +Collapsed=0 + +[Render] +Pos=583,717 +Size=410,425 +Collapsed=0 + +[Scene] +Pos=60,60 +Size=256,206 +Collapsed=0 + +[Transform] +Pos=579,636 +Size=235,167 +Collapsed=0 + diff --git a/examples/raytrace/main.cc b/examples/raytrace/main.cc new file mode 100644 index 0000000..5aea00b --- /dev/null +++ b/examples/raytrace/main.cc @@ -0,0 +1,1032 @@ +/* +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 "obj-loader.h" +#include "trackball.h" + +#ifdef WIN32 +#ifndef NOMINMAX +#define NOMINMAX +#endif +#endif + +b3gDefaultOpenGLWindow* window = 0; +int gWidth = 512; +int gHeight = 512; +int gMousePosX = -1, gMousePosY = -1; +bool gMouseLeftDown = false; + +//FIX issue when max passes is done - no modes is switched. pass must be set to 0 when mode is changed +int gShowBufferMode_prv = SHOW_BUFFER_COLOR; +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 + , + gShowBufferMode //added mode passing + ); + + 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); + } + +} + +//tigra: add default material + example::Material default_material; + +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; + + //tigra: set default material to 95% white diffuse + default_material.diffuse[0] = 0.95f; + default_material.diffuse[1] = 0.95f; + default_material.diffuse[2] = 0.95f; + + default_material.specular[0] = 0; + default_material.specular[1] = 0; + default_material.specular[2] = 0; + + bool ret = LoadObj(gRenderConfig.obj_filename, gRenderConfig.scene_scale, &meshes, &materials, &textures); + if (!ret) { + std::cerr << "Failed to load .obj [ " << gRenderConfig.obj_filename << " ]" << std::endl; + return -1; + } + + gAsset.materials = materials; + gAsset.default_material = default_material; + 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"); + + //fix max passes issue + if(gShowBufferMode_prv!=gShowBufferMode) + { + gRenderConfig.pass = 0; + gShowBufferMode_prv = gShowBufferMode; + } + + + // 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 new file mode 100644 index 0000000..c11b953 --- /dev/null +++ b/examples/raytrace/material.h @@ -0,0 +1,75 @@ +#ifndef EXAMPLE_MATERIAL_H_ +#define EXAMPLE_MATERIAL_H_ + +#include + +#ifdef __clang__ +#pragma clang diagnostic push +#if __has_warning("-Wzero-as-null-pointer-constant") +#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" +#endif +#endif + +namespace example { + +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; + int _pad_; + 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 new file mode 100644 index 0000000..b6670a9 --- /dev/null +++ b/examples/raytrace/matrix.cc @@ -0,0 +1,216 @@ +#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 new file mode 100644 index 0000000..4261673 --- /dev/null +++ b/examples/raytrace/mesh.h @@ -0,0 +1,188 @@ +#ifndef EXAMPLE_MESH_H_ +#define EXAMPLE_MESH_H_ + +#include +#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: + explicit Mesh(const size_t vertex_stride) : + stride(vertex_stride) { + } + + std::string name; + + std::vector vertices; /// stride * 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]; + size_t stride; /// stride for vertex data. + + // --- 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/nanosg.h b/examples/raytrace/nanosg.h new file mode 100644 index 0000000..8e1ef28 --- /dev/null +++ b/examples/raytrace/nanosg.h @@ -0,0 +1,876 @@ +/* +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(const 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(), mesh_->stride); + nanort::TriangleSAHPred triangle_pred( + mesh_->vertices.data(), mesh_->faces.data(), mesh_->stride); + + bool ret = + accel_.Build(static_cast(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_; + int _pad_; +}; + +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) ? 1 : 0; + ray_dir_sign_[1] = ray.dir[1] < static_cast(0.0) ? 1 : 0; + ray_dir_sign_[2] = ray.dir[2] < static_cast(0.0) ? 1 : 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(); + (void)stats; + + // 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(), + node.GetMesh()->stride); + 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); + // printf("tworld %f, tnear %f\n", t_world, t_nearest); + + 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/obj-loader.cc b/examples/raytrace/obj-loader.cc new file mode 100644 index 0000000..7ebc9b9 --- /dev/null +++ b/examples/raytrace/obj-loader.cc @@ -0,0 +1,459 @@ +#include "obj-loader.h" +#include "../../nanort.h" // for float3 + +#define TINYOBJLOADER_IMPLEMENTATION +#include "tiny_obj_loader.h" + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wold-style-cast" +#pragma clang diagnostic ignored "-Wreserved-id-macro" +#pragma clang diagnostic ignored "-Wc++98-compat-pedantic" +#pragma clang diagnostic ignored "-Wcast-align" +#pragma clang diagnostic ignored "-Wpadded" +#pragma clang diagnostic ignored "-Wold-style-cast" +#pragma clang diagnostic ignored "-Wsign-conversion" +#pragma clang diagnostic ignored "-Wvariadic-macros" +#pragma clang diagnostic ignored "-Wc++11-extensions" +#pragma clang diagnostic ignored "-Wdisabled-macro-expansion" +#pragma clang diagnostic ignored "-Wimplicit-fallthrough" +#if __has_warning("-Wdouble-promotion") +#pragma clang diagnostic ignored "-Wdouble-promotion" +#endif +#if __has_warning("-Wcomma") +#pragma clang diagnostic ignored "-Wcomma" +#endif +#if __has_warning("-Wcast-qual") +#pragma clang diagnostic ignored "-Wcast-qual" +#endif +#endif + +#define STB_IMAGE_IMPLEMENTATION +#include "stb_image.h" + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +#include + +#ifdef NANOSG_USE_CXX11 +#include +#else +#include +#endif + +#define USE_TEX_CACHE 1 + +namespace example { + +typedef nanort::real3 float3; + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wexit-time-destructors" +#pragma clang diagnostic ignored "-Wglobal-constructors" +#endif + +// TODO(LTE): Remove global static definition. +#ifdef NANOSG_USE_CXX11 +static std::unordered_map hashed_tex; +#else +static std::map hashed_tex; +#endif + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +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); +} + +static std::string GetBaseDir(const std::string &filepath) { + if (filepath.find_last_of("/\\") != std::string::npos) + return filepath.substr(0, filepath.find_last_of("/\\")); + return ""; +} + +static int LoadTexture(const std::string &filename, + std::vector *textures) { + int idx; + + if (filename.empty()) return -1; + + std::cout << " Loading texture : " << filename << std::endl; + Texture texture; + + // tigra: find in cache. get index + if (USE_TEX_CACHE) { + if (hashed_tex.find(filename) != hashed_tex.end()) { + puts("from cache"); + return hashed_tex[filename]; + } + } + + int w, h, n; + unsigned char *data = stbi_load(filename.c_str(), &w, &h, &n, 0); + if (data) { + texture.width = w; + texture.height = h; + texture.components = n; + + size_t n_elem = size_t(w * h * n); + texture.image = new unsigned char[n_elem]; + for (size_t i = 0; i < n_elem; i++) { + texture.image[i] = data[i]; + } + + free(data); + + textures->push_back(texture); + + idx = int(textures->size()) - 1; + + // tigra: store index to cache + if (USE_TEX_CACHE) { + hashed_tex[filename] = idx; + } + + return idx; + } + + std::cout << " Failed to load : " << filename << std::endl; + return -1; +} + +static void ComputeBoundingBoxOfMesh(float bmin[3], float bmax[3], + const example::Mesh &mesh) { + bmin[0] = bmin[1] = bmin[2] = std::numeric_limits::max(); + bmax[0] = bmax[1] = bmax[2] = -std::numeric_limits::max(); + + for (size_t i = 0; i < mesh.vertices.size() / 3; i++) { + bmin[0] = std::min(bmin[0], mesh.vertices[3 * i + 0]); + bmin[1] = std::min(bmin[1], mesh.vertices[3 * i + 1]); + bmin[2] = std::min(bmin[1], mesh.vertices[3 * i + 2]); + + bmax[0] = std::max(bmax[0], mesh.vertices[3 * i + 0]); + bmax[1] = std::max(bmax[1], mesh.vertices[3 * i + 1]); + bmax[2] = std::max(bmax[2], mesh.vertices[3 * i + 2]); + } +} + +bool LoadObj(const std::string &filename, float scale, + std::vector > *meshes, + std::vector *out_materials, + std::vector *out_textures) { + tinyobj::attrib_t attrib; + std::vector shapes; + std::vector materials; + std::string err; + + std::string basedir = GetBaseDir(filename) + "/"; + const char *basepath = (basedir.compare("/") == 0) ? NULL : basedir.c_str(); + + // auto t_start = std::chrono::system_clock::now(); + + bool ret = + tinyobj::LoadObj(&attrib, &shapes, &materials, &err, filename.c_str(), + basepath, /* triangulate */ true); + + // auto t_end = std::chrono::system_clock::now(); + // std::chrono::duration ms = t_end - t_start; + + if (!err.empty()) { + std::cerr << err << std::endl; + } + + if (!ret) { + return false; + } + + // std::cout << "[LoadOBJ] Parse time : " << ms.count() << " [msecs]" + // << std::endl; + + std::cout << "[LoadOBJ] # of shapes in .obj : " << shapes.size() << std::endl; + std::cout << "[LoadOBJ] # of materials in .obj : " << materials.size() + << std::endl; + + { + size_t total_num_vertices = 0; + size_t total_num_faces = 0; + + total_num_vertices = attrib.vertices.size() / 3; + std::cout << " vertices : " << attrib.vertices.size() / 3 << std::endl; + + for (size_t i = 0; i < shapes.size(); i++) { + std::cout << " shape[" << i << "].name : " << shapes[i].name + << std::endl; + std::cout << " shape[" << i + << "].indices : " << shapes[i].mesh.indices.size() << std::endl; + assert((shapes[i].mesh.indices.size() % 3) == 0); + + total_num_faces += shapes[i].mesh.indices.size() / 3; + + // tigra: empty name convert to _id + if (shapes[i].name.length() == 0) { +#ifdef NANOSG_USE_CXX11 + shapes[i].name = "_" + std::to_string(i); +#else + std::stringstream ss; + ss << i; + shapes[i].name = "_" + ss.str(); +#endif + std::cout << " EMPTY shape[" << i << "].name, new : " << shapes[i].name + << std::endl; + } + } + std::cout << "[LoadOBJ] # of faces: " << total_num_faces << std::endl; + std::cout << "[LoadOBJ] # of vertices: " << total_num_vertices << std::endl; + } + + // TODO(LTE): Implement tangents and binormals + + for (size_t i = 0; i < shapes.size(); i++) { + Mesh mesh(/* stride */ sizeof(float) * 3); + + mesh.name = shapes[i].name; + + const size_t num_faces = shapes[i].mesh.indices.size() / 3; + mesh.faces.resize(num_faces * 3); + mesh.material_ids.resize(num_faces); + mesh.facevarying_normals.resize(num_faces * 3 * 3); + mesh.facevarying_uvs.resize(num_faces * 3 * 2); + mesh.vertices.resize(num_faces * 3 * 3); + + for (size_t f = 0; f < shapes[i].mesh.indices.size() / 3; f++) { + // reorder vertices. may create duplicated vertices. + size_t f0 = size_t(shapes[i].mesh.indices[3 * f + 0].vertex_index); + size_t f1 = size_t(shapes[i].mesh.indices[3 * f + 1].vertex_index); + size_t f2 = size_t(shapes[i].mesh.indices[3 * f + 2].vertex_index); + + mesh.vertices[9 * f + 0] = scale * attrib.vertices[3 * f0 + 0]; + mesh.vertices[9 * f + 1] = scale * attrib.vertices[3 * f0 + 1]; + mesh.vertices[9 * f + 2] = scale * attrib.vertices[3 * f0 + 2]; + + mesh.vertices[9 * f + 3] = scale * attrib.vertices[3 * f1 + 0]; + mesh.vertices[9 * f + 4] = scale * attrib.vertices[3 * f1 + 1]; + mesh.vertices[9 * f + 5] = scale * attrib.vertices[3 * f1 + 2]; + + mesh.vertices[9 * f + 6] = scale * attrib.vertices[3 * f2 + 0]; + mesh.vertices[9 * f + 7] = scale * attrib.vertices[3 * f2 + 1]; + mesh.vertices[9 * f + 8] = scale * attrib.vertices[3 * f2 + 2]; + + mesh.faces[3 * f + 0] = static_cast(3 * f + 0); + mesh.faces[3 * f + 1] = static_cast(3 * f + 1); + mesh.faces[3 * f + 2] = static_cast(3 * f + 2); + + mesh.material_ids[f] = + static_cast(shapes[i].mesh.material_ids[f]); + } + + if (attrib.normals.size() > 0) { + for (size_t f = 0; f < shapes[i].mesh.indices.size() / 3; f++) { + size_t f0, f1, f2; + + f0 = size_t(shapes[i].mesh.indices[3 * f + 0].normal_index); + f1 = size_t(shapes[i].mesh.indices[3 * f + 1].normal_index); + f2 = size_t(shapes[i].mesh.indices[3 * f + 2].normal_index); + + if (f0 > 0 && f1 > 0 && f2 > 0) { + float n0[3], n1[3], n2[3]; + + n0[0] = attrib.normals[3 * f0 + 0]; + n0[1] = attrib.normals[3 * f0 + 1]; + n0[2] = attrib.normals[3 * f0 + 2]; + + n1[0] = attrib.normals[3 * f1 + 0]; + n1[1] = attrib.normals[3 * f1 + 1]; + n1[2] = attrib.normals[3 * f1 + 2]; + + n2[0] = attrib.normals[3 * f2 + 0]; + n2[1] = attrib.normals[3 * f2 + 1]; + n2[2] = attrib.normals[3 * f2 + 2]; + + mesh.facevarying_normals[3 * (3 * f + 0) + 0] = n0[0]; + mesh.facevarying_normals[3 * (3 * f + 0) + 1] = n0[1]; + mesh.facevarying_normals[3 * (3 * f + 0) + 2] = n0[2]; + + mesh.facevarying_normals[3 * (3 * f + 1) + 0] = n1[0]; + mesh.facevarying_normals[3 * (3 * f + 1) + 1] = n1[1]; + mesh.facevarying_normals[3 * (3 * f + 1) + 2] = n1[2]; + + mesh.facevarying_normals[3 * (3 * f + 2) + 0] = n2[0]; + mesh.facevarying_normals[3 * (3 * f + 2) + 1] = n2[1]; + mesh.facevarying_normals[3 * (3 * f + 2) + 2] = n2[2]; + } else { // face contains invalid normal index. calc geometric normal. + f0 = size_t(shapes[i].mesh.indices[3 * f + 0].vertex_index); + f1 = size_t(shapes[i].mesh.indices[3 * f + 1].vertex_index); + f2 = size_t(shapes[i].mesh.indices[3 * f + 2].vertex_index); + + float3 v0, v1, v2; + + v0[0] = attrib.vertices[3 * f0 + 0]; + v0[1] = attrib.vertices[3 * f0 + 1]; + v0[2] = attrib.vertices[3 * f0 + 2]; + + v1[0] = attrib.vertices[3 * f1 + 0]; + v1[1] = attrib.vertices[3 * f1 + 1]; + v1[2] = attrib.vertices[3 * f1 + 2]; + + v2[0] = attrib.vertices[3 * f2 + 0]; + v2[1] = attrib.vertices[3 * f2 + 1]; + v2[2] = attrib.vertices[3 * f2 + 2]; + + float3 N; + CalcNormal(N, v0, v1, v2); + + mesh.facevarying_normals[3 * (3 * f + 0) + 0] = N[0]; + mesh.facevarying_normals[3 * (3 * f + 0) + 1] = N[1]; + mesh.facevarying_normals[3 * (3 * f + 0) + 2] = N[2]; + + mesh.facevarying_normals[3 * (3 * f + 1) + 0] = N[0]; + mesh.facevarying_normals[3 * (3 * f + 1) + 1] = N[1]; + mesh.facevarying_normals[3 * (3 * f + 1) + 2] = N[2]; + + mesh.facevarying_normals[3 * (3 * f + 2) + 0] = N[0]; + mesh.facevarying_normals[3 * (3 * f + 2) + 1] = N[1]; + mesh.facevarying_normals[3 * (3 * f + 2) + 2] = N[2]; + } + } + } else { + // calc geometric normal + for (size_t f = 0; f < shapes[i].mesh.indices.size() / 3; f++) { + size_t f0, f1, f2; + + f0 = size_t(shapes[i].mesh.indices[3 * f + 0].vertex_index); + f1 = size_t(shapes[i].mesh.indices[3 * f + 1].vertex_index); + f2 = size_t(shapes[i].mesh.indices[3 * f + 2].vertex_index); + + float3 v0, v1, v2; + + v0[0] = attrib.vertices[3 * f0 + 0]; + v0[1] = attrib.vertices[3 * f0 + 1]; + v0[2] = attrib.vertices[3 * f0 + 2]; + + v1[0] = attrib.vertices[3 * f1 + 0]; + v1[1] = attrib.vertices[3 * f1 + 1]; + v1[2] = attrib.vertices[3 * f1 + 2]; + + v2[0] = attrib.vertices[3 * f2 + 0]; + v2[1] = attrib.vertices[3 * f2 + 1]; + v2[2] = attrib.vertices[3 * f2 + 2]; + + float3 N; + CalcNormal(N, v0, v1, v2); + + mesh.facevarying_normals[3 * (3 * f + 0) + 0] = N[0]; + mesh.facevarying_normals[3 * (3 * f + 0) + 1] = N[1]; + mesh.facevarying_normals[3 * (3 * f + 0) + 2] = N[2]; + + mesh.facevarying_normals[3 * (3 * f + 1) + 0] = N[0]; + mesh.facevarying_normals[3 * (3 * f + 1) + 1] = N[1]; + mesh.facevarying_normals[3 * (3 * f + 1) + 2] = N[2]; + + mesh.facevarying_normals[3 * (3 * f + 2) + 0] = N[0]; + mesh.facevarying_normals[3 * (3 * f + 2) + 1] = N[1]; + mesh.facevarying_normals[3 * (3 * f + 2) + 2] = N[2]; + } + } + + if (attrib.texcoords.size() > 0) { + for (size_t f = 0; f < shapes[i].mesh.indices.size() / 3; f++) { + size_t f0, f1, f2; + + f0 = size_t(shapes[i].mesh.indices[3 * f + 0].texcoord_index); + f1 = size_t(shapes[i].mesh.indices[3 * f + 1].texcoord_index); + f2 = size_t(shapes[i].mesh.indices[3 * f + 2].texcoord_index); + + if (f0 > 0 && f1 > 0 && f2 > 0) { + float3 n0, n1, n2; + + n0[0] = attrib.texcoords[2 * f0 + 0]; + n0[1] = attrib.texcoords[2 * f0 + 1]; + + n1[0] = attrib.texcoords[2 * f1 + 0]; + n1[1] = attrib.texcoords[2 * f1 + 1]; + + n2[0] = attrib.texcoords[2 * f2 + 0]; + n2[1] = attrib.texcoords[2 * f2 + 1]; + + mesh.facevarying_uvs[2 * (3 * f + 0) + 0] = n0[0]; + mesh.facevarying_uvs[2 * (3 * f + 0) + 1] = n0[1]; + + mesh.facevarying_uvs[2 * (3 * f + 1) + 0] = n1[0]; + mesh.facevarying_uvs[2 * (3 * f + 1) + 1] = n1[1]; + + mesh.facevarying_uvs[2 * (3 * f + 2) + 0] = n2[0]; + mesh.facevarying_uvs[2 * (3 * f + 2) + 1] = n2[1]; + } + } + } + + // Compute pivot translation and add offset to the vertices. + float bmin[3], bmax[3]; + ComputeBoundingBoxOfMesh(bmin, bmax, mesh); + + float bcenter[3]; + bcenter[0] = 0.5f * (bmax[0] - bmin[0]) + bmin[0]; + bcenter[1] = 0.5f * (bmax[1] - bmin[1]) + bmin[1]; + bcenter[2] = 0.5f * (bmax[2] - bmin[2]) + bmin[2]; + + for (size_t v = 0; v < mesh.vertices.size() / 3; v++) { + mesh.vertices[3 * v + 0] -= bcenter[0]; + mesh.vertices[3 * v + 1] -= bcenter[1]; + mesh.vertices[3 * v + 2] -= bcenter[2]; + } + + mesh.pivot_xform[0][0] = 1.0f; + mesh.pivot_xform[0][1] = 0.0f; + mesh.pivot_xform[0][2] = 0.0f; + mesh.pivot_xform[0][3] = 0.0f; + + mesh.pivot_xform[1][0] = 0.0f; + mesh.pivot_xform[1][1] = 1.0f; + mesh.pivot_xform[1][2] = 0.0f; + mesh.pivot_xform[1][3] = 0.0f; + + mesh.pivot_xform[2][0] = 0.0f; + mesh.pivot_xform[2][1] = 0.0f; + mesh.pivot_xform[2][2] = 1.0f; + mesh.pivot_xform[2][3] = 0.0f; + + mesh.pivot_xform[3][0] = bcenter[0]; + mesh.pivot_xform[3][1] = bcenter[1]; + mesh.pivot_xform[3][2] = bcenter[2]; + mesh.pivot_xform[3][3] = 1.0f; + + meshes->push_back(mesh); + } + + // material_t -> Material and Texture + out_materials->resize(materials.size()); + out_textures->resize(0); + for (size_t i = 0; i < materials.size(); i++) { + (*out_materials)[i].diffuse[0] = materials[i].diffuse[0]; + (*out_materials)[i].diffuse[1] = materials[i].diffuse[1]; + (*out_materials)[i].diffuse[2] = materials[i].diffuse[2]; + (*out_materials)[i].specular[0] = materials[i].specular[0]; + (*out_materials)[i].specular[1] = materials[i].specular[1]; + (*out_materials)[i].specular[2] = materials[i].specular[2]; + + (*out_materials)[i].id = int(i); + + // map_Kd + (*out_materials)[i].diffuse_texid = + LoadTexture(materials[i].diffuse_texname, out_textures); + // map_Ks + (*out_materials)[i].specular_texid = + LoadTexture(materials[i].specular_texname, out_textures); + } + + return true; +} + +} // namespace example diff --git a/examples/raytrace/obj-loader.h b/examples/raytrace/obj-loader.h new file mode 100644 index 0000000..9827791 --- /dev/null +++ b/examples/raytrace/obj-loader.h @@ -0,0 +1,19 @@ +#ifndef EXAMPLE_OBJ_LOADER_H_ +#define EXAMPLE_OBJ_LOADER_H_ + +#include +#include + +#include "mesh.h" +#include "material.h" + +namespace example { + +/// +/// Loads wavefront .obj mesh +/// +bool LoadObj(const std::string &filename, float scale, std::vector > *meshes, std::vector *materials, std::vector *textures); + +} + +#endif // EXAMPLE_OBJ_LOADER_H_ diff --git a/examples/raytrace/premake5.lua b/examples/raytrace/premake5.lua new file mode 100644 index 0000000..e16dc89 --- /dev/null +++ b/examples/raytrace/premake5.lua @@ -0,0 +1,114 @@ +newoption { + trigger = "with-gtk3nfd", + description = "Build with native file dialog support(GTK3 required. Linux only)" +} + +newoption { + trigger = "asan", + description = "Enable Address Sanitizer(gcc5+ ang clang only)" +} + +sources = { + "main.cc", + "render.cc", + "render-config.cc", + "obj-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 "NanoSGSolution" + 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 new file mode 100644 index 0000000..b2e8e5f --- /dev/null +++ b/examples/raytrace/render-config.cc @@ -0,0 +1,116 @@ +#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("obj_filename") != o.end()) { + if (o["obj_filename"].is()) { + config->obj_filename = o["obj_filename"].get(); + } + } + + if (o.find("eson_filename") != o.end()) { + if (o["eson_filename"].is()) { + config->eson_filename = o["eson_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 new file mode 100644 index 0000000..fed4c0b --- /dev/null +++ b/examples/raytrace/render-config.h @@ -0,0 +1,42 @@ +#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 obj_filename; + std::string eson_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 new file mode 100644 index 0000000..a1b47ea --- /dev/null +++ b/examples/raytrace/render.cc @@ -0,0 +1,560 @@ +/* +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]; + } +} + +#if 0 // TODO(LTE): Not used method. Delete. +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]; + ray.dir[1] = dir[1]; + ray.dir[2] = dir[2]; + + return ray; +} +#endif + +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, + int &_showBufferMode + ) { + //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; + + //for modes not a "color" + if(_showBufferMode != SHOW_BUFFER_COLOR) + { + //only one pass + if(config.pass > 0) + continue; + + //to the center of pixel + u0 = 0.5f; + u1 = 0.5f; + } + + 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]; + + //tigra: add default material + const Material &default_material = asset.default_material; + + 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]; + + //printf("material_id=%d materials=%lld\n", material_id, materials.size()); + + float diffuse_col[3]; + + float specular_col[3]; + + //tigra: material_id is ok + if(material_id>=0 && material_id= 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]; + } + + 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]; + } + } + else + //tigra: wrong material_id, use default_material + { + + //printf("default_material\n"); + + diffuse_col[0] = default_material.diffuse[0]; + diffuse_col[1] = default_material.diffuse[1]; + diffuse_col[2] = default_material.diffuse[2]; + specular_col[0] = default_material.specular[0]; + specular_col[1] = default_material.specular[1]; + specular_col[2] = default_material.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 new file mode 100644 index 0000000..ccd2f2d --- /dev/null +++ b/examples/raytrace/render.h @@ -0,0 +1,45 @@ +#ifndef EXAMPLE_RENDER_H_ +#define EXAMPLE_RENDER_H_ + +#include // C++11 + +//mode definitions now here + +#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) + +#include "render-config.h" +#include "nanosg.h" +#include "mesh.h" +#include "material.h" + +namespace example { + +struct Asset { + std::vector > meshes; + std::vector materials; + + //tigra: add default material + Material default_material; + 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, + int& _showBufferMode + ); +}; +}; + +#endif // EXAMPLE_RENDER_H_ diff --git a/examples/raytrace/viwewer.make b/examples/raytrace/viwewer.make new file mode 100644 index 0000000..e341427 --- /dev/null +++ b/examples/raytrace/viwewer.make @@ -0,0 +1,339 @@ +# GNU Make project makefile autogenerated by Premake + +ifndef config + config=release_native +endif + +ifndef verbose + SILENT = @ +endif + +.PHONY: clean prebuild prelink + +ifeq ($(config),release_native) + RESCOMP = windres + TARGETDIR = bin/native/Release + TARGET = $(TARGETDIR)/view + OBJDIR = obj/native/Release + DEFINES += -DGLEW_INIT_OPENGL11_FUNCTIONS=1 -DGLEW_STATIC -DGLEW_DYNAMIC_LOAD_ALL_GLX_FUNCTIONS=1 + INCLUDES += -I../common/ThirdPartyLibs/Glew -I. -I../.. -I../common -I../common/imgui -I../common/glm + FORCE_INCLUDE += + ALL_CPPFLAGS += $(CPPFLAGS) -MMD -MP $(DEFINES) $(INCLUDES) + ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -O2 -g + ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CPPFLAGS) -O2 -g -std=c++11 + ALL_RESFLAGS += $(RESFLAGS) $(DEFINES) $(INCLUDES) + LIBS += -lX11 -lpthread -ldl + LDDEPS += + ALL_LDFLAGS += $(LDFLAGS) + LINKCMD = $(CXX) -o "$@" $(OBJECTS) $(RESOURCES) $(ALL_LDFLAGS) $(LIBS) + define PREBUILDCMDS + endef + define PRELINKCMDS + endef + define POSTBUILDCMDS + endef +all: prebuild prelink $(TARGET) + @: + +endif + +ifeq ($(config),release_x64) + RESCOMP = windres + TARGETDIR = bin/x64/Release + TARGET = $(TARGETDIR)/view + OBJDIR = obj/x64/Release + DEFINES += -DGLEW_INIT_OPENGL11_FUNCTIONS=1 -DGLEW_STATIC -DGLEW_DYNAMIC_LOAD_ALL_GLX_FUNCTIONS=1 + INCLUDES += -I../common/ThirdPartyLibs/Glew -I. -I../.. -I../common -I../common/imgui -I../common/glm + FORCE_INCLUDE += + ALL_CPPFLAGS += $(CPPFLAGS) -MMD -MP $(DEFINES) $(INCLUDES) + ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -m64 -O2 -g + ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CPPFLAGS) -m64 -O2 -g -std=c++11 + ALL_RESFLAGS += $(RESFLAGS) $(DEFINES) $(INCLUDES) + LIBS += -lX11 -lpthread -ldl + LDDEPS += + ALL_LDFLAGS += $(LDFLAGS) -L/usr/lib64 -m64 + LINKCMD = $(CXX) -o "$@" $(OBJECTS) $(RESOURCES) $(ALL_LDFLAGS) $(LIBS) + define PREBUILDCMDS + endef + define PRELINKCMDS + endef + define POSTBUILDCMDS + endef +all: prebuild prelink $(TARGET) + @: + +endif + +ifeq ($(config),release_x32) + RESCOMP = windres + TARGETDIR = bin/x32/Release + TARGET = $(TARGETDIR)/view + OBJDIR = obj/x32/Release + DEFINES += -DGLEW_INIT_OPENGL11_FUNCTIONS=1 -DGLEW_STATIC -DGLEW_DYNAMIC_LOAD_ALL_GLX_FUNCTIONS=1 + INCLUDES += -I../common/ThirdPartyLibs/Glew -I. -I../.. -I../common -I../common/imgui -I../common/glm + FORCE_INCLUDE += + ALL_CPPFLAGS += $(CPPFLAGS) -MMD -MP $(DEFINES) $(INCLUDES) + ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -m32 -O2 -g + ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CPPFLAGS) -m32 -O2 -g -std=c++11 + ALL_RESFLAGS += $(RESFLAGS) $(DEFINES) $(INCLUDES) + LIBS += -lX11 -lpthread -ldl + LDDEPS += + ALL_LDFLAGS += $(LDFLAGS) -L/usr/lib32 -m32 + LINKCMD = $(CXX) -o "$@" $(OBJECTS) $(RESOURCES) $(ALL_LDFLAGS) $(LIBS) + define PREBUILDCMDS + endef + define PRELINKCMDS + endef + define POSTBUILDCMDS + endef +all: prebuild prelink $(TARGET) + @: + +endif + +ifeq ($(config),debug_native) + RESCOMP = windres + TARGETDIR = bin/native/Debug + TARGET = $(TARGETDIR)/view_debug + OBJDIR = obj/native/Debug + DEFINES += -DGLEW_INIT_OPENGL11_FUNCTIONS=1 -DGLEW_STATIC -DGLEW_DYNAMIC_LOAD_ALL_GLX_FUNCTIONS=1 -DDEBUG + INCLUDES += -I../common/ThirdPartyLibs/Glew -I. -I../.. -I../common -I../common/imgui -I../common/glm + FORCE_INCLUDE += + ALL_CPPFLAGS += $(CPPFLAGS) -MMD -MP $(DEFINES) $(INCLUDES) + ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -g + ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CPPFLAGS) -g -std=c++11 + ALL_RESFLAGS += $(RESFLAGS) $(DEFINES) $(INCLUDES) + LIBS += -lX11 -lpthread -ldl + LDDEPS += + ALL_LDFLAGS += $(LDFLAGS) + LINKCMD = $(CXX) -o "$@" $(OBJECTS) $(RESOURCES) $(ALL_LDFLAGS) $(LIBS) + define PREBUILDCMDS + endef + define PRELINKCMDS + endef + define POSTBUILDCMDS + endef +all: prebuild prelink $(TARGET) + @: + +endif + +ifeq ($(config),debug_x64) + RESCOMP = windres + TARGETDIR = bin/x64/Debug + TARGET = $(TARGETDIR)/view_debug + OBJDIR = obj/x64/Debug + DEFINES += -DGLEW_INIT_OPENGL11_FUNCTIONS=1 -DGLEW_STATIC -DGLEW_DYNAMIC_LOAD_ALL_GLX_FUNCTIONS=1 -DDEBUG + INCLUDES += -I../common/ThirdPartyLibs/Glew -I. -I../.. -I../common -I../common/imgui -I../common/glm + FORCE_INCLUDE += + ALL_CPPFLAGS += $(CPPFLAGS) -MMD -MP $(DEFINES) $(INCLUDES) + ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -m64 -g + ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CPPFLAGS) -m64 -g -std=c++11 + ALL_RESFLAGS += $(RESFLAGS) $(DEFINES) $(INCLUDES) + LIBS += -lX11 -lpthread -ldl + LDDEPS += + ALL_LDFLAGS += $(LDFLAGS) -L/usr/lib64 -m64 + LINKCMD = $(CXX) -o "$@" $(OBJECTS) $(RESOURCES) $(ALL_LDFLAGS) $(LIBS) + define PREBUILDCMDS + endef + define PRELINKCMDS + endef + define POSTBUILDCMDS + endef +all: prebuild prelink $(TARGET) + @: + +endif + +ifeq ($(config),debug_x32) + RESCOMP = windres + TARGETDIR = bin/x32/Debug + TARGET = $(TARGETDIR)/view_debug + OBJDIR = obj/x32/Debug + DEFINES += -DGLEW_INIT_OPENGL11_FUNCTIONS=1 -DGLEW_STATIC -DGLEW_DYNAMIC_LOAD_ALL_GLX_FUNCTIONS=1 -DDEBUG + INCLUDES += -I../common/ThirdPartyLibs/Glew -I. -I../.. -I../common -I../common/imgui -I../common/glm + FORCE_INCLUDE += + ALL_CPPFLAGS += $(CPPFLAGS) -MMD -MP $(DEFINES) $(INCLUDES) + ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -m32 -g + ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CPPFLAGS) -m32 -g -std=c++11 + ALL_RESFLAGS += $(RESFLAGS) $(DEFINES) $(INCLUDES) + LIBS += -lX11 -lpthread -ldl + LDDEPS += + ALL_LDFLAGS += $(LDFLAGS) -L/usr/lib32 -m32 + LINKCMD = $(CXX) -o "$@" $(OBJECTS) $(RESOURCES) $(ALL_LDFLAGS) $(LIBS) + define PREBUILDCMDS + endef + define PRELINKCMDS + endef + define POSTBUILDCMDS + endef +all: prebuild prelink $(TARGET) + @: + +endif + +OBJECTS := \ + $(OBJDIR)/X11OpenGLWindow.o \ + $(OBJDIR)/glew.o \ + $(OBJDIR)/ImGuizmo.o \ + $(OBJDIR)/imgui.o \ + $(OBJDIR)/imgui_draw.o \ + $(OBJDIR)/imgui_impl_btgui.o \ + $(OBJDIR)/trackball.o \ + $(OBJDIR)/main.o \ + $(OBJDIR)/matrix.o \ + $(OBJDIR)/obj-loader.o \ + $(OBJDIR)/render-config.o \ + $(OBJDIR)/render.o \ + +RESOURCES := \ + +CUSTOMFILES := \ + +SHELLTYPE := msdos +ifeq (,$(ComSpec)$(COMSPEC)) + SHELLTYPE := posix +endif +ifeq (/bin,$(findstring /bin,$(SHELL))) + SHELLTYPE := posix +endif + +$(TARGET): $(GCH) ${CUSTOMFILES} $(OBJECTS) $(LDDEPS) $(RESOURCES) + @echo Linking viwewer +ifeq (posix,$(SHELLTYPE)) + $(SILENT) mkdir -p $(TARGETDIR) +else + $(SILENT) mkdir $(subst /,\\,$(TARGETDIR)) +endif + $(SILENT) $(LINKCMD) + $(POSTBUILDCMDS) + +clean: + @echo Cleaning viwewer +ifeq (posix,$(SHELLTYPE)) + $(SILENT) rm -f $(TARGET) + $(SILENT) rm -rf $(OBJDIR) +else + $(SILENT) if exist $(subst /,\\,$(TARGET)) del $(subst /,\\,$(TARGET)) + $(SILENT) if exist $(subst /,\\,$(OBJDIR)) rmdir /s /q $(subst /,\\,$(OBJDIR)) +endif + +prebuild: + $(PREBUILDCMDS) + +prelink: + $(PRELINKCMDS) + +ifneq (,$(PCH)) +$(OBJECTS): $(GCH) $(PCH) +$(GCH): $(PCH) + @echo $(notdir $<) +ifeq (posix,$(SHELLTYPE)) + $(SILENT) mkdir -p $(OBJDIR) +else + $(SILENT) mkdir $(subst /,\\,$(OBJDIR)) +endif + $(SILENT) $(CXX) -x c++-header $(ALL_CXXFLAGS) -o "$@" -MF "$(@:%.gch=%.d)" -c "$<" +endif + +$(OBJDIR)/X11OpenGLWindow.o: ../common/OpenGLWindow/X11OpenGLWindow.cpp + @echo $(notdir $<) +ifeq (posix,$(SHELLTYPE)) + $(SILENT) mkdir -p $(OBJDIR) +else + $(SILENT) mkdir $(subst /,\\,$(OBJDIR)) +endif + $(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" +$(OBJDIR)/glew.o: ../common/ThirdPartyLibs/Glew/glew.c + @echo $(notdir $<) +ifeq (posix,$(SHELLTYPE)) + $(SILENT) mkdir -p $(OBJDIR) +else + $(SILENT) mkdir $(subst /,\\,$(OBJDIR)) +endif + $(SILENT) $(CC) $(ALL_CFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" +$(OBJDIR)/ImGuizmo.o: ../common/imgui/ImGuizmo.cpp + @echo $(notdir $<) +ifeq (posix,$(SHELLTYPE)) + $(SILENT) mkdir -p $(OBJDIR) +else + $(SILENT) mkdir $(subst /,\\,$(OBJDIR)) +endif + $(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" +$(OBJDIR)/imgui.o: ../common/imgui/imgui.cpp + @echo $(notdir $<) +ifeq (posix,$(SHELLTYPE)) + $(SILENT) mkdir -p $(OBJDIR) +else + $(SILENT) mkdir $(subst /,\\,$(OBJDIR)) +endif + $(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" +$(OBJDIR)/imgui_draw.o: ../common/imgui/imgui_draw.cpp + @echo $(notdir $<) +ifeq (posix,$(SHELLTYPE)) + $(SILENT) mkdir -p $(OBJDIR) +else + $(SILENT) mkdir $(subst /,\\,$(OBJDIR)) +endif + $(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" +$(OBJDIR)/imgui_impl_btgui.o: ../common/imgui/imgui_impl_btgui.cpp + @echo $(notdir $<) +ifeq (posix,$(SHELLTYPE)) + $(SILENT) mkdir -p $(OBJDIR) +else + $(SILENT) mkdir $(subst /,\\,$(OBJDIR)) +endif + $(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" +$(OBJDIR)/trackball.o: ../common/trackball.cc + @echo $(notdir $<) +ifeq (posix,$(SHELLTYPE)) + $(SILENT) mkdir -p $(OBJDIR) +else + $(SILENT) mkdir $(subst /,\\,$(OBJDIR)) +endif + $(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" +$(OBJDIR)/main.o: main.cc + @echo $(notdir $<) +ifeq (posix,$(SHELLTYPE)) + $(SILENT) mkdir -p $(OBJDIR) +else + $(SILENT) mkdir $(subst /,\\,$(OBJDIR)) +endif + $(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" +$(OBJDIR)/matrix.o: matrix.cc + @echo $(notdir $<) +ifeq (posix,$(SHELLTYPE)) + $(SILENT) mkdir -p $(OBJDIR) +else + $(SILENT) mkdir $(subst /,\\,$(OBJDIR)) +endif + $(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" +$(OBJDIR)/obj-loader.o: obj-loader.cc + @echo $(notdir $<) +ifeq (posix,$(SHELLTYPE)) + $(SILENT) mkdir -p $(OBJDIR) +else + $(SILENT) mkdir $(subst /,\\,$(OBJDIR)) +endif + $(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" +$(OBJDIR)/render-config.o: render-config.cc + @echo $(notdir $<) +ifeq (posix,$(SHELLTYPE)) + $(SILENT) mkdir -p $(OBJDIR) +else + $(SILENT) mkdir $(subst /,\\,$(OBJDIR)) +endif + $(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" +$(OBJDIR)/render.o: render.cc + @echo $(notdir $<) +ifeq (posix,$(SHELLTYPE)) + $(SILENT) mkdir -p $(OBJDIR) +else + $(SILENT) mkdir $(subst /,\\,$(OBJDIR)) +endif + $(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" + +-include $(OBJECTS:%.o=%.d) +ifneq (,$(PCH)) + -include $(OBJDIR)/$(notdir $(PCH)).d +endif \ No newline at end of file