1074 lines
32 KiB
C++
1074 lines
32 KiB
C++
|
/*
|
||
|
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 <sys/stat.h>
|
||
|
#include <sys/types.h>
|
||
|
#include <time.h>
|
||
|
#else
|
||
|
#include <fcntl.h>
|
||
|
#include <sys/mman.h>
|
||
|
#include <sys/stat.h>
|
||
|
#endif
|
||
|
|
||
|
#include <algorithm>
|
||
|
#include <cassert>
|
||
|
#include <cmath>
|
||
|
#include <cstdio>
|
||
|
#include <cstdlib>
|
||
|
#include <cstring>
|
||
|
#include <fstream>
|
||
|
#include <iostream>
|
||
|
#include <limits>
|
||
|
#include <map>
|
||
|
#include <sstream>
|
||
|
#include <string>
|
||
|
#include <vector>
|
||
|
|
||
|
#include <atomic> // C++11
|
||
|
#include <chrono> // C++11
|
||
|
#include <mutex> // C++11
|
||
|
#include <thread> // 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<float, example::Mesh<float> > gScene;
|
||
|
static example::Asset gAsset;
|
||
|
static std::vector<nanosg::Node<float, example::Mesh<float> > > gNodes;
|
||
|
|
||
|
std::atomic<bool> gRenderQuit;
|
||
|
std::atomic<bool> gRenderRefresh;
|
||
|
std::atomic<bool> gRenderCancel;
|
||
|
std::atomic<bool> gSceneDirty;
|
||
|
example::RenderConfig gRenderConfig;
|
||
|
std::mutex gMutex;
|
||
|
|
||
|
struct RenderLayer {
|
||
|
std::vector<float> displayRGBA; // Accumurated image.
|
||
|
std::vector<float> rgba;
|
||
|
std::vector<float> auxRGBA; // Auxiliary buffer
|
||
|
std::vector<int> sampleCounts; // Sample num counter for each pixel.
|
||
|
std::vector<float> normalRGBA; // For visualizing normal
|
||
|
std::vector<float> positionRGBA; // For visualizing position
|
||
|
std::vector<float> depthRGBA; // For visualizing depth
|
||
|
std::vector<float> texCoordRGBA; // For visualizing texcoord
|
||
|
std::vector<float> 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<std::mutex> guard(gMutex);
|
||
|
gRenderConfig.pass = 0;
|
||
|
}
|
||
|
|
||
|
gRenderRefresh = true;
|
||
|
gRenderCancel = true;
|
||
|
}
|
||
|
|
||
|
void RenderThread() {
|
||
|
{
|
||
|
std::lock_guard<std::mutex> 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<std::mutex> 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<std::mutex> guard(gMutex);
|
||
|
|
||
|
gRenderConfig.pass++;
|
||
|
}
|
||
|
|
||
|
auto endT = std::chrono::system_clock::now();
|
||
|
|
||
|
std::chrono::duration<double, std::milli> 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<int>(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<float>(gRenderConfig.width);
|
||
|
float h = static_cast<float>(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<int>(width);
|
||
|
gHeight = static_cast<int>(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<float> 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<float>(gRenderLayer.sampleCounts[i]);
|
||
|
buf[4 * i + 1] /= static_cast<float>(gRenderLayer.sampleCounts[i]);
|
||
|
buf[4 * i + 2] /= static_cast<float>(gRenderLayer.sampleCounts[i]);
|
||
|
buf[4 * i + 3] /= static_cast<float>(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<float>::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<float> 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<const GLvoid*>(&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<float> *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<float, example::Mesh<float> > &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<float, example::Mesh<float> > &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<nanosg::Node<float, example::Mesh<float> > > &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<std::string> *display_names,
|
||
|
std::vector<std::string> *names,
|
||
|
const nanosg::Node<float, example::Mesh<float> > &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<example::Mesh<float> > meshes;
|
||
|
std::vector<example::Material> materials;
|
||
|
std::vector<example::Texture> 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<float, example::Mesh<float> > 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<const char *> imgui_node_names;
|
||
|
std::vector<std::string> display_node_names;
|
||
|
std::vector<std::string> node_names;
|
||
|
std::map<int, nanosg::Node<float, example::Mesh<float> > *> 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<float, example::Mesh<float> > *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<void *>(static_cast<intptr_t>(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;
|
||
|
}
|