/* 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 #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/gtc/matrix_transform.hpp" #include "glm/gtc/quaternion.hpp" #include "glm/gtc/type_ptr.hpp" #include "glm/mat4x4.hpp" #if defined(_MSC_VER) #pragma warning(pop) #endif #include "gltf-loader.h" #include "nanosg.h" #include "obj-loader.h" #include "render-config.h" #include "render.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()) { gSceneDirty = true; // RequestRender(); } 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)); if (button == 0 && !state) gMouseLeftDown = false; // prevent sticky trackball after using gizmo ImGuiIO &io = ImGui::GetIO(); if (io.WantCaptureMouse || io.WantCaptureKeyboard) { if (button == 0 && !state) { if (ImGuizmo::IsUsing()) { gSceneDirty = true; RequestRender(); } } } else { // 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 { } } } } 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 pseudoColor(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] = pseudoColor(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; // Material pushed as first material on the list materials.push_back(default_material); if (!gRenderConfig.obj_filename.empty()) { 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; } } if (!gRenderConfig.gltf_filename.empty()) { std::cout << "Found gltf file : " << gRenderConfig.gltf_filename << "\n"; bool ret = LoadGLTF(gRenderConfig.gltf_filename, gRenderConfig.scene_scale, &meshes, &materials, &textures); if (!ret) { std::cerr << "Failed to load glTF file [ " << gRenderConfig.gltf_filename << " ]" << std::endl; return -1; } } if (textures.size() > 0) { materials[0].diffuse_texid = 0; } 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]); // case where the name of a mesh isn't defined in the loaded file if (gAsset.meshes[n].name.empty()) { std::string generatedName = "unnamed_" + std::to_string(n); gAsset.meshes[n].name = generatedName; meshes[n].name = generatedName; } 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::CreateContext(); 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 pseudo 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_index = 0; { ImGui::Begin("Scene"); ImGui::ListBox("Node list", &node_selected_index, imgui_node_names.data(), imgui_node_names.size(), 16); auto node_selected = node_map.at(node_selected_index); node_matrix = glm::make_mat4(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_index]->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; }