protot/3rdparty/tinygltf/examples/raytrace/main.cc

1074 lines
32 KiB
C++
Raw Permalink Normal View History

2018-03-19 23:00:51 +01:00
/*
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;
}