1796 lines
55 KiB
C++
1796 lines
55 KiB
C++
#include <algorithm>
|
|
#include <atomic>
|
|
#include <cassert>
|
|
#include <chrono>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <iostream>
|
|
#include <map>
|
|
#include <mutex>
|
|
#include <thread>
|
|
#include <vector>
|
|
|
|
#if defined(_MSC_VER)
|
|
#include <direct.h> // _getcwd
|
|
#endif
|
|
|
|
// GL
|
|
//
|
|
// glad must be included before glfw3.h
|
|
// TODO: Use imgui's imgui_impl_opengl3_loader.h
|
|
#if defined(_WIN32)
|
|
#if !defined(NOMINMAX)
|
|
#define NOMINMAX
|
|
#endif
|
|
#include <Windows.h>
|
|
#endif
|
|
|
|
#include "glad/glad.h"
|
|
//
|
|
#include <GLFW/glfw3.h>
|
|
|
|
// GUI common
|
|
#include "../common/imgui/imgui.h"
|
|
#include "../common/imgui/imgui_impl_glfw.h"
|
|
#include "../common/imgui/imgui_impl_opengl3.h"
|
|
#include "../common/trackball.h"
|
|
#include "../common/viewport_camera.hh"
|
|
|
|
// TinyUSDZ
|
|
// include relative to openglviewer example cmake top dir for clangd lsp.
|
|
#include "external/linalg.h"
|
|
#include "io-util.hh"
|
|
#include "linear-algebra.hh"
|
|
#include "pprinter.hh" // import to_string(tinyusdz::value::***)
|
|
#include "tinyusdz.hh"
|
|
#include "tydra/render-data.hh"
|
|
#include "tydra/scene-access.hh"
|
|
#include "value-pprint.hh" // import to_string(tinyusdz::value::***)
|
|
|
|
// local
|
|
#include "shader.hh"
|
|
|
|
// variable name must match in shaders/***.vert
|
|
constexpr auto kAttribPoints = "input_position";
|
|
constexpr auto kAttribNormals = "input_normal";
|
|
constexpr auto kAttribTexCoordBase = "input_uv";
|
|
constexpr auto kAttribTexCoord0 = "input_uv";
|
|
constexpr auto kMaxTexCoords = 1; // TODO: multi texcoords
|
|
|
|
constexpr auto kUniformModelMatrix = "modelMatrix";
|
|
constexpr auto kUniformNormalMatrix = "normalMatrix";
|
|
constexpr auto kUniformMVPMatrix = "mvp";
|
|
|
|
constexpr auto kUniformDiffuseTex = "diffuseTex";
|
|
constexpr auto kUniformDiffuseTexTransform = "diffuseTexTransform";
|
|
constexpr auto kUniformDiffuseTexScaleAndBias =
|
|
"diffuseTexScaleAndBias"; // (sx, sy, bx, by)
|
|
|
|
constexpr auto kUniformNormaTex = "normalTex";
|
|
constexpr auto kUniformNormaTexTransform = "normalTexTransform";
|
|
constexpr auto kUniformNormalTexScaleAndBias =
|
|
"normalTexScaleAndBias"; // (sx, sy, bx, by)
|
|
|
|
constexpr auto kUniformOcclusionTex = "occlusionlTex";
|
|
constexpr auto kUniformOcclusionTexTransform = "occlusionlTexTransform";
|
|
constexpr auto kUniformOcclusionTexScaleAndBias =
|
|
"occlusionTexScaleAndBias"; // (sx, sy, bx, by)
|
|
|
|
// Embedded shaders
|
|
#include "shaders/no_skinning.vert_inc.hh"
|
|
#include "shaders/normals.frag_inc.hh"
|
|
#include "shaders/usdpreviewsurface.frag_inc.hh"
|
|
#include "shaders/world_fragment.frag_inc.hh"
|
|
|
|
#define CHECK_GL(tag) \
|
|
do { \
|
|
GLenum err = glGetError(); \
|
|
if (err != GL_NO_ERROR) { \
|
|
std::cerr << "[" << tag << "] " << __FILE__ << ":" << __LINE__ << ":" \
|
|
<< __func__ << " code " << std::to_string(int(err)) << "\n"; \
|
|
} \
|
|
} while (0)
|
|
|
|
struct GLTexParams {
|
|
// std::map<std::string, GLint> uniforms;
|
|
GLenum wrapS{GL_REPEAT};
|
|
GLenum wrapT{GL_REPEAT};
|
|
std::array<float, 4> borderCol{0.0f, 0.0f, 0.0f, 0.0f}; // transparent black
|
|
|
|
// Use 3x3 mat to support pivot transform.
|
|
tinyusdz::tydra::mat3 uv_transform{tinyusdz::tydra::mat3::identity()};
|
|
};
|
|
|
|
struct GLTexState {
|
|
GLTexParams texParams;
|
|
std::string sampler_name;
|
|
uint32_t slot_id{0};
|
|
GLuint tex_id; // glBindTexture id
|
|
GLint u_tex{-1}; // sampler glUniform location
|
|
|
|
GLint u_transform; // texcoord transform
|
|
};
|
|
|
|
template <typename T>
|
|
struct GLTexOrFactor {
|
|
GLTexOrFactor(const T &v) : factor(v) {}
|
|
|
|
GLTexState tex;
|
|
T factor;
|
|
GLint u_factor{-1};
|
|
};
|
|
|
|
template <typename T>
|
|
struct GLUniformFactor {
|
|
GLUniformFactor(const T &v) : factor(v) {}
|
|
|
|
T factor;
|
|
GLint u_factor{-1};
|
|
};
|
|
|
|
struct GLUsdPreviewSurfaceState {
|
|
static constexpr auto kDiffuseColor = "diffuseColor";
|
|
static constexpr auto kEmissiveColor = "emissiveColor";
|
|
static constexpr auto kSpecularColor = "specularColor";
|
|
static constexpr auto kUseSpecularWorkflow = "useSpecularWorkflow";
|
|
static constexpr auto kMetallic = "metallic";
|
|
static constexpr auto kRoughness = "roughness";
|
|
static constexpr auto kClearcoat = "clearcoat";
|
|
static constexpr auto kClearcoatRoughness = "clearcoatRoughness";
|
|
static constexpr auto kOpacity = "opacity";
|
|
static constexpr auto kOpacityThreshold = "opacityThreshold";
|
|
static constexpr auto kIor = "ior";
|
|
static constexpr auto kNormal = "normal";
|
|
static constexpr auto kOcclusion = "occlusion";
|
|
|
|
GLTexOrFactor<tinyusdz::tydra::vec3> diffuseColor{{0.18f, 0.18f, 0.18f}};
|
|
GLTexOrFactor<tinyusdz::tydra::vec3> emissiveColor{{0.0f, 0.0f, 0.0f}};
|
|
|
|
GLUniformFactor<int> useSpecularWorkflow{0}; // non-texturable
|
|
|
|
GLTexOrFactor<tinyusdz::tydra::vec3> specularColor{
|
|
{0.0f, 0.0f, 0.0f}}; // useSpecularWorkflow = 1
|
|
GLTexOrFactor<float> metallic{0.0f}; // useSpecularWorkflow = 0
|
|
|
|
GLTexOrFactor<float> roughness{0.5f};
|
|
GLTexOrFactor<float> clearcoat{0.0f};
|
|
GLTexOrFactor<float> clearcoatRoughness{0.01f};
|
|
GLTexOrFactor<float> opacity{1.0f};
|
|
GLTexOrFactor<float> opacityThreshold{0.0f};
|
|
|
|
GLTexOrFactor<float> ior{1.5f};
|
|
|
|
GLTexOrFactor<tinyusdz::tydra::vec3> normal{
|
|
{0.0f, 0.0f, 1.0f}}; // normal map
|
|
|
|
// No displacement mapping on OpenGL
|
|
// GLTexOrFactor<float> displacement{0.0f};
|
|
|
|
GLTexOrFactor<float> occlusion{1.0f};
|
|
};
|
|
|
|
template <typename T>
|
|
bool SetupGLUsdPreviewSurfaceParam(const GLuint prog_id,
|
|
const tinyusdz::tydra::RenderScene &scene,
|
|
const std::string &base_shadername,
|
|
const tinyusdz::tydra::ShaderParam<T> &s,
|
|
|
|
GLTexOrFactor<T> &dst) {
|
|
if (s.is_texture()) {
|
|
{
|
|
std::string u_name = base_shadername + "Tex";
|
|
GLint loc = glGetUniformLocation(prog_id, u_name.c_str());
|
|
dst.tex.u_tex = loc;
|
|
}
|
|
|
|
{
|
|
std::string u_name = base_shadername + "TexTransform";
|
|
GLint loc = glGetUniformLocation(prog_id, u_name.c_str());
|
|
dst.tex.u_transform = loc;
|
|
if (s.textureId < 0 || s.textureId >= scene.textures.size()) {
|
|
std::cerr << "Invalid txtureId for " << base_shadername + "\n";
|
|
} else {
|
|
const tinyusdz::tydra::UVTexture &uvtex =
|
|
scene.textures[size_t(s.textureId)];
|
|
dst.tex.texParams.uv_transform = uvtex.transform;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
GLint loc = glGetUniformLocation(prog_id, base_shadername.c_str());
|
|
if (loc < 0) {
|
|
std::cerr << base_shadername << " uniform not found in the shader.\n";
|
|
}
|
|
dst.u_factor = loc;
|
|
dst.factor = s.value;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ReloadShader(GLuint prog_id, const std::string &vert_filepath,
|
|
const std::string &frag_filepath) {
|
|
std::string vert_str;
|
|
std::string frag_str;
|
|
|
|
if (vert_filepath.size() && tinyusdz::io::FileExists(vert_filepath)) {
|
|
std::vector<uint8_t> bytes;
|
|
std::string err;
|
|
if (!tinyusdz::io::ReadWholeFile(&bytes, &err, vert_filepath)) {
|
|
std::cerr << "Read vertg shader failed: " << err << "\n";
|
|
return false;
|
|
}
|
|
|
|
vert_str =
|
|
std::string(reinterpret_cast<char *>(bytes.data()), bytes.size());
|
|
|
|
std::cout << "VERT:\n" << vert_str << "\n";
|
|
}
|
|
|
|
if (frag_filepath.size() && tinyusdz::io::FileExists(frag_filepath)) {
|
|
std::vector<uint8_t> bytes;
|
|
std::string err;
|
|
if (!tinyusdz::io::ReadWholeFile(&bytes, &err, frag_filepath)) {
|
|
std::cerr << "Read frag shader failed: " << err << "\n";
|
|
return false;
|
|
}
|
|
|
|
frag_str =
|
|
std::string(reinterpret_cast<char *>(bytes.data()), bytes.size());
|
|
|
|
std::cout << "FRAG:\n" << frag_str << "\n";
|
|
}
|
|
|
|
// TODO
|
|
return true;
|
|
}
|
|
|
|
bool SetupGLUsdPreviewSurface(GLuint prog_id,
|
|
tinyusdz::tydra::RenderScene &scene,
|
|
tinyusdz::tydra::RenderMaterial &m,
|
|
GLUsdPreviewSurfaceState &dst) {
|
|
const auto surfaceShader = m.surfaceShader;
|
|
|
|
if (!SetupGLUsdPreviewSurfaceParam(
|
|
prog_id, scene, GLUsdPreviewSurfaceState::kDiffuseColor,
|
|
surfaceShader.diffuseColor, dst.diffuseColor)) {
|
|
return false;
|
|
}
|
|
|
|
if (!SetupGLUsdPreviewSurfaceParam(
|
|
prog_id, scene, GLUsdPreviewSurfaceState::kEmissiveColor,
|
|
surfaceShader.emissiveColor, dst.emissiveColor)) {
|
|
return false;
|
|
}
|
|
|
|
if (!SetupGLUsdPreviewSurfaceParam(
|
|
prog_id, scene, GLUsdPreviewSurfaceState::kSpecularColor,
|
|
surfaceShader.specularColor, dst.specularColor)) {
|
|
return false;
|
|
}
|
|
|
|
if (!SetupGLUsdPreviewSurfaceParam(prog_id, scene,
|
|
GLUsdPreviewSurfaceState::kMetallic,
|
|
surfaceShader.metallic, dst.metallic)) {
|
|
return false;
|
|
}
|
|
|
|
if (!SetupGLUsdPreviewSurfaceParam(prog_id, scene,
|
|
GLUsdPreviewSurfaceState::kRoughness,
|
|
surfaceShader.roughness, dst.roughness)) {
|
|
return false;
|
|
}
|
|
|
|
if (!SetupGLUsdPreviewSurfaceParam(prog_id, scene,
|
|
GLUsdPreviewSurfaceState::kClearcoat,
|
|
surfaceShader.clearcoat, dst.clearcoat)) {
|
|
return false;
|
|
}
|
|
|
|
if (!SetupGLUsdPreviewSurfaceParam(
|
|
prog_id, scene, GLUsdPreviewSurfaceState::kClearcoatRoughness,
|
|
surfaceShader.clearcoatRoughness, dst.clearcoatRoughness)) {
|
|
return false;
|
|
}
|
|
|
|
if (!SetupGLUsdPreviewSurfaceParam(prog_id, scene,
|
|
GLUsdPreviewSurfaceState::kOpacity,
|
|
surfaceShader.opacity, dst.opacity)) {
|
|
return false;
|
|
}
|
|
|
|
if (!SetupGLUsdPreviewSurfaceParam(
|
|
prog_id, scene, GLUsdPreviewSurfaceState::kOpacityThreshold,
|
|
surfaceShader.opacityThreshold, dst.opacityThreshold)) {
|
|
return false;
|
|
}
|
|
|
|
if (!SetupGLUsdPreviewSurfaceParam(prog_id, scene,
|
|
GLUsdPreviewSurfaceState::kIor,
|
|
surfaceShader.ior, dst.ior)) {
|
|
return false;
|
|
}
|
|
|
|
if (!SetupGLUsdPreviewSurfaceParam(prog_id, scene,
|
|
GLUsdPreviewSurfaceState::kOcclusion,
|
|
surfaceShader.occlusion, dst.occlusion)) {
|
|
return false;
|
|
}
|
|
|
|
if (!SetupGLUsdPreviewSurfaceParam(prog_id, scene,
|
|
GLUsdPreviewSurfaceState::kNormal,
|
|
surfaceShader.normal, dst.normal)) {
|
|
return false;
|
|
}
|
|
|
|
{
|
|
GLint loc = glGetUniformLocation(
|
|
prog_id, GLUsdPreviewSurfaceState::kUseSpecularWorkflow);
|
|
if (loc < 0) {
|
|
std::cerr << GLUsdPreviewSurfaceState::kUseSpecularWorkflow
|
|
<< " uniform not found in the shader.\n";
|
|
}
|
|
dst.useSpecularWorkflow.factor =
|
|
surfaceShader.useSpecularWorkFlow ? 1.0f : 0.0f;
|
|
dst.useSpecularWorkflow.u_factor = loc;
|
|
}
|
|
|
|
// TODO: `displacement` param
|
|
|
|
return true;
|
|
}
|
|
|
|
struct GLVertexUniformState {
|
|
GLint u_model{-1};
|
|
GLint u_normal{-1};
|
|
GLint u_mvp{-1};
|
|
|
|
std::array<float, 16> modelMatrix[16];
|
|
std::array<float, 9> normalMatrix[9]; // 3x3 transpose(inverse(model * view))
|
|
std::array<float, 16> mvp[16]; // modeviewprojection
|
|
};
|
|
|
|
// TODO: Use handle_id for key
|
|
struct GLMeshState {
|
|
std::map<std::string, GLint> attribs;
|
|
std::vector<GLuint> diffuseTexHandles;
|
|
GLuint vertex_array_object{0}; // vertex array object
|
|
GLuint num_triangles{0}; // up to 4GB triangles
|
|
};
|
|
|
|
struct GLNodeState {
|
|
GLVertexUniformState gl_v_uniform_state;
|
|
GLMeshState gl_mesh_state;
|
|
|
|
GLTexState gl_tex_state;
|
|
};
|
|
|
|
struct GLProgramState {
|
|
// std::map<std::string, GLint> uniforms;
|
|
|
|
std::map<std::string, example::shader> shaders;
|
|
};
|
|
|
|
struct GLScene {
|
|
std::vector<GLNodeState> gl_nodes;
|
|
|
|
// scene bounding box
|
|
std::array<float, 3> bmin;
|
|
std::array<float, 3> bmax;
|
|
};
|
|
|
|
struct GUIContext {
|
|
enum AOV {
|
|
AOV_COLOR = 0,
|
|
AOV_NORMAL,
|
|
AOV_POSITION,
|
|
AOV_DEPTH,
|
|
AOV_TEXCOORD,
|
|
AOV_VARYCOORD,
|
|
AOV_VERTEXCOLOR
|
|
};
|
|
AOV aov{AOV_COLOR};
|
|
|
|
int width = 1024;
|
|
int height = 768;
|
|
|
|
int mouse_x = -1;
|
|
int mouse_y = -1;
|
|
|
|
bool mouse_left_down = false;
|
|
bool shift_pressed = false;
|
|
bool ctrl_pressed = false;
|
|
bool tab_pressed = false;
|
|
|
|
float curr_quat[4] = {0.0f, 0.0f, 0.0f, 1.0f};
|
|
float prev_quat[4] = {0.0f, 0.0f, 0.0f, 1.0f};
|
|
|
|
float xrotate = 0.0f; // in degree
|
|
float yrotate = 0.0f; // in degree
|
|
|
|
float fov = 45.0f; // in degree
|
|
float znear = 0.01f;
|
|
float zfar = 1000.0f;
|
|
|
|
std::array<float, 3> eye = {0.0f, 0.5f, -5.0f};
|
|
std::array<float, 3> lookat = {0.0f, 0.0f, 0.0f};
|
|
std::array<float, 3> up = {0.0f, 1.0f, 0.0f};
|
|
|
|
example::Camera camera;
|
|
|
|
GLUsdPreviewSurfaceState *selected_surfaceShader{nullptr};
|
|
|
|
std::vector<GLUsdPreviewSurfaceState> surfaceShaders;
|
|
|
|
// for ImGui
|
|
std::vector<std::string> surfaceShaderNames;
|
|
std::string selected_surfaceShaderName;
|
|
|
|
std::string usd_filepath;
|
|
|
|
std::string converter_info;
|
|
std::string converter_warn;
|
|
|
|
std::string vert_filename{"../shaders/no_skinning.vert"};
|
|
std::string frag_filename{"../shaders/usdpreviewsurface.frag"};
|
|
};
|
|
|
|
GUIContext gCtx;
|
|
|
|
//
|
|
// Combo with std::vector<std::string>
|
|
//
|
|
static bool ImGuiComboUI(const std::string &caption, std::string ¤t_item,
|
|
const std::vector<std::string> &items) {
|
|
bool changed = false;
|
|
|
|
if (ImGui::BeginCombo(caption.c_str(), current_item.c_str())) {
|
|
for (int n = 0; n < items.size(); n++) {
|
|
bool is_selected = (current_item == items[n]);
|
|
if (ImGui::Selectable(items[n].c_str(), is_selected)) {
|
|
current_item = items[n];
|
|
changed = true;
|
|
}
|
|
if (is_selected) {
|
|
// Set the initial focus when opening the combo (scrolling + for
|
|
// keyboard navigation support in the upcoming navigation branch)
|
|
ImGui::SetItemDefaultFocus();
|
|
}
|
|
}
|
|
ImGui::EndCombo();
|
|
}
|
|
|
|
return changed;
|
|
}
|
|
|
|
static void MaterialUI()
|
|
{
|
|
ImGui::Begin("Material");
|
|
|
|
ImGuiComboUI("surfaceShader", gCtx.selected_surfaceShaderName, gCtx.surfaceShaderNames);
|
|
|
|
ImGui::End();
|
|
}
|
|
|
|
static void UsdPreviewSurfaceParamUI(
|
|
GLUsdPreviewSurfaceState &state)
|
|
{
|
|
bool changed = false;
|
|
|
|
ImGui::Begin("Shader param");
|
|
|
|
changed |= ImGui::ColorEdit3("diffuseColor", &state.diffuseColor.factor[0]);
|
|
changed |= ImGui::ColorEdit3("emissiveColor", &state.emissiveColor.factor[0]);
|
|
bool specWorkflow = state.useSpecularWorkflow.factor > 0 ? true : false;
|
|
changed |= ImGui::Checkbox("useSpecularWorkflow", &specWorkflow);
|
|
state.useSpecularWorkflow.factor = specWorkflow;
|
|
|
|
if (specWorkflow) {
|
|
changed |= ImGui::ColorEdit3("specularColor", &state.specularColor.factor[0]);
|
|
} else {
|
|
changed |= ImGui::SliderFloat("metallic", &state.metallic.factor, 0.0f, 1.0f);
|
|
}
|
|
|
|
changed |= ImGui::SliderFloat("clearcoat", &state.clearcoat.factor, 0.0f, 1.0f);
|
|
changed |= ImGui::SliderFloat("clearcoatRoughness", &state.clearcoatRoughness.factor, 0.0f, 1.0f);
|
|
|
|
changed |= ImGui::SliderFloat("opacity", &state.opacity.factor, 0.0f, 1.0f);
|
|
changed |= ImGui::SliderFloat("opacityThreshold", &state.opacityThreshold.factor, 0.0f, 1.0f);
|
|
|
|
changed |= ImGui::SliderFloat("ior", &state.ior.factor, 0.0f, 6.0f);
|
|
|
|
changed |= ImGui::SliderFloat("occlusion", &state.occlusion.factor, 0.0f, 1.0f);
|
|
|
|
|
|
ImGui::End();
|
|
}
|
|
|
|
// --- glfw ----------------------------------------------------
|
|
|
|
static void error_callback(int error, const char *description) {
|
|
std::cerr << "GLFW Error : " << error << ", " << description << std::endl;
|
|
}
|
|
|
|
static void key_callback(GLFWwindow *window, int key, int scancode, int action,
|
|
int mods) {
|
|
(void)scancode;
|
|
|
|
ImGuiIO &io = ImGui::GetIO();
|
|
if (io.WantCaptureKeyboard) {
|
|
return;
|
|
}
|
|
|
|
if ((key == GLFW_KEY_LEFT_SHIFT) || (key == GLFW_KEY_RIGHT_SHIFT)) {
|
|
auto *param =
|
|
reinterpret_cast<GUIContext *>(glfwGetWindowUserPointer(window));
|
|
|
|
param->shift_pressed = (action == GLFW_PRESS);
|
|
}
|
|
|
|
if ((key == GLFW_KEY_LEFT_CONTROL) || (key == GLFW_KEY_RIGHT_CONTROL)) {
|
|
auto *param =
|
|
reinterpret_cast<GUIContext *>(glfwGetWindowUserPointer(window));
|
|
|
|
param->ctrl_pressed = (action == GLFW_PRESS);
|
|
}
|
|
|
|
// ctrl-q
|
|
if ((key == GLFW_KEY_Q) && (action == GLFW_PRESS) &&
|
|
(mods & GLFW_MOD_CONTROL)) {
|
|
glfwSetWindowShouldClose(window, GLFW_TRUE);
|
|
}
|
|
|
|
// esc
|
|
if ((key == GLFW_KEY_ESCAPE) && (action == GLFW_PRESS)) {
|
|
glfwSetWindowShouldClose(window, GLFW_TRUE);
|
|
}
|
|
}
|
|
|
|
static void mouse_move_callback(GLFWwindow *window, double x, double y) {
|
|
auto param = reinterpret_cast<GUIContext *>(glfwGetWindowUserPointer(window));
|
|
assert(param);
|
|
|
|
if (param->mouse_left_down) {
|
|
float w = static_cast<float>(param->width);
|
|
float h = static_cast<float>(param->height);
|
|
|
|
float x_offset = param->width - w;
|
|
float y_offset = param->height - h;
|
|
|
|
if (param->ctrl_pressed) {
|
|
const float dolly_scale = 0.1f;
|
|
param->eye[2] += dolly_scale * (param->mouse_y - static_cast<float>(y));
|
|
param->lookat[2] +=
|
|
dolly_scale * (param->mouse_y - static_cast<float>(y));
|
|
} else if (param->shift_pressed) {
|
|
const float trans_scale = 0.02f;
|
|
param->eye[0] += trans_scale * (param->mouse_x - static_cast<float>(x));
|
|
param->eye[1] -= trans_scale * (param->mouse_y - static_cast<float>(y));
|
|
param->lookat[0] +=
|
|
trans_scale * (param->mouse_x - static_cast<float>(x));
|
|
param->lookat[1] -=
|
|
trans_scale * (param->mouse_y - static_cast<float>(y));
|
|
|
|
} else {
|
|
#if 0
|
|
// Adjust y.
|
|
trackball(param->prev_quat,
|
|
(2.f * (param->mouse_x - x_offset) - w) / static_cast<float>(w),
|
|
(h - 2.f * (param->mouse_y - y_offset)) / static_cast<float>(h),
|
|
(2.f * (static_cast<float>(x) - x_offset) - w) /
|
|
static_cast<float>(w),
|
|
(h - 2.f * (static_cast<float>(y) - y_offset)) /
|
|
static_cast<float>(h));
|
|
add_quats(param->prev_quat, param->curr_quat, param->curr_quat);
|
|
#else
|
|
const float rotation_amp = 1.0f;
|
|
param->xrotate += rotation_amp * (param->mouse_y - static_cast<float>(y));
|
|
param->yrotate += rotation_amp * (param->mouse_x - static_cast<float>(x));
|
|
|
|
// limit rotation around X axis.
|
|
if (param->xrotate < -89) {
|
|
param->xrotate = -89;
|
|
}
|
|
if (param->xrotate > 89) {
|
|
param->xrotate = 89;
|
|
}
|
|
|
|
#endif
|
|
}
|
|
}
|
|
|
|
param->mouse_x = static_cast<int>(x);
|
|
param->mouse_y = static_cast<int>(y);
|
|
}
|
|
|
|
static void mouse_button_callback(GLFWwindow *window, int button, int action,
|
|
int mods) {
|
|
ImGuiIO &io = ImGui::GetIO();
|
|
if (io.WantCaptureMouse || io.WantCaptureKeyboard) {
|
|
return;
|
|
}
|
|
|
|
auto param = reinterpret_cast<GUIContext *>(glfwGetWindowUserPointer(window));
|
|
assert(param);
|
|
|
|
// left button
|
|
if (button == 0) {
|
|
if (action) {
|
|
param->mouse_left_down = true;
|
|
trackball(param->prev_quat, 0.0f, 0.0f, 0.0f, 0.0f);
|
|
} else {
|
|
param->mouse_left_down = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void resize_callback(GLFWwindow *window, int width, int height) {
|
|
auto param = reinterpret_cast<GUIContext *>(glfwGetWindowUserPointer(window));
|
|
assert(param);
|
|
|
|
param->width = width;
|
|
param->height = height;
|
|
}
|
|
|
|
// ------------------------------------------------
|
|
|
|
namespace {
|
|
|
|
void SetupVertexUniforms(GLVertexUniformState &gl_state,
|
|
const tinyusdz::value::matrix4d &worldmatd,
|
|
const tinyusdz::value::matrix4f &viewproj) {
|
|
using namespace tinyusdz::value;
|
|
|
|
// implicitly casts matrix4d to matrix4f;
|
|
matrix4f worldmat = worldmatd;
|
|
|
|
matrix4d invtransmatd =
|
|
tinyusdz::inverse(tinyusdz::upper_left_3x3_only(worldmatd));
|
|
|
|
matrix3d invtransmat33d = tinyusdz::to_matrix3x3(invtransmatd);
|
|
matrix3f invtransmat33 = invtransmat33d;
|
|
|
|
memcpy(gl_state.modelMatrix, &worldmat.m[0][0], sizeof(float) * 16);
|
|
memcpy(gl_state.normalMatrix, &invtransmat33.m[0][0], sizeof(float) * 9);
|
|
|
|
// NOTE: USD uses pre-multiply matmul
|
|
matrix4f mvp = viewproj * worldmat;
|
|
|
|
// FIXME:
|
|
memcpy(gl_state.mvp, &mvp.m[0][0], sizeof(float) * 16);
|
|
}
|
|
|
|
void SetVertexUniforms(const GLVertexUniformState &gl_state) {
|
|
if (gl_state.u_model > -1) {
|
|
glUniformMatrix4fv(gl_state.u_model, 1, GL_FALSE,
|
|
gl_state.modelMatrix->data());
|
|
CHECK_GL("UniformMatrix u_modelview");
|
|
}
|
|
|
|
if (gl_state.u_normal > -1) {
|
|
glUniformMatrix3fv(gl_state.u_normal, 1, GL_FALSE,
|
|
gl_state.normalMatrix->data());
|
|
CHECK_GL("UniformMatrix u_normal");
|
|
}
|
|
|
|
if (gl_state.u_mvp > -1) {
|
|
glUniformMatrix4fv(gl_state.u_mvp, 1, GL_FALSE, gl_state.mvp->data());
|
|
CHECK_GL("UniformMatrix u_mvp");
|
|
}
|
|
}
|
|
|
|
void SetTexUniforms(const GLuint prog_id, const GLTexState &gl_tex) {
|
|
glActiveTexture(GL_TEXTURE0 + gl_tex.slot_id);
|
|
glBindTexture(GL_TEXTURE_2D, gl_tex.tex_id);
|
|
|
|
GLint loc = glGetUniformLocation(prog_id, gl_tex.sampler_name.c_str());
|
|
if (loc > -1) {
|
|
glUniform1i(loc, gl_tex.slot_id);
|
|
}
|
|
CHECK_GL("glUniform1i u_modelview");
|
|
}
|
|
|
|
bool LoadShaders(GLProgramState &gl_state) {
|
|
// default = show normal vector as color.
|
|
std::string vert_str(reinterpret_cast<char *>(shaders_no_skinning_vert),
|
|
shaders_no_skinning_vert_len);
|
|
|
|
std::string frag_str(reinterpret_cast<char *>(shaders_usdpreviewsurface_frag),
|
|
shaders_usdpreviewsurface_frag_len);
|
|
|
|
example::shader default_shader("default", vert_str, frag_str);
|
|
|
|
gl_state.shaders["default"] = std::move(default_shader);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool SetupGLUniforms(GLuint prog_id, GLVertexUniformState &gl_v_uniform_state) {
|
|
GLint model_loc = glGetUniformLocation(prog_id, kUniformModelMatrix);
|
|
if (model_loc < 0) {
|
|
std::cerr << kUniformModelMatrix << " not found in the vertex shader.\n";
|
|
// return false;
|
|
} else {
|
|
gl_v_uniform_state.u_model = model_loc;
|
|
}
|
|
|
|
GLint norm_loc = glGetUniformLocation(prog_id, kUniformNormalMatrix);
|
|
if (norm_loc < 0) {
|
|
std::cerr << kUniformNormalMatrix << " not found in the vertex shader.\n";
|
|
// return false;
|
|
} else {
|
|
gl_v_uniform_state.u_normal = norm_loc;
|
|
}
|
|
|
|
GLint mvp_loc = glGetUniformLocation(prog_id, kUniformMVPMatrix);
|
|
if (mvp_loc < 0) {
|
|
std::cerr << kUniformMVPMatrix << " not found in the vertex shader.\n";
|
|
// return false;
|
|
} else {
|
|
gl_v_uniform_state.u_mvp = mvp_loc;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool SetupTexture(const tinyusdz::tydra::RenderScene &scene,
|
|
tinyusdz::tydra::UVTexture &tex) {
|
|
auto glwrapmode = [](const tinyusdz::tydra::UVTexture::WrapMode mode) {
|
|
if (mode == tinyusdz::tydra::UVTexture::WrapMode::CLAMP_TO_EDGE) {
|
|
return GL_CLAMP_TO_EDGE;
|
|
} else if (mode == tinyusdz::tydra::UVTexture::WrapMode::REPEAT) {
|
|
return GL_REPEAT;
|
|
} else if (mode == tinyusdz::tydra::UVTexture::WrapMode::MIRROR) {
|
|
return GL_MIRRORED_REPEAT;
|
|
} else if (mode == tinyusdz::tydra::UVTexture::WrapMode::CLAMP_TO_BORDER) {
|
|
return GL_CLAMP_TO_BORDER;
|
|
}
|
|
// Just in case: Fallback to REPEAT
|
|
return GL_REPEAT;
|
|
};
|
|
|
|
GLTexState texState;
|
|
GLTexParams texParams;
|
|
|
|
texParams.wrapS = glwrapmode(tex.wrapS);
|
|
texParams.wrapT = glwrapmode(tex.wrapT);
|
|
|
|
GLuint texid{0};
|
|
glGenTextures(1, &texid);
|
|
|
|
glBindTexture(GL_TEXTURE_2D, texid);
|
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
|
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, texParams.wrapS);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, texParams.wrapT);
|
|
|
|
// Transparent black For `black` wrap mode.
|
|
// https://github.com/PixarAnimationStudios/OpenUSD/commit/2cf6612b2b1d5a1a1031bc153867116c5963e605
|
|
texParams.borderCol = {0.0f, 0.0f, 0.0f, 0.0f};
|
|
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR,
|
|
&texParams.borderCol[0]);
|
|
CHECK_GL("texture_id[" << std::to_string(tex.texture_image_id)
|
|
<< "] glTexParameters");
|
|
|
|
texState.texParams = std::move(texParams);
|
|
|
|
int64_t image_id = tex.texture_image_id;
|
|
|
|
if (image_id >= 0) {
|
|
if (image_id < scene.images.size()) {
|
|
const tinyusdz::tydra::TextureImage &image =
|
|
scene.images[size_t(image_id)];
|
|
|
|
if ((image.width < 1) || (image.height < 1) || (image.channels < 1)) {
|
|
std::cerr << "Texture image is not loaded(texture file not found?).\n";
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
return false;
|
|
}
|
|
|
|
uint32_t bytesperpixel = 1;
|
|
|
|
GLenum format{GL_LUMINANCE};
|
|
if (image.channels == 1) {
|
|
format = GL_LUMINANCE;
|
|
bytesperpixel = 1;
|
|
} else if (image.channels == 2) {
|
|
format = GL_LUMINANCE_ALPHA;
|
|
bytesperpixel = 2;
|
|
} else if (image.channels == 3) {
|
|
format = GL_RGB;
|
|
bytesperpixel = 3;
|
|
} else if (image.channels == 4) {
|
|
format = GL_RGBA;
|
|
bytesperpixel = 4;
|
|
}
|
|
|
|
GLenum type{GL_BYTE};
|
|
if (image.texelComponentType == tinyusdz::tydra::ComponentType::UInt8) {
|
|
type = GL_BYTE;
|
|
bytesperpixel *= 1;
|
|
} else if (image.texelComponentType ==
|
|
tinyusdz::tydra::ComponentType::Half) {
|
|
type = GL_SHORT;
|
|
bytesperpixel *= 2;
|
|
} else if (image.texelComponentType ==
|
|
tinyusdz::tydra::ComponentType::UInt32) {
|
|
type = GL_UNSIGNED_INT;
|
|
bytesperpixel *= 4;
|
|
} else if (image.texelComponentType ==
|
|
tinyusdz::tydra::ComponentType::Float) {
|
|
type = GL_FLOAT;
|
|
bytesperpixel *= 4;
|
|
} else {
|
|
std::cout << "Unsupported texelComponentType: "
|
|
<< tinyusdz::tydra::to_string(image.texelComponentType)
|
|
<< "\n";
|
|
}
|
|
|
|
int64_t buffer_id = image.buffer_id;
|
|
if ((buffer_id >= 0) && (buffer_id < scene.buffers.size())) {
|
|
const tinyusdz::tydra::BufferData &buffer =
|
|
scene.buffers[size_t(buffer_id)];
|
|
|
|
// byte length check.
|
|
if (size_t(image.width) * size_t(image.height) * size_t(bytesperpixel) >
|
|
buffer.data.size()) {
|
|
std::cerr << "Insufficient texel data. : "
|
|
<< "width: " << image.width << ", height " << image.height
|
|
<< ", bytesperpixel " << bytesperpixel
|
|
<< ", requested bytes: "
|
|
<< size_t(image.width) * size_t(image.height) *
|
|
size_t(bytesperpixel)
|
|
<< ", buffer bytes: " << std::to_string(buffer.data.size())
|
|
<< "\n";
|
|
// continue anyway
|
|
} else {
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.width, image.height, 0,
|
|
format, type, buffer.data.data());
|
|
CHECK_GL("texture_id[" << std::to_string(image_id)
|
|
<< "] glTexImage2D");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
tex.handle = texid;
|
|
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
|
|
return true;
|
|
}
|
|
|
|
static void BuildFacevaryingGeometricNormals(
|
|
const std::vector<tinyusdz::tydra::vec3> &points,
|
|
std::vector<tinyusdz::tydra::vec3> &geom_facevarying_normals);
|
|
|
|
static bool SetupMesh(const tinyusdz::Axis &stageUpAxis,
|
|
const tinyusdz::tydra::RenderMesh &mesh,
|
|
GLuint program_id,
|
|
GLMeshState &gl_state) // [out]
|
|
{
|
|
std::cout << "program_id " << program_id << "\n";
|
|
std::vector<uint32_t> indices;
|
|
|
|
if (mesh.faceVertexCounts.empty()) {
|
|
// assume all triangulaged faces.
|
|
if ((mesh.faceVertexIndices.size() % 3) != 0) {
|
|
std::cerr << "mesh <" << mesh.abs_name << "> faceVertexIndices.size "
|
|
<< std::to_string(mesh.faceVertexIndices.size())
|
|
<< " must be multiple of 3\n";
|
|
}
|
|
|
|
for (size_t f = 0; f < mesh.faceVertexIndices.size() / 3; f++) {
|
|
for (size_t c = 0; c < 3; c++) {
|
|
indices.push_back(mesh.faceVertexIndices[3 * f + c]);
|
|
}
|
|
}
|
|
} else {
|
|
size_t faceVertexIndexOffset = 0;
|
|
|
|
// Currently all faces must be triangle.
|
|
for (size_t f = 0; f < mesh.faceVertexCounts.size(); f++) {
|
|
if (mesh.faceVertexCounts[f] != 3) {
|
|
std::cerr << "mesh <" << mesh.abs_name
|
|
<< "> Non triangle face found at faceVertexCounts[" << f
|
|
<< "] (" << mesh.faceVertexCounts[f] << ")\n";
|
|
return false;
|
|
}
|
|
|
|
size_t fvCounts = mesh.faceVertexCounts[f];
|
|
for (size_t c = 0; c < fvCounts; c++) {
|
|
indices.push_back(mesh.faceVertexIndices[faceVertexIndexOffset + c]);
|
|
}
|
|
|
|
faceVertexIndexOffset += fvCounts;
|
|
}
|
|
}
|
|
|
|
glGenVertexArrays(1, &gl_state.vertex_array_object);
|
|
CHECK_GL(mesh.abs_name << "GenVertexArrays");
|
|
|
|
glBindVertexArray(gl_state.vertex_array_object);
|
|
CHECK_GL(mesh.abs_name << "BindVertexArray");
|
|
|
|
//
|
|
// Current settings
|
|
// - position
|
|
// - normals
|
|
// - texcoords0
|
|
//
|
|
// all vertex attribs are represented as facevarying data.
|
|
//
|
|
// - Static mesh(STATIC_DRAW) only
|
|
|
|
std::vector<tinyusdz::tydra::vec3> facevaryingVertices;
|
|
{ // position
|
|
|
|
// expand position to facevarying data.
|
|
// assume faces are all triangle.
|
|
gl_state.num_triangles = indices.size() / 3;
|
|
|
|
for (size_t i = 0; i < indices.size() / 3; i++) {
|
|
size_t vi0 = indices[3 * i + 0];
|
|
size_t vi1 = indices[3 * i + 1];
|
|
size_t vi2 = indices[3 * i + 2];
|
|
|
|
if (vi0 >= mesh.points.size()) {
|
|
std::cerr << "indices[" << (3 * i + 0) << "(" << vi0
|
|
<< ") exceeds mesh.points.size()(" << mesh.points.size()
|
|
<< ")\n";
|
|
return false;
|
|
}
|
|
|
|
if (vi1 >= mesh.points.size()) {
|
|
std::cerr << "indices[" << (3 * i + 1) << "(" << vi1
|
|
<< ") exceeds mesh.points.size()(" << mesh.points.size()
|
|
<< ")\n";
|
|
return false;
|
|
}
|
|
|
|
if (vi2 >= mesh.points.size()) {
|
|
std::cerr << "indices[" << (3 * i + 2) << "(" << vi2
|
|
<< ") exceeds mesh.points.size()(" << mesh.points.size()
|
|
<< ")\n";
|
|
return false;
|
|
}
|
|
|
|
if (stageUpAxis == tinyusdz::Axis::Z) {
|
|
tinyusdz::tydra::vec3 p0;
|
|
p0[0] = mesh.points[vi0][0];
|
|
p0[1] = mesh.points[vi0][2];
|
|
p0[2] = mesh.points[vi0][1];
|
|
|
|
tinyusdz::tydra::vec3 p1;
|
|
p1[0] = mesh.points[vi1][0];
|
|
p1[1] = mesh.points[vi1][2];
|
|
p1[2] = mesh.points[vi1][1];
|
|
|
|
tinyusdz::tydra::vec3 p2;
|
|
p2[0] = mesh.points[vi2][0];
|
|
p2[1] = mesh.points[vi2][2];
|
|
p2[2] = mesh.points[vi2][1];
|
|
|
|
facevaryingVertices.push_back(p0);
|
|
facevaryingVertices.push_back(p1);
|
|
facevaryingVertices.push_back(p2);
|
|
} else {
|
|
// TODO: upAxis X
|
|
facevaryingVertices.push_back(mesh.points[vi0]);
|
|
facevaryingVertices.push_back(mesh.points[vi1]);
|
|
facevaryingVertices.push_back(mesh.points[vi2]);
|
|
}
|
|
}
|
|
|
|
GLuint vb;
|
|
glGenBuffers(1, &vb);
|
|
glBindBuffer(GL_ARRAY_BUFFER, vb);
|
|
glBufferData(GL_ARRAY_BUFFER,
|
|
facevaryingVertices.size() * sizeof(tinyusdz::tydra::vec3),
|
|
facevaryingVertices.data(), GL_STATIC_DRAW);
|
|
CHECK_GL("Set facevaryingVertices buffer data");
|
|
|
|
GLint loc = glGetAttribLocation(program_id, kAttribPoints);
|
|
|
|
if (loc > -1) {
|
|
glEnableVertexAttribArray(loc);
|
|
glVertexAttribPointer(loc, 3, GL_FLOAT, GL_FALSE,
|
|
/* stride */ sizeof(GLfloat) * 3, 0);
|
|
CHECK_GL("VertexAttribPointer");
|
|
} else {
|
|
std::cerr << "vertex positions: " << kAttribPoints
|
|
<< " attribute not found in vertex shader.\n";
|
|
return false;
|
|
}
|
|
}
|
|
|
|
std::vector<tinyusdz::tydra::vec3> facevaryingNormals;
|
|
if (mesh.facevaryingNormals.size()) {
|
|
facevaryingNormals = mesh.facevaryingNormals;
|
|
} else {
|
|
BuildFacevaryingGeometricNormals(facevaryingVertices, facevaryingNormals);
|
|
}
|
|
|
|
if (facevaryingNormals.size()) { // normals
|
|
GLuint vb;
|
|
glGenBuffers(1, &vb);
|
|
glBindBuffer(GL_ARRAY_BUFFER, vb);
|
|
glBufferData(GL_ARRAY_BUFFER,
|
|
mesh.facevaryingNormals.size() * sizeof(tinyusdz::tydra::vec3),
|
|
mesh.facevaryingNormals.data(), GL_STATIC_DRAW);
|
|
CHECK_GL("Set facevaryingNormals buffer data");
|
|
|
|
GLint loc = glGetAttribLocation(program_id, kAttribNormals);
|
|
|
|
if (loc > -1) {
|
|
glEnableVertexAttribArray(loc);
|
|
glVertexAttribPointer(loc, 3, GL_FLOAT, GL_FALSE,
|
|
/* stride */ sizeof(GLfloat) * 3, 0);
|
|
CHECK_GL("VertexAttribPointer");
|
|
} else {
|
|
std::cerr
|
|
<< "vertex normals: " << kAttribNormals
|
|
<< " attribute not found in vertex shader. Shader does not use it?\n";
|
|
// may ok
|
|
}
|
|
}
|
|
|
|
// texcoords0 only
|
|
// TODO: multi texcoords
|
|
if (mesh.facevaryingTexcoords.size() == 1) {
|
|
for (const auto it : mesh.facevaryingTexcoords) {
|
|
uint32_t slot_id = it.first;
|
|
if (slot_id >= kMaxTexCoords) {
|
|
std::cerr << "Texcoord slot id " << slot_id
|
|
<< " must be less than kMaxTexCoords " << kMaxTexCoords
|
|
<< "\n";
|
|
return false;
|
|
}
|
|
|
|
GLuint vb;
|
|
glGenBuffers(1, &vb);
|
|
glBindBuffer(GL_ARRAY_BUFFER, vb);
|
|
glBufferData(GL_ARRAY_BUFFER,
|
|
it.second.size() * sizeof(tinyusdz::tydra::vec2),
|
|
it.second.data(), GL_STATIC_DRAW);
|
|
CHECK_GL("Set facevaryingTexcoord0 buffer data");
|
|
|
|
std::string texattr = kAttribTexCoordBase + std::to_string(slot_id);
|
|
GLint loc = glGetAttribLocation(program_id, texattr.c_str());
|
|
|
|
if (loc > -1) {
|
|
glEnableVertexAttribArray(loc);
|
|
glVertexAttribPointer(loc, 2, GL_FLOAT, GL_FALSE,
|
|
/* stride */ sizeof(GLfloat) * 2, 0);
|
|
CHECK_GL("VertexAttribPointer");
|
|
} else {
|
|
std::cerr << "Texture UV0: " << texattr
|
|
<< " attribute not found in vertex shader.\n";
|
|
// may OK
|
|
}
|
|
}
|
|
}
|
|
|
|
// We build facevarying vertex data, so no index buffers.
|
|
#if 0
|
|
// Build index buffer.
|
|
GLuint elementbuffer;
|
|
glGenBuffers(1, &elementbuffer);
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementbuffer);
|
|
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned int), &indices[0], GL_STATIC_DRAW);
|
|
CHECK_GL(mesh.abs_name << "ElementArrayBuffer");
|
|
#endif
|
|
|
|
glBindVertexArray(0);
|
|
CHECK_GL(mesh.abs_name << "UnBind VAO");
|
|
|
|
return true;
|
|
}
|
|
|
|
static void DrawMesh(const GLMeshState &gl_state) {
|
|
// Simply bind vertex array object and call glDrawArrays.
|
|
glBindVertexArray(gl_state.vertex_array_object);
|
|
glDrawArrays(GL_TRIANGLES, 0, gl_state.num_triangles * 3);
|
|
CHECK_GL("DrawArrays");
|
|
glBindVertexArray(0);
|
|
}
|
|
|
|
static void DrawNode(GLNodeState &gl_node,
|
|
const tinyusdz::value::matrix4f &viewproj) {
|
|
// FIXME
|
|
tinyusdz::value::matrix4d identm = tinyusdz::value::matrix4d::identity();
|
|
tinyusdz::value::double3 trans = {0.0, 0.0, 0.0};
|
|
tinyusdz::value::double3 rotate = {0.0, 0.0, 0.0};
|
|
tinyusdz::value::double3 scale = {1.0, 1.0, -1.0};
|
|
tinyusdz::value::matrix4d rotm =
|
|
tinyusdz::trs_angle_xyz(trans, rotate, scale);
|
|
|
|
tinyusdz::value::matrix4d modelm = rotm * identm;
|
|
|
|
SetupVertexUniforms(gl_node.gl_v_uniform_state, modelm, viewproj);
|
|
|
|
SetVertexUniforms(gl_node.gl_v_uniform_state);
|
|
// SetTexUniforms(gl_node.gl_tex_state);
|
|
|
|
DrawMesh(gl_node.gl_mesh_state);
|
|
}
|
|
|
|
static void ConvertMatrix(example::mat4 &m, tinyusdz::value::matrix4f &dst) {
|
|
for (size_t i = 0; i < 4; i++) {
|
|
for (size_t j = 0; j < 4; j++) {
|
|
dst.m[i][j] = m[i][j];
|
|
}
|
|
}
|
|
}
|
|
|
|
static void DrawScene(const example::shader &shader, GLScene &scene) {
|
|
//
|
|
// Use single shader for the scene
|
|
//
|
|
|
|
tinyusdz::value::matrix4f view;
|
|
ConvertMatrix(gCtx.camera.matrices.view, view);
|
|
|
|
tinyusdz::value::matrix4f proj;
|
|
ConvertMatrix(gCtx.camera.matrices.perspective, proj);
|
|
|
|
tinyusdz::value::matrix4f viewproj = view * proj;
|
|
|
|
// bind program
|
|
shader.use();
|
|
CHECK_GL("shader.use");
|
|
|
|
for (size_t i = 0; i < scene.gl_nodes.size(); i++) {
|
|
auto &gl_node = scene.gl_nodes[i];
|
|
DrawNode(gl_node, viewproj);
|
|
}
|
|
|
|
glUseProgram(0);
|
|
CHECK_GL("glUseProgram(0)");
|
|
}
|
|
|
|
static void ComputeBoundingBox(const tinyusdz::tydra::RenderMesh &mesh,
|
|
std::array<float, 3> &bmin,
|
|
std::array<float, 3> &bmax) {
|
|
bmin = {std::numeric_limits<float>::infinity(),
|
|
std::numeric_limits<float>::infinity(),
|
|
std::numeric_limits<float>::infinity()};
|
|
|
|
bmax = {-std::numeric_limits<float>::infinity(),
|
|
-std::numeric_limits<float>::infinity(),
|
|
-std::numeric_limits<float>::infinity()};
|
|
|
|
for (const auto &p : mesh.points) {
|
|
bmin[0] = (std::min)(bmin[0], p[0]);
|
|
bmin[1] = (std::min)(bmin[1], p[1]);
|
|
bmin[2] = (std::min)(bmin[2], p[2]);
|
|
|
|
bmax[0] = (std::max)(bmax[0], p[0]);
|
|
bmax[1] = (std::max)(bmax[1], p[1]);
|
|
bmax[2] = (std::max)(bmax[2], p[2]);
|
|
}
|
|
}
|
|
|
|
static bool ProcScene(const example::shader &gl_shader,
|
|
const tinyusdz::Stage &stage,
|
|
std::string &asset_search_path, GLScene *scene) {
|
|
tinyusdz::Axis upAxis{tinyusdz::Axis::Y};
|
|
if (stage.metas().upAxis.authored()) {
|
|
upAxis = stage.metas().upAxis.get_value();
|
|
}
|
|
std::cout << "upAxis " << tinyusdz::to_string(upAxis) << "\n";
|
|
|
|
//
|
|
// Stage to Renderable Scene
|
|
//
|
|
tinyusdz::tydra::RenderSceneConverter converter;
|
|
|
|
converter.set_search_paths({asset_search_path});
|
|
|
|
tinyusdz::tydra::RenderScene renderScene;
|
|
bool ret = converter.ConvertToRenderScene(stage, &renderScene);
|
|
if (converter.GetWarning().size()) {
|
|
std::cout << "ConvertToRenderScene WARN: " << converter.GetWarning()
|
|
<< "\n";
|
|
|
|
gCtx.converter_warn = converter.GetWarning();
|
|
}
|
|
|
|
if (!ret) {
|
|
std::cerr << "Failed to convert USD Stage to OpenGL-like data structure: "
|
|
<< converter.GetError() << "\n";
|
|
exit(-1);
|
|
}
|
|
|
|
std::cout << "# of meshes: " << renderScene.meshes.size() << "\n";
|
|
std::cout << "# of textures: " << renderScene.textures.size() << "\n";
|
|
|
|
gCtx.converter_info = converter.GetInfo();
|
|
|
|
std::array<float, 3> scene_bmin;
|
|
std::array<float, 3> scene_bmax;
|
|
|
|
scene_bmin = {std::numeric_limits<float>::infinity(),
|
|
std::numeric_limits<float>::infinity(),
|
|
std::numeric_limits<float>::infinity()};
|
|
|
|
scene_bmax = {-std::numeric_limits<float>::infinity(),
|
|
-std::numeric_limits<float>::infinity(),
|
|
-std::numeric_limits<float>::infinity()};
|
|
|
|
tinyusdz::value::matrix4f view;
|
|
ConvertMatrix(gCtx.camera.matrices.view, view);
|
|
|
|
tinyusdz::value::matrix4f proj;
|
|
ConvertMatrix(gCtx.camera.matrices.perspective, proj);
|
|
|
|
tinyusdz::value::matrix4f viewproj = proj * view;
|
|
|
|
std::map<std::string, GLTexState> gl_tex_state_map;
|
|
|
|
for (size_t i = 0; i < renderScene.textures.size(); i++) {
|
|
SetupTexture(renderScene, renderScene.textures[i]);
|
|
}
|
|
|
|
// TODO: Material
|
|
|
|
for (size_t i = 0; i < renderScene.meshes.size(); i++) {
|
|
std::array<float, 3> bmin;
|
|
std::array<float, 3> bmax;
|
|
|
|
ComputeBoundingBox(renderScene.meshes[i], bmin, bmax);
|
|
|
|
std::cout << "mesh[" << i << "].bmin " << bmin[0] << ", " << bmin[1] << ", "
|
|
<< bmin[2] << "\n";
|
|
std::cout << "mesh[" << i << "].bmax " << bmax[0] << ", " << bmax[1] << ", "
|
|
<< bmax[2] << "\n";
|
|
|
|
// TODO: accounnt for xform
|
|
scene_bmin[0] = (std::min)(bmin[0], scene_bmin[0]);
|
|
scene_bmin[1] = (std::min)(bmin[1], scene_bmin[1]);
|
|
scene_bmin[2] = (std::min)(bmin[2], scene_bmin[2]);
|
|
|
|
scene_bmax[0] = (std::max)(bmax[0], scene_bmax[0]);
|
|
scene_bmax[1] = (std::max)(bmax[1], scene_bmax[1]);
|
|
scene_bmax[2] = (std::max)(bmax[2], scene_bmax[2]);
|
|
|
|
GLMeshState gl_mesh;
|
|
if (!SetupMesh(upAxis, renderScene.meshes[i], gl_shader.get_program(),
|
|
gl_mesh)) {
|
|
std::cerr << "SetupMesh for mesh[" << i << "] failed.\n";
|
|
exit(-1);
|
|
}
|
|
|
|
GLNodeState gl_node;
|
|
gl_node.gl_mesh_state = gl_mesh;
|
|
|
|
// FIXME:
|
|
tinyusdz::value::double3 scene_center;
|
|
scene_center[0] = scene_bmin[0] + 0.5 * (scene_bmax[0] - scene_bmin[0]);
|
|
scene_center[1] = scene_bmin[1] + 0.5 * (scene_bmax[1] - scene_bmin[1]);
|
|
scene_center[2] = scene_bmin[2] + 0.5 * (scene_bmax[2] - scene_bmin[2]);
|
|
|
|
// FIXME
|
|
tinyusdz::value::matrix4d identm = tinyusdz::value::matrix4d::identity();
|
|
tinyusdz::value::double3 trans = {scene_center[0], scene_center[1],
|
|
scene_center[2]};
|
|
tinyusdz::value::double3 rotate = {0.0, 0.0, 0.0};
|
|
tinyusdz::value::double3 scale = {1.0, 1.0, -1.0};
|
|
tinyusdz::value::matrix4d rotm =
|
|
tinyusdz::trs_angle_xyz(trans, rotate, scale);
|
|
|
|
tinyusdz::value::matrix4d modelm = rotm * identm;
|
|
|
|
std::cout << "global matrix: " << identm << "\n";
|
|
SetupVertexUniforms(gl_node.gl_v_uniform_state, modelm, viewproj);
|
|
SetupGLUniforms(gl_shader.get_program(), gl_node.gl_v_uniform_state);
|
|
|
|
scene->gl_nodes.emplace_back(gl_node);
|
|
}
|
|
|
|
scene->bmin = scene_bmin;
|
|
scene->bmax = scene_bmax;
|
|
|
|
// TODO
|
|
return true;
|
|
}
|
|
|
|
static void vnormalize(float v[3]) {
|
|
float r;
|
|
|
|
r = std::sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
|
|
if (r == 0.0) return;
|
|
|
|
v[0] /= r;
|
|
v[1] /= r;
|
|
v[2] /= r;
|
|
}
|
|
|
|
static void vcross(float v1[3], float v2[3], float result[3]) {
|
|
result[0] = v1[1] * v2[2] - v1[2] * v2[1];
|
|
result[1] = v1[2] * v2[0] - v1[0] * v2[2];
|
|
result[2] = v1[0] * v2[1] - v1[1] * v2[0];
|
|
}
|
|
|
|
// based on mesa.
|
|
static void MygluLookAt(float eye[3], float center[3], float up[3]) {
|
|
float forward[3], side[3];
|
|
GLfloat m[4][4];
|
|
|
|
forward[0] = center[0] - eye[0];
|
|
forward[1] = center[1] - eye[1];
|
|
forward[2] = center[2] - eye[2];
|
|
|
|
vnormalize(forward);
|
|
|
|
/* Side = forward x up */
|
|
vcross(forward, up, side);
|
|
vnormalize(side);
|
|
|
|
/* Recompute up as: up = side x forward */
|
|
vcross(side, forward, up);
|
|
|
|
memset(m, 0, sizeof(GLfloat) * 16);
|
|
m[0][0] = 1.0f;
|
|
m[1][1] = 1.0f;
|
|
m[2][2] = 1.0f;
|
|
m[3][3] = 1.0f;
|
|
|
|
m[0][0] = side[0];
|
|
m[1][0] = side[1];
|
|
m[2][0] = side[2];
|
|
|
|
m[0][1] = up[0];
|
|
m[1][1] = up[1];
|
|
m[2][1] = up[2];
|
|
|
|
m[0][2] = -forward[0];
|
|
m[1][2] = -forward[1];
|
|
m[2][2] = -forward[2];
|
|
|
|
glMultMatrixf(&m[0][0]);
|
|
// TODO: Use glTranslated?
|
|
glTranslatef(-eye[0], -eye[1], -eye[2]);
|
|
}
|
|
|
|
std::string find_file(const std::string basefile, int max_parents = 8) {
|
|
if (max_parents > 16) {
|
|
return std::string();
|
|
}
|
|
|
|
std::string filepath = basefile;
|
|
|
|
for (size_t i = 0; i < max_parents; i++) {
|
|
if (tinyusdz::io::FileExists(filepath)) {
|
|
return filepath;
|
|
}
|
|
|
|
filepath = "../" + filepath;
|
|
}
|
|
|
|
return std::string();
|
|
}
|
|
|
|
static void BuildFacevaryingGeometricNormals(
|
|
const std::vector<tinyusdz::tydra::vec3> &points,
|
|
std::vector<tinyusdz::tydra::vec3> &geom_facevarying_normals) {
|
|
if ((points.size() % 3) != 0) {
|
|
return;
|
|
}
|
|
|
|
size_t npoints = points.size() / 3;
|
|
|
|
for (size_t i = 0; i < npoints; i++) {
|
|
tinyusdz::value::point3f p0;
|
|
p0.x = points[3 * i + 0][0];
|
|
p0.y = points[3 * i + 0][1];
|
|
p0.z = points[3 * i + 0][2];
|
|
tinyusdz::value::point3f p1;
|
|
p1.x = points[3 * i + 1][0];
|
|
p1.y = points[3 * i + 1][1];
|
|
p1.z = points[3 * i + 1][2];
|
|
tinyusdz::value::point3f p2;
|
|
p2.x = points[3 * i + 2][0];
|
|
p2.y = points[3 * i + 2][1];
|
|
p2.z = points[3 * i + 2][2];
|
|
|
|
tinyusdz::value::point3f p10 = p1 - p0;
|
|
tinyusdz::value::point3f p20 = p2 - p0;
|
|
|
|
// CCW
|
|
tinyusdz::value::point3f Ng = tinyusdz::vcross(p10, p20);
|
|
Ng = tinyusdz::vnormalize(Ng);
|
|
|
|
tinyusdz::tydra::vec3 nf;
|
|
nf[0] = Ng.x;
|
|
nf[1] = Ng.y;
|
|
nf[2] = Ng.z;
|
|
|
|
geom_facevarying_normals.push_back(nf);
|
|
geom_facevarying_normals.push_back(nf);
|
|
geom_facevarying_normals.push_back(nf);
|
|
}
|
|
}
|
|
|
|
static void ImMatrix4Display(const char *label, const float *m) {
|
|
static std::string labels[4];
|
|
|
|
labels[0] = std::string(label) + " m0";
|
|
labels[1] = std::string(label) + " m1";
|
|
labels[2] = std::string(label) + " m2";
|
|
labels[3] = std::string(label) + " m3";
|
|
|
|
ImGui::InputFloat4(labels[0].c_str(), const_cast<float *>(m), "%.3f",
|
|
ImGuiInputTextFlags_ReadOnly);
|
|
ImGui::InputFloat4(labels[1].c_str(), const_cast<float *>(m + 4), "%.3f",
|
|
ImGuiInputTextFlags_ReadOnly);
|
|
ImGui::InputFloat4(labels[2].c_str(), const_cast<float *>(m + 8), "%.3f",
|
|
ImGuiInputTextFlags_ReadOnly);
|
|
ImGui::InputFloat4(labels[3].c_str(), const_cast<float *>(m + 12), "%.3f",
|
|
ImGuiInputTextFlags_ReadOnly);
|
|
}
|
|
|
|
static void ImMatrix3Display(const char *label, const float *m) {
|
|
static std::string labels[3];
|
|
|
|
labels[0] = std::string(label) + " m0";
|
|
labels[1] = std::string(label) + " m1";
|
|
labels[2] = std::string(label) + " m2";
|
|
|
|
ImGui::InputFloat3(labels[0].c_str(), const_cast<float *>(m), "%.3f",
|
|
ImGuiInputTextFlags_ReadOnly);
|
|
ImGui::InputFloat3(labels[1].c_str(), const_cast<float *>(m + 3), "%.3f",
|
|
ImGuiInputTextFlags_ReadOnly);
|
|
ImGui::InputFloat3(labels[2].c_str(), const_cast<float *>(m + 6), "%.3f",
|
|
ImGuiInputTextFlags_ReadOnly);
|
|
}
|
|
|
|
} // namespace
|
|
|
|
int main(int argc, char **argv) {
|
|
// Setup window
|
|
glfwSetErrorCallback(error_callback);
|
|
if (!glfwInit()) {
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
// Decide GL+GLSL versions
|
|
#if defined(IMGUI_IMPL_OPENGL_ES2)
|
|
// GL ES 2.0 + GLSL 100
|
|
const char *glsl_version = "#version 100";
|
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
|
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
|
|
glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
|
|
#elif defined(__APPLE__)
|
|
// GL 3.2 + GLSL 150
|
|
const char *glsl_version = "#version 150";
|
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
|
|
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 3.2+ only
|
|
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // Required on Mac
|
|
#else
|
|
// GL 3.0 + GLSL 130
|
|
const char *glsl_version = "#version 130";
|
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
|
|
// glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 3.2+
|
|
// only glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // 3.0+ only
|
|
#endif
|
|
|
|
float highDPIscaleFactor = 1.0f;
|
|
|
|
float xscale=1.0f;
|
|
float yscale=1.0f;
|
|
#if defined(_WIN32) || defined(__linux__)
|
|
// if it's a HighDPI monitor, try to scale everything
|
|
GLFWmonitor *monitor = glfwGetPrimaryMonitor();
|
|
glfwGetMonitorContentScale(monitor, &xscale, &yscale);
|
|
std::cout << "monitor xscale, yscale = " << xscale << ", " << yscale << "\n";
|
|
if (xscale > 1 || yscale > 1) {
|
|
highDPIscaleFactor = xscale;
|
|
glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_TRUE);
|
|
}
|
|
#elif __APPLE__
|
|
glfwWindowHint(GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_FALSE);
|
|
#endif
|
|
|
|
#ifdef _DEBUG_OPENGL
|
|
glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE);
|
|
#endif
|
|
|
|
std::string filename = "models/suzanne.usdc";
|
|
// std::string filename = "models/texture-cat-plane.usdc";
|
|
// std::string filename = "models/texturedcube.usdc";
|
|
// std::string filename = "models/simple-plane.usdz";
|
|
#if defined(_MSC_VER)
|
|
std::cout << "cwd: " << _getcwd(nullptr, 0) << "\n";
|
|
#endif
|
|
|
|
if (argc > 1) {
|
|
filename = std::string(argv[1]);
|
|
}
|
|
|
|
std::string full_filepath = find_file(filename);
|
|
if (full_filepath.empty()) {
|
|
std::cerr << "cannot find or file not exists: " << filename << "\n";
|
|
}
|
|
|
|
std::cout << "Loading USD file " << full_filepath << "\n";
|
|
|
|
std::string warn;
|
|
std::string err;
|
|
tinyusdz::Stage stage;
|
|
|
|
bool ret = tinyusdz::LoadUSDFromFile(full_filepath, &stage, &warn, &err);
|
|
if (!warn.empty()) {
|
|
std::cerr << "WARN : " << warn << "\n";
|
|
return EXIT_FAILURE;
|
|
}
|
|
if (!err.empty()) {
|
|
std::cerr << "ERR : " << err << "\n";
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
std::string basedir = tinyusdz::io::GetBaseDir(full_filepath);
|
|
std::cout << "basedir = " << basedir << "\n";
|
|
|
|
gCtx.usd_filepath = full_filepath;
|
|
|
|
GLFWwindow *window{nullptr};
|
|
window = glfwCreateWindow(gCtx.width, gCtx.height, "Simple USDZ GL viewer",
|
|
nullptr, nullptr);
|
|
glfwMakeContextCurrent(window);
|
|
glfwSwapInterval(1); // vsync on
|
|
|
|
if (!gladLoadGLLoader(reinterpret_cast<GLADloadproc>(glfwGetProcAddress))) {
|
|
std::cerr << "Failed to load OpenGL functions with gladLoadGL\n";
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
std::cout << "OpenGL " << GLVersion.major << '.' << GLVersion.minor << '\n';
|
|
|
|
if (GLVersion.major < 2) {
|
|
std::cerr << "OpenGL 2. or later should be available." << std::endl;
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
#ifdef _DEBUG_OPENGL
|
|
glEnable(GL_DEBUG_OUTPUT);
|
|
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
|
|
glDebugMessageCallback(reinterpret_cast<GLDEBUGPROC>(glDebugOutput), nullptr);
|
|
glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, nullptr,
|
|
GL_TRUE);
|
|
#endif
|
|
|
|
GUIContext gui_ctx;
|
|
|
|
glfwSetWindowUserPointer(window, &gui_ctx);
|
|
glfwSetKeyCallback(window, key_callback);
|
|
glfwSetCursorPosCallback(window, mouse_move_callback);
|
|
glfwSetMouseButtonCallback(window, mouse_button_callback);
|
|
glfwSetFramebufferSizeCallback(window, resize_callback);
|
|
|
|
bool done = false;
|
|
|
|
ImGui::CreateContext();
|
|
|
|
ImGui::StyleColorsDark();
|
|
|
|
ImGui_ImplGlfw_InitForOpenGL(window, true);
|
|
ImGui_ImplOpenGL3_Init(glsl_version);
|
|
|
|
GLProgramState gl_progs;
|
|
if (!LoadShaders(gl_progs)) {
|
|
exit(-1);
|
|
}
|
|
|
|
GLScene gl_scene;
|
|
if (!ProcScene(gl_progs.shaders["default"], stage,
|
|
/* asset search path */ basedir, &gl_scene)) {
|
|
exit(-1);
|
|
}
|
|
|
|
std::cout << "scene bmin: " << gl_scene.bmin[0] << ", " << gl_scene.bmin[1]
|
|
<< ", " << gl_scene.bmin[2] << "\n";
|
|
std::cout << "scene bmax: " << gl_scene.bmax[0] << ", " << gl_scene.bmax[1]
|
|
<< ", " << gl_scene.bmax[2] << "\n";
|
|
|
|
int display_w, display_h;
|
|
ImVec4 clear_color = {0.1f, 0.18f, 0.3f, 1.0f};
|
|
|
|
{
|
|
float cxscale, cyscale;
|
|
glfwGetWindowContentScale(window, &cxscale, &cyscale);
|
|
std::cout << "xscale, yscale = " << cxscale << ", " << cyscale << "\n";
|
|
|
|
ImGuiIO &io = ImGui::GetIO();
|
|
|
|
io.DisplayFramebufferScale = {2.0f, 2.0f}; // HACK
|
|
|
|
ImFontConfig font_config;
|
|
font_config.SizePixels = 16.0f * xscale;
|
|
io.Fonts->AddFontDefault(&font_config);
|
|
}
|
|
|
|
std::array<float, 3> bmin = {-100.0f, -100.0f, -100.0f};
|
|
std::array<float, 3> bmax = {100.0f, 100.0f, 100.0f};
|
|
|
|
float maxExtent = 0.5f * (bmax[0] - bmin[0]);
|
|
if (maxExtent < 0.5f * (bmax[1] - bmin[1])) {
|
|
maxExtent = 0.5f * (bmax[1] - bmin[1]);
|
|
}
|
|
if (maxExtent < 0.5f * (bmax[2] - bmin[2])) {
|
|
maxExtent = 0.5f * (bmax[2] - bmin[2]);
|
|
}
|
|
|
|
float eye[3], lookat[3], up[3];
|
|
|
|
up[0] = 0.0f;
|
|
up[1] = 1.0f;
|
|
up[2] = 0.0f;
|
|
|
|
const example::shader &curr_shader = gl_progs.shaders["default"];
|
|
|
|
while (!done) {
|
|
glfwPollEvents();
|
|
ImGui_ImplOpenGL3_NewFrame();
|
|
ImGui_ImplGlfw_NewFrame();
|
|
ImGui::NewFrame();
|
|
|
|
ImGui::Begin("Info");
|
|
ImGui::Text("View control");
|
|
ImGui::Text("ctrl + left mouse");
|
|
ImGui::Text("shift + left mouse");
|
|
ImGui::Text("left mouse");
|
|
ImGui::End();
|
|
|
|
ImGui::Begin("Scene");
|
|
ImGui::InputText("filename", const_cast<char *>(gCtx.usd_filepath.c_str()),
|
|
gCtx.usd_filepath.size(), ImGuiInputTextFlags_ReadOnly);
|
|
ImGui::InputFloat3("scene bmin", &gl_scene.bmin[0], "%.3f",
|
|
ImGuiInputTextFlags_ReadOnly);
|
|
ImGui::InputFloat3("scene bmax", &gl_scene.bmax[0], "%.3f",
|
|
ImGuiInputTextFlags_ReadOnly);
|
|
ImGui::End();
|
|
|
|
ImGui::Begin("Material");
|
|
|
|
ImGui::End();
|
|
|
|
if (gCtx.selected_surfaceShader) {
|
|
UsdPreviewSurfaceParamUI(*gCtx.selected_surfaceShader);
|
|
}
|
|
|
|
ImGui::Begin("RenderScene converter log");
|
|
ImGui::InputTextMultiline("info",
|
|
const_cast<char *>(gCtx.converter_info.c_str()),
|
|
gCtx.converter_info.size(), ImVec2(800, 300),
|
|
ImGuiInputTextFlags_ReadOnly);
|
|
ImGui::InputTextMultiline("warn",
|
|
const_cast<char *>(gCtx.converter_warn.c_str()),
|
|
gCtx.converter_warn.size(), ImVec2(800, 300),
|
|
ImGuiInputTextFlags_ReadOnly);
|
|
ImGui::End();
|
|
|
|
// For developers only
|
|
ImGui::Begin("dev");
|
|
{
|
|
static bool compile_ok{true};
|
|
|
|
if (ImGui::Button("Reload shader")) {
|
|
compile_ok = ReloadShader(curr_shader.get_program(), gCtx.vert_filename,
|
|
gCtx.frag_filename);
|
|
}
|
|
|
|
if (compile_ok) {
|
|
ImGui::TextColored(ImVec4(0.3, 1.0, 0.4, 1.0), "Shader Compile OK");
|
|
} else {
|
|
ImGui::TextColored(ImVec4(1.0, 0.2, 0.1, 1.0), "Shader Compile Failed");
|
|
}
|
|
ImGui::End();
|
|
}
|
|
|
|
ImGui::Begin("Camera");
|
|
ImGui::SliderFloat("fov", &gCtx.fov, 0.0f, 178.0f);
|
|
ImGui::InputFloat("znear", &gCtx.znear);
|
|
ImGui::InputFloat("zfar", &gCtx.zfar);
|
|
ImGui::InputFloat3("eye", &gCtx.eye[0]);
|
|
ImGui::Separator();
|
|
ImGui::SliderFloat("xrot", &gCtx.xrotate, -89.9f, 89.9f);
|
|
ImGui::SliderFloat("yrot", &gCtx.yrotate, -180.0f, 180.0f);
|
|
ImGui::Separator();
|
|
if (ImGui::Button("Reset rotation")) {
|
|
gCtx.xrotate = 0.0f;
|
|
gCtx.yrotate = 0.0f;
|
|
}
|
|
ImGui::End();
|
|
|
|
glfwGetFramebufferSize(window, &display_w, &display_h);
|
|
glViewport(0, 0, display_w, display_h);
|
|
glClearColor(clear_color.x, clear_color.y, clear_color.z, clear_color.w);
|
|
glEnable(GL_DEPTH_TEST);
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
|
|
float aspect = float(display_w) / float(display_h);
|
|
// view
|
|
gCtx.camera.setPosition(gCtx.eye);
|
|
gCtx.camera.setRotation({gCtx.xrotate, gCtx.yrotate, 0.0f});
|
|
gCtx.camera.setPerspective(gCtx.fov, aspect, gCtx.znear, gCtx.zfar);
|
|
|
|
ImGui::Begin("View matrix");
|
|
{
|
|
tinyusdz::value::matrix4f view;
|
|
ConvertMatrix(gCtx.camera.matrices.view, view);
|
|
tinyusdz::value::matrix4f proj;
|
|
ConvertMatrix(gCtx.camera.matrices.perspective, proj);
|
|
|
|
// example::mat4 viewproj = linalg::mul(gCtx.camera.matrices.perspective,
|
|
// gCtx.camera.matrices.view);
|
|
tinyusdz::value::matrix4f viewproj = view * proj;
|
|
|
|
ImMatrix4Display("view", &gCtx.camera.matrices.view[0][0]);
|
|
ImGui::Separator();
|
|
ImMatrix4Display("perspective", &gCtx.camera.matrices.perspective[0][0]);
|
|
ImGui::Separator();
|
|
// ImMatrix4Display("viewproj", &viewproj.x[0]);
|
|
ImMatrix4Display("viewproj", &viewproj.m[0][0]);
|
|
|
|
ImGui::End();
|
|
}
|
|
|
|
DrawScene(curr_shader, gl_scene);
|
|
|
|
#if 0
|
|
// Draw scene
|
|
if ((scene.default_root_node >= 0) && (scene.default_root_node < scene.nodes.size())) {
|
|
DrawNode(scene, scene.nodes[scene.default_root_node]);
|
|
} else {
|
|
static bool printed = false;
|
|
if (!printed) {
|
|
std::cerr << "USD scene does not contain root node. or has invalid root node ID\n";
|
|
std::cerr << " # of nodes in the scene: " << scene.nodes.size() << "\n";
|
|
std::cerr << " scene.default_root_node: " << scene.default_root_node << "\n";
|
|
printed = true;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// Imgui
|
|
|
|
ImGui::Render();
|
|
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
|
|
|
|
// glFlush();
|
|
glfwSwapBuffers(window);
|
|
|
|
static int frameCount = 0;
|
|
static double currentTime = glfwGetTime();
|
|
static double previousTime = currentTime;
|
|
static char title[256];
|
|
|
|
frameCount++;
|
|
currentTime = glfwGetTime();
|
|
const auto deltaTime = currentTime - previousTime;
|
|
if (deltaTime >= 1.0) {
|
|
sprintf(title, "Simple GL USDC/USDA/USDZ viewer [%dFPS]", frameCount);
|
|
glfwSetWindowTitle(window, title);
|
|
frameCount = 0;
|
|
previousTime = currentTime;
|
|
}
|
|
|
|
done = glfwWindowShouldClose(window);
|
|
};
|
|
|
|
std::cout << "Close window\n";
|
|
|
|
ImGui_ImplOpenGL3_Shutdown();
|
|
ImGui_ImplGlfw_Shutdown();
|
|
ImGui::DestroyContext();
|
|
|
|
glfwDestroyWindow(window);
|
|
glfwTerminate();
|
|
|
|
return EXIT_SUCCESS;
|
|
}
|