OpenSubdiv/examples/glPtexViewer/glPtexViewer.cpp
Takahito Tejima 4a4322983f Osd drawing API refactoring.
Remove DrawRegistry from osd layer and put a simple shader caching
utility into examples/common. osd layer only provides patch shader
snippet and let client configure and compile the code. Clients also
maintain the lifetime of shader object, which is preferable for the
actual application integration.

update all examples to use the new scheme.
2015-05-13 17:35:46 -07:00

2227 lines
73 KiB
C++

//
// Copyright 2013 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#if defined(__APPLE__)
#if defined(OSD_USES_GLEW)
#include <GL/glew.h>
#else
#include <OpenGL/gl3.h>
#endif
#define GLFW_INCLUDE_GL3
#define GLFW_NO_GLU
#else
#include <stdlib.h>
#include <GL/glew.h>
#if defined(WIN32)
#include <GL/wglew.h>
#endif
#endif
#include <GLFW/glfw3.h>
GLFWwindow* g_window = 0;
GLFWmonitor* g_primary = 0;
#include <vector>
#include <sstream>
#include <iostream>
#include <fstream>
#include <string>
#include <utility>
#include <algorithm>
#include <osd/glDrawContext.h>
#include <far/error.h>
#include <osd/cpuEvaluator.h>
#include <osd/cpuGLVertexBuffer.h>
#ifdef OPENSUBDIV_HAS_OPENMP
#include <osd/ompEvaluator.h>
#endif
#ifdef OPENSUBDIV_HAS_TBB
#include <osd/tbbEvaluator.h>
#endif
#ifdef OPENSUBDIV_HAS_OPENCL
#include <osd/clEvaluator.h>
#include <osd/clGLVertexBuffer.h>
#include "../common/clDeviceContext.h"
CLDeviceContext g_clDeviceContext;
#endif
#ifdef OPENSUBDIV_HAS_CUDA
#include <osd/cudaEvaluator.h>
#include <osd/cudaGLVertexBuffer.h>
#include "../common/cudaDeviceContext.h"
CudaDeviceContext g_cudaDeviceContext;
#endif
#ifdef OPENSUBDIV_HAS_GLSL_TRANSFORM_FEEDBACK
#include <osd/glXFBEvaluator.h>
#include <osd/glVertexBuffer.h>
#endif
#ifdef OPENSUBDIV_HAS_GLSL_COMPUTE
#include <osd/glComputeEvaluator.h>
#include <osd/glVertexBuffer.h>
#endif
#include <osd/glMesh.h>
OpenSubdiv::Osd::GLMeshInterface *g_mesh;
#include "Ptexture.h"
#include "PtexUtils.h"
#include <common/vtr_utils.h>
#include "../common/stopwatch.h"
#include "../common/simple_math.h"
#include "../common/gl_hud.h"
#include "../common/hdr_reader.h"
#include "../common/glPtexMipmapTexture.h"
#include "../common/glShaderCache.h"
#include <osd/glslPatchShaderSource.h>
static const char *g_defaultShaderSource =
#if defined(GL_ARB_tessellation_shader) || defined(GL_VERSION_4_0)
#include "shader.gen.h"
#else
#include "shader_gl3.gen.h"
#endif
;
static const char *g_skyShaderSource =
#include "skyshader.gen.h"
;
static std::string g_shaderSource;
static const char *g_shaderFilename = NULL;
enum KernelType { kCPU = 0,
kOPENMP = 1,
kTBB = 2,
kCUDA = 3,
kCL = 4,
kGLSL = 5,
kGLSLCompute = 6 };
enum HudCheckBox { HUD_CB_ADAPTIVE,
HUD_CB_DISPLAY_OCCLUSION,
HUD_CB_DISPLAY_NORMALMAP,
HUD_CB_DISPLAY_SPECULAR,
HUD_CB_CAGE_EDGES,
HUD_CB_ANIMATE_VERTICES,
HUD_CB_VIEW_LOD,
HUD_CB_FRACTIONAL_SPACING,
HUD_CB_PATCH_CULL,
HUD_CB_IBL,
HUD_CB_BLOOM,
HUD_CB_SEAMLESS_MIPMAP,
HUD_CB_FREEZE };
enum HudRadioGroup { HUD_RB_KERNEL,
HUD_RB_LEVEL,
HUD_RB_SCHEME,
HUD_RB_WIRE,
HUD_RB_COLOR,
HUD_RB_DISPLACEMENT,
HUD_RB_NORMAL };
enum DisplayType { DISPLAY_WIRE,
DISPLAY_SHADED,
DISPLAY_WIRE_ON_SHADED };
enum ColorType { COLOR_NONE,
COLOR_PTEX_NEAREST,
COLOR_PTEX_HW_BILINEAR,
COLOR_PTEX_BILINEAR,
COLOR_PTEX_BIQUADRATIC,
COLOR_PATCHTYPE,
COLOR_PATCHCOORD,
COLOR_NORMAL };
enum DisplacementType { DISPLACEMENT_NONE,
DISPLACEMENT_HW_BILINEAR,
DISPLACEMENT_BILINEAR,
DISPLACEMENT_BIQUADRATIC };
enum NormalType { NORMAL_SURFACE,
NORMAL_FACET,
NORMAL_HW_SCREENSPACE,
NORMAL_SCREENSPACE,
NORMAL_BIQUADRATIC,
NORMAL_BIQUADRATIC_WG };
//-----------------------------------------------------------------------------
int g_frame = 0,
g_repeatCount = 0;
// GUI variables
int g_fullscreen = 0,
g_wire = DISPLAY_SHADED,
g_drawNormals = 0,
g_drawCageEdges = 0,
g_mbutton[3] = {0, 0, 0},
g_level = 1,
g_tessLevel = 2,
g_kernel = kCPU,
g_scheme = 0,
g_running = 1,
g_maxMipmapLevels = 10,
g_color = COLOR_PTEX_BILINEAR,
g_displacement = DISPLACEMENT_NONE,
g_normal = NORMAL_SURFACE;
float g_moveScale = 0.0f,
g_displacementScale = 1.0f,
g_mipmapBias = 0.0;
bool g_adaptive = false,
g_yup = false,
g_patchCull = true,
g_screenSpaceTess = true,
g_fractionalSpacing = true,
g_ibl = false,
g_bloom = false,
g_freeze = false;
GLuint g_constantUB = 0,
g_constantBinding = 0;
// ptex switch
bool g_occlusion = false,
g_specular = false;
bool g_seamless = true;
// camera
float g_rotate[2] = {0, 0},
g_dolly = 5,
g_pan[2] = {0, 0},
g_center[3] = {0, 0, 0},
g_size = 0;
int g_prev_x = 0,
g_prev_y = 0;
// viewport
int g_width = 1024,
g_height = 1024;
GLhud g_hud;
// performance
float g_cpuTime = 0;
float g_gpuTime = 0;
#define NUM_FPS_TIME_SAMPLES 6
float g_fpsTimeSamples[NUM_FPS_TIME_SAMPLES] = {0, 0, 0, 0, 0, 0};
int g_currentFpsTimeSample = 0;
Stopwatch g_fpsTimer;
float g_animTime = 0;
// geometry
std::vector<float> g_positions,
g_normals;
std::vector<std::vector<float> > g_animPositions;
GLuint g_queries[2] = {0, 0};
GLuint g_vao = 0;
GLuint g_cageEdgeVAO = 0;
GLuint g_skyVAO = 0;
GLuint g_edgeIndexBuffer = 0;
GLuint g_numCageEdges = 0;
GLuint g_diffuseEnvironmentMap = 0;
GLuint g_specularEnvironmentMap = 0;
//------------------------------------------------------------------------------
struct Sky {
int numIndices;
GLuint vertexBuffer;
GLuint elementBuffer;
GLuint mvpMatrix;
GLDrawConfig *drawConfig;
Sky() : numIndices(0), vertexBuffer(0), elementBuffer(0), mvpMatrix(0),
drawConfig(NULL) {}
~Sky() {
delete drawConfig;
}
bool BuildProgram(const char *source) {
if (drawConfig) delete drawConfig;
drawConfig = new GLDrawConfig("#version 410\n");
drawConfig->CompileAndAttachShader(GL_VERTEX_SHADER,
"#define SKY_VERTEX_SHADER\n" +
std::string(source));
drawConfig->CompileAndAttachShader(GL_FRAGMENT_SHADER,
"#define SKY_FRAGMENT_SHADER\n" +
std::string(source));
if (drawConfig->Link() == false) {
delete drawConfig;
drawConfig = NULL;
return false;
}
return true;
}
int GetProgram() const {
if (drawConfig) return drawConfig->GetProgram();
return 0;
}
} g_sky;
//------------------------------------------------------------------------------
GLPtexMipmapTexture * g_osdPTexImage = 0;
GLPtexMipmapTexture * g_osdPTexDisplacement = 0;
GLPtexMipmapTexture * g_osdPTexOcclusion = 0;
GLPtexMipmapTexture * g_osdPTexSpecular = 0;
const char * g_ptexColorFilename;
size_t g_ptexMemoryUsage = 0;
static void
checkGLErrors(std::string const & where = "") {
GLuint err;
while ((err = glGetError()) != GL_NO_ERROR) {
std::cerr << "GL error: "
<< (where.empty() ? "" : where + " ")
<< err << "\n";
}
}
//------------------------------------------------------------------------------
static void
calcNormals(OpenSubdiv::Far::TopologyRefiner * refiner,
std::vector<float> const & pos, std::vector<float> & result ) {
typedef OpenSubdiv::Far::ConstIndexArray IndexArray;
// calc normal vectors
int nverts = refiner->GetNumVertices(0),
nfaces = refiner->GetNumFaces(0);
for (int face = 0; face < nfaces; ++face) {
IndexArray fverts = refiner->GetFaceVertices(0, face);
float const * p0 = &pos[fverts[0]*3],
* p1 = &pos[fverts[1]*3],
* p2 = &pos[fverts[2]*3];
float n[3];
cross(n, p0, p1, p2);
for (int vert = 0; vert < fverts.size(); ++vert) {
int idx = fverts[vert] * 3;
result[idx ] += n[0];
result[idx+1] += n[1];
result[idx+2] += n[2];
}
}
for (int i = 0; i < nverts; ++i)
normalize(&result[i*3]);
}
//------------------------------------------------------------------------------
void
updateGeom() {
int nverts = (int)g_positions.size() / 3;
if (g_moveScale and g_adaptive and not g_animPositions.empty()) {
// baked animation only works with adaptive for now
// (since non-adaptive requires normals)
int nkey = (int)g_animPositions.size();
const float fps = 24.0f;
float p = fmodf(g_animTime * fps, (float)nkey);
int key = (int)p;
float b = p - key;
std::vector<float> vertex;
vertex.reserve(nverts*3);
for (int i = 0; i < nverts*3; ++i) {
float p0 = g_animPositions[key][i];
float p1 = g_animPositions[(key+1)%nkey][i];
vertex.push_back(p0*(1-b) + p1*b);
}
g_mesh->UpdateVertexBuffer(&vertex[0], 0, nverts);
} else {
std::vector<float> vertex;
vertex.reserve(nverts*6);
const float *p = &g_positions[0];
const float *n = &g_normals[0];
for (int i = 0; i < nverts; ++i) {
float move = g_size*0.005f*cosf(p[0]*100/g_size+g_frame*0.01f);
vertex.push_back(p[0]);
vertex.push_back(p[1]+g_moveScale*move);
vertex.push_back(p[2]);
p += 3;
if (g_adaptive == false) {
vertex.push_back(n[0]);
vertex.push_back(n[1]);
vertex.push_back(n[2]);
n += 3;
}
}
g_mesh->UpdateVertexBuffer(&vertex[0], 0, nverts);
}
Stopwatch s;
s.Start();
g_mesh->Refine();
s.Stop();
g_cpuTime = float(s.GetElapsed() * 1000.0f);
s.Start();
g_mesh->Synchronize();
s.Stop();
g_gpuTime = float(s.GetElapsed() * 1000.0f);
}
//-------------------------------------------------------------------------------
void
fitFrame() {
g_pan[0] = g_pan[1] = 0;
g_dolly = g_size;
}
//-------------------------------------------------------------------------------
Shape *
createPTexGeo(PtexTexture * r) {
PtexMetaData* meta = r->getMetaData();
if (meta->numKeys() < 3) {
return NULL;
}
float const * vp;
int const *vi, *vc;
int nvp, nvi, nvc;
meta->getValue("PtexFaceVertCounts", vc, nvc);
if (nvc == 0) {
return NULL;
}
meta->getValue("PtexVertPositions", vp, nvp);
if (nvp == 0) {
return NULL;
}
meta->getValue("PtexFaceVertIndices", vi, nvi);
if (nvi == 0) {
return NULL;
}
Shape * shape = new Shape;
shape->scheme = kCatmark;
shape->verts.resize(nvp);
for (int i=0; i<nvp; ++i) {
shape->verts[i] = vp[i];
}
shape->nvertsPerFace.resize(nvc);
for (int i=0; i<nvc; ++i) {
shape->nvertsPerFace[i] = vc[i];
}
shape->faceverts.resize(nvi);
for (int i=0; i<nvi; ++i) {
shape->faceverts[i] = vi[i];
}
// compute model bounding
float min[3] = {vp[0], vp[1], vp[2]};
float max[3] = {vp[0], vp[1], vp[2]};
for (int i = 0; i < nvp/3; ++i) {
for (int j = 0; j < 3; ++j) {
float v = vp[i*3+j];
min[j] = std::min(min[j], v);
max[j] = std::max(max[j], v);
}
}
for (int j = 0; j < 3; ++j) {
g_center[j] = (min[j] + max[j]) * 0.5f;
g_size += (max[j]-min[j])*(max[j]-min[j]);
}
g_size = sqrtf(g_size);
return shape;
}
//------------------------------------------------------------------------------
void
reshape(GLFWwindow *, int width, int height) {
g_width = width;
g_height = height;
int windowWidth = g_width, windowHeight = g_height;
// window size might not match framebuffer size on a high DPI display
glfwGetWindowSize(g_window, &windowWidth, &windowHeight);
g_hud.Rebuild(windowWidth, windowHeight, width, height);
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE)
assert(false);
glBindTexture(GL_TEXTURE_2D, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
checkGLErrors("Reshape");
}
void reshape() {
reshape(g_window, g_width, g_height);
}
void windowClose(GLFWwindow*) {
g_running = false;
}
//------------------------------------------------------------------------------
const char *getKernelName(int kernel) {
if (kernel == kCPU)
return "CPU";
else if (kernel == kOPENMP)
return "OpenMP";
else if (kernel == kCUDA)
return "Cuda";
else if (kernel == kGLSL)
return "GLSL";
else if (kernel == kCL)
return "OpenCL";
return "Unknown";
}
//------------------------------------------------------------------------------
union Effect {
struct {
unsigned int wire:2;
unsigned int color:3;
unsigned int displacement:2;
unsigned int normal:3;
int occlusion:1;
int specular:1;
int patchCull:1;
int screenSpaceTess:1;
int fractionalSpacing:1;
int ibl:1;
int seamless:1;
};
int value;
bool operator < (const Effect &e) const {
return value < e.value;
}
};
struct EffectDesc {
EffectDesc(OpenSubdiv::Far::PatchDescriptor desc,
Effect effect) : desc(desc), effect(effect),
maxValence(0), numElements(0) { }
OpenSubdiv::Far::PatchDescriptor desc;
Effect effect;
int maxValence;
int numElements;
bool operator < (const EffectDesc &e) const {
return desc < e.desc || (desc == e.desc &&
(maxValence < e.maxValence || ((maxValence == e.maxValence) &&
(effect < e.effect))));
}
};
//------------------------------------------------------------------------------
class ShaderCache : public GLShaderCache<EffectDesc> {
public:
virtual GLDrawConfig *CreateDrawConfig(EffectDesc const &effectDesc) {
using namespace OpenSubdiv;
// compile shader program
#if defined(GL_ARB_tessellation_shader) || defined(GL_VERSION_4_0)
const char *glslVersion = "#version 400\n";
#else
const char *glslVersion = "#version 330\n";
#endif
GLDrawConfig *config = new GLDrawConfig(glslVersion);
Far::PatchDescriptor::Type type = effectDesc.desc.GetType();
// common defines
std::stringstream ss;
if (type == Far::PatchDescriptor::QUADS) {
ss << "#define PRIM_QUAD\n";
} else if (type == Far::PatchDescriptor::LINES) {
ss << "#define PRIM_LINE\n";
} else {
ss << "#define PRIM_TRI\n";
}
// OSD tessellation controls
if (effectDesc.effect.screenSpaceTess) {
ss << "#define OSD_ENABLE_SCREENSPACE_TESSELLATION\n";
}
if (effectDesc.effect.fractionalSpacing) {
ss << "#define OSD_FRACTIONAL_ODD_SPACING\n";
}
if (effectDesc.effect.patchCull) {
ss << "#define OSD_ENABLE_PATCH_CULL\n";
}
// for legacy gregory
ss << "#define OSD_MAX_VALENCE " << effectDesc.maxValence << "\n";
ss << "#define OSD_NUM_ELEMENTS " << effectDesc.numElements << "\n";
// add ptex functions
ss << GLPtexMipmapTexture::GetShaderSource();
// -------------------------------------------------------------
// display styles
// -------------------------------------------------------------
// mipmap
if (effectDesc.effect.seamless) {
ss << "#define SEAMLESS_MIPMAP\n";
}
// wire
if (effectDesc.effect.wire == 0) {
ss << "#define GEOMETRY_OUT_WIRE\n";
} else if (effectDesc.effect.wire == 1) {
ss << "#define GEOMETRY_OUT_FILL\n";
} else if (effectDesc.effect.wire == 2) {
ss << "#define GEOMETRY_OUT_LINE\n";
}
// color
switch(effectDesc.effect.color) {
case COLOR_NONE:
break;
case COLOR_PTEX_NEAREST:
ss << "#define COLOR_PTEX_NEAREST\n";
break;
case COLOR_PTEX_HW_BILINEAR:
ss << "#define COLOR_PTEX_HW_BILINEAR\n";
break;
case COLOR_PTEX_BILINEAR:
ss << "#define COLOR_PTEX_BILINEAR\n";
break;
case COLOR_PTEX_BIQUADRATIC:
ss << "#define COLOR_PTEX_BIQUADRATIC\n";
break;
case COLOR_PATCHTYPE:
ss << "#define COLOR_PATCHTYPE\n";
break;
case COLOR_PATCHCOORD:
ss << "#define COLOR_PATCHCOORD\n";
break;
case COLOR_NORMAL:
ss << "#define COLOR_NORMAL\n";
break;
}
// displacement
switch (effectDesc.effect.displacement) {
case DISPLACEMENT_NONE:
break;
case DISPLACEMENT_HW_BILINEAR:
ss << "#define DISPLACEMENT_HW_BILINEAR\n";
break;
case DISPLACEMENT_BILINEAR:
ss << "#define DISPLACEMENT_BILINEAR\n";
break;
case DISPLACEMENT_BIQUADRATIC:
ss << "#define DISPLACEMENT_BIQUADRATIC\n";
break;
}
// normal
switch (effectDesc.effect.normal) {
case NORMAL_FACET:
ss << "#define NORMAL_FACET\n";
break;
case NORMAL_HW_SCREENSPACE:
ss << "#define NORMAL_HW_SCREENSPACE\n";
break;
case NORMAL_SCREENSPACE:
ss << "#define NORMAL_SCREENSPACE\n";
break;
case NORMAL_BIQUADRATIC:
ss << "#define NORMAL_BIQUADRATIC\n";
break;
case NORMAL_BIQUADRATIC_WG:
ss << "#define OSD_COMPUTE_NORMAL_DERIVATIVES\n";
ss << "#define NORMAL_BIQUADRATIC_WG\n";
break;
}
// occlusion
if (effectDesc.effect.occlusion)
ss << "#define USE_PTEX_OCCLUSION\n";
// specular
if (effectDesc.effect.specular)
ss << "#define USE_PTEX_SPECULAR\n";
// IBL
if (effectDesc.effect.ibl)
ss << "#define USE_IBL\n";
// need for patch color-coding : we need these defines in the fragment shader
if (type == Far::PatchDescriptor::GREGORY) {
ss << "#define OSD_PATCH_GREGORY\n";
} else if (type == Far::PatchDescriptor::GREGORY_BOUNDARY) {
ss << "#define OSD_PATCH_GREGORY_BOUNDARY\n";
} else if (type == Far::PatchDescriptor::GREGORY_BASIS) {
ss << "#define OSD_PATCH_GREGORY_BASIS\n";
}
// include osd PatchCommon
ss << Osd::GLSLPatchShaderSource::GetCommonShaderSource();
std::string common = ss.str();
ss.str("");
// vertex shader
ss << common
// enable local vertex shader
<< (effectDesc.desc.IsAdaptive() ? "" : "#define VERTEX_SHADER\n")
<< g_shaderSource
<< Osd::GLSLPatchShaderSource::GetVertexShaderSource(type);
config->CompileAndAttachShader(GL_VERTEX_SHADER, ss.str());
ss.str("");
if (effectDesc.desc.IsAdaptive()) {
// tess control shader
ss << common
<< g_shaderSource
<< Osd::GLSLPatchShaderSource::GetTessControlShaderSource(type);
config->CompileAndAttachShader(GL_TESS_CONTROL_SHADER, ss.str());
ss.str("");
// tess eval shader
ss << common
<< g_shaderSource
<< Osd::GLSLPatchShaderSource::GetTessEvalShaderSource(type);
config->CompileAndAttachShader(GL_TESS_EVALUATION_SHADER, ss.str());
ss.str("");
}
// geometry shader
ss << common
<< "#define GEOMETRY_SHADER\n" // enable local geometry shader
<< g_shaderSource;
config->CompileAndAttachShader(GL_GEOMETRY_SHADER, ss.str());
ss.str("");
// fragment shader
ss << common
<< "#define FRAGMENT_SHADER\n" // enable local fragment shader
<< g_shaderSource;
config->CompileAndAttachShader(GL_FRAGMENT_SHADER, ss.str());
ss.str("");
if (!config->Link()) {
delete config;
return NULL;
}
// assign uniform locations
GLuint program = config->GetProgram();
GLuint uboIndex = glGetUniformBlockIndex(program, "Constant");
if (uboIndex != GL_INVALID_INDEX)
glUniformBlockBinding(program, uboIndex, g_constantBinding);
// assign texture locations
GLint loc;
// patch textures
glUseProgram(program);
if ((loc = glGetUniformLocation(program, "OsdVertexBuffer")) != -1) {
glUniform1i(loc, 0); // GL_TEXTURE0
}
if ((loc = glGetUniformLocation(program, "OsdValenceBuffer")) != -1) {
glUniform1i(loc, 1); // GL_TEXTURE1
}
if ((loc = glGetUniformLocation(program, "OsdQuadOffsetBuffer")) != -1) {
glUniform1i(loc, 2); // GL_TEXTURE2
}
if ((loc = glGetUniformLocation(program, "OsdPatchParamBuffer")) != -1) {
glUniform1i(loc, 3); // GL_TEXTURE3
}
// environment textures
if ((loc = glGetUniformLocation(program, "diffuseEnvironmentMap")) != -1) {
glUniform1i(loc, 5);
}
if ((loc = glGetUniformLocation(program, "specularEnvironmentMap")) != -1) {
glUniform1i(loc, 6);
}
// ptex textures
if ((loc = glGetUniformLocation(program, "textureImage_Data")) != -1) {
glUniform1i(loc, 7);
}
if ((loc = glGetUniformLocation(program, "textureImage_Packing")) != -1) {
glUniform1i(loc, 8);
}
if ((loc = glGetUniformLocation(program, "textureDisplace_Data")) != -1) {
glUniform1i(loc, 9);
}
if ((loc = glGetUniformLocation(program, "textureDisplace_Packing")) != -1) {
glUniform1i(loc, 10);
}
if ((loc = glGetUniformLocation(program, "textureOcclusion_Data")) != -1) {
glUniform1i(loc, 11);
}
if ((loc = glGetUniformLocation(program, "textureOcclusion_Packing")) != -1) {
glUniform1i(loc, 12);
}
if ((loc = glGetUniformLocation(program, "textureSpecular_Data")) != -1) {
glUniform1i(loc, 13);
}
if ((loc = glGetUniformLocation(program, "textureSpecular_Packing")) != -1) {
glUniform1i(loc, 14);
}
glUseProgram(0);
return config;
}
};
ShaderCache g_shaderCache;
//------------------------------------------------------------------------------
GLPtexMipmapTexture *
createPtex(const char *filename, int memLimit) {
Ptex::String ptexError;
printf("Loading ptex : %s\n", filename);
#define USE_PTEX_CACHE
#define PTEX_CACHE_SIZE (512*1024*1024)
#ifdef USE_PTEX_CACHE
PtexCache *cache = PtexCache::create(1, PTEX_CACHE_SIZE);
PtexTexture *ptex = cache->get(filename, ptexError);
#else
PtexTexture *ptex = PtexTexture::open(filename, ptexError, true);
#endif
if (ptex == NULL) {
printf("Error in reading %s\n", filename);
exit(1);
}
size_t targetMemory = memLimit * 1024 * 1024; // MB
GLPtexMipmapTexture *osdPtex = GLPtexMipmapTexture::Create(
ptex, g_maxMipmapLevels, targetMemory);
GLuint texture = osdPtex->GetTexelsTexture();
glBindTexture(GL_TEXTURE_2D_ARRAY, texture);
GLint w, h, d;
glGetTexLevelParameteriv(GL_TEXTURE_2D_ARRAY, 0, GL_TEXTURE_WIDTH, &w);
glGetTexLevelParameteriv(GL_TEXTURE_2D_ARRAY, 0, GL_TEXTURE_HEIGHT, &h);
glGetTexLevelParameteriv(GL_TEXTURE_2D_ARRAY, 0, GL_TEXTURE_DEPTH, &d);
printf("PageSize = %d x %d x %d\n", w, h, d);
glBindTexture(GL_TEXTURE_2D_ARRAY, 0);
ptex->release();
#ifdef USE_PTEX_CACHE
cache->release();
#endif
return osdPtex;
}
//------------------------------------------------------------------------------
void
createOsdMesh(int level, int kernel) {
checkGLErrors("createOsdMesh");
Ptex::String ptexError;
PtexTexture *ptexColor = PtexTexture::open(g_ptexColorFilename, ptexError, true);
if (ptexColor == NULL) {
printf("Error in reading %s\n", g_ptexColorFilename);
exit(1);
}
// generate Shape representation from ptex
Shape * shape = createPTexGeo(ptexColor);
if (not shape) {
return;
}
g_positions=shape->verts;
typedef OpenSubdiv::Far::ConstIndexArray IndexArray;
// create Vtr mesh (topology)
OpenSubdiv::Sdc::SchemeType sdctype = GetSdcType(*shape);
OpenSubdiv::Sdc::Options sdcoptions = GetSdcOptions(*shape);
OpenSubdiv::Far::TopologyRefiner * refiner =
OpenSubdiv::Far::TopologyRefinerFactory<Shape>::Create(*shape,
OpenSubdiv::Far::TopologyRefinerFactory<Shape>::Options(sdctype, sdcoptions));
// save coarse topology (used for coarse mesh drawing)
// create cage edge index
int nedges = refiner->GetNumEdges(0);
std::vector<int> edgeIndices(nedges*2);
for(int i=0; i<nedges; ++i) {
IndexArray verts = refiner->GetEdgeVertices(0, i);
edgeIndices[i*2 ]=verts[0];
edgeIndices[i*2+1]=verts[1];
}
delete shape;
g_normals.resize(g_positions.size(), 0.0f);
calcNormals(refiner, g_positions, g_normals);
delete g_mesh;
g_mesh = NULL;
// Adaptive refinement currently supported only for catmull-clark scheme
bool doAdaptive = (g_adaptive != 0 and g_scheme == 0);
OpenSubdiv::Osd::MeshBitset bits;
bits.set(OpenSubdiv::Osd::MeshAdaptive, doAdaptive);
bits.set(OpenSubdiv::Osd::MeshEndCapGregoryBasis, true);
int numVertexElements = g_adaptive ? 3 : 6;
int numVaryingElements = 0;
if (kernel == kCPU) {
g_mesh = new OpenSubdiv::Osd::Mesh<OpenSubdiv::Osd::CpuGLVertexBuffer,
OpenSubdiv::Far::StencilTables,
OpenSubdiv::Osd::CpuEvaluator,
OpenSubdiv::Osd::GLDrawContext>(
refiner,
numVertexElements,
numVaryingElements,
level, bits);
#ifdef OPENSUBDIV_HAS_OPENMP
} else if (kernel == kOPENMP) {
g_mesh = new OpenSubdiv::Osd::Mesh<OpenSubdiv::Osd::CpuGLVertexBuffer,
OpenSubdiv::Far::StencilTables,
OpenSubdiv::Osd::OmpEvaluator,
OpenSubdiv::Osd::GLDrawContext>(
refiner,
numVertexElements,
numVaryingElements,
level, bits);
#endif
#ifdef OPENSUBDIV_HAS_TBB
} else if (kernel == kTBB) {
g_mesh = new OpenSubdiv::Osd::Mesh<OpenSubdiv::Osd::CpuGLVertexBuffer,
OpenSubdiv::Far::StencilTables,
OpenSubdiv::Osd::TbbEvaluator,
OpenSubdiv::Osd::GLDrawContext>(
refiner,
numVertexElements,
numVaryingElements,
level, bits);
#endif
#ifdef OPENSUBDIV_HAS_OPENCL
} else if (kernel == kCL) {
static OpenSubdiv::Osd::EvaluatorCacheT<OpenSubdiv::Osd::CLEvaluator> clEvaluatorCache;
g_mesh = new OpenSubdiv::Osd::Mesh<OpenSubdiv::Osd::CLGLVertexBuffer,
OpenSubdiv::Osd::CLStencilTables,
OpenSubdiv::Osd::CLEvaluator,
OpenSubdiv::Osd::GLDrawContext,
CLDeviceContext>(
refiner,
numVertexElements,
numVaryingElements,
level, bits,
&clEvaluatorCache,
&g_clDeviceContext);
#endif
#ifdef OPENSUBDIV_HAS_CUDA
} else if (kernel == kCUDA) {
g_mesh = new OpenSubdiv::Osd::Mesh<OpenSubdiv::Osd::CudaGLVertexBuffer,
OpenSubdiv::Osd::CudaStencilTables,
OpenSubdiv::Osd::CudaEvaluator,
OpenSubdiv::Osd::GLDrawContext>(
refiner,
numVertexElements,
numVaryingElements,
level, bits);
#endif
#ifdef OPENSUBDIV_HAS_GLSL_TRANSFORM_FEEDBACK
} else if (kernel == kGLSL) {
static OpenSubdiv::Osd::EvaluatorCacheT<OpenSubdiv::Osd::GLXFBEvaluator> glXFBEvaluatorCache;
g_mesh = new OpenSubdiv::Osd::Mesh<OpenSubdiv::Osd::GLVertexBuffer,
OpenSubdiv::Osd::GLStencilTablesTBO,
OpenSubdiv::Osd::GLXFBEvaluator,
OpenSubdiv::Osd::GLDrawContext>(
refiner,
numVertexElements,
numVaryingElements,
level, bits,
&glXFBEvaluatorCache);
#endif
#ifdef OPENSUBDIV_HAS_GLSL_COMPUTE
} else if (kernel == kGLSLCompute) {
static OpenSubdiv::Osd::EvaluatorCacheT<OpenSubdiv::Osd::GLComputeEvaluator> glComputeEvaluatorCache;
g_mesh = new OpenSubdiv::Osd::Mesh<OpenSubdiv::Osd::GLVertexBuffer,
OpenSubdiv::Osd::GLStencilTablesSSBO,
OpenSubdiv::Osd::GLComputeEvaluator,
OpenSubdiv::Osd::GLDrawContext>(
refiner,
numVertexElements,
numVaryingElements,
level, bits,
&glComputeEvaluatorCache);
#endif
} else {
printf("Unsupported kernel %s\n", getKernelName(kernel));
}
if (glGetError() != GL_NO_ERROR) {
printf("GLERROR\n");
}
updateGeom();
// ------ VAO
glBindVertexArray(g_vao);
glBindBuffer(GL_ARRAY_BUFFER, g_mesh->BindVertexBuffer());
if (g_adaptive) {
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
} else {
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 6, 0);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 6, (float*)12);
}
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_mesh->GetDrawContext()->GetPatchIndexBuffer());
// ------ Cage VAO
glBindVertexArray(g_cageEdgeVAO);
glBindBuffer(GL_ARRAY_BUFFER, g_mesh->BindVertexBuffer());
if (g_adaptive) {
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
} else {
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 6, 0);
}
if (not g_edgeIndexBuffer) glGenBuffers(1, &g_edgeIndexBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_edgeIndexBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(int)*edgeIndices.size(),
&edgeIndices[0], GL_STATIC_DRAW);
g_numCageEdges = (int)edgeIndices.size();
glBindVertexArray(0);
}
//------------------------------------------------------------------------------
void
createSky() {
const int U_DIV = 20;
const int V_DIV = 20;
std::vector<float> vbo;
std::vector<int> indices;
for (int u = 0; u <= U_DIV; ++u) {
for (int v = 0; v < V_DIV; ++v) {
float s = float(2*M_PI*float(u)/U_DIV);
float t = float(M_PI*float(v)/(V_DIV-1));
vbo.push_back(-sin(t)*sin(s));
vbo.push_back(cos(t));
vbo.push_back(-sin(t)*cos(s));
vbo.push_back(u/float(U_DIV));
vbo.push_back(v/float(V_DIV));
if (v > 0 && u > 0) {
indices.push_back((u-1)*V_DIV+v-1);
indices.push_back(u*V_DIV+v-1);
indices.push_back((u-1)*V_DIV+v);
indices.push_back((u-1)*V_DIV+v);
indices.push_back(u*V_DIV+v-1);
indices.push_back(u*V_DIV+v);
}
}
}
glGenBuffers(1, &g_sky.vertexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, g_sky.vertexBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(float)*vbo.size(), &vbo[0], GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glGenBuffers(1, &g_sky.elementBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_sky.elementBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(int)*indices.size(), &indices[0], GL_STATIC_DRAW);
g_sky.numIndices = (int)indices.size();
g_sky.BuildProgram(g_skyShaderSource);
GLint environmentMap = glGetUniformLocation(g_sky.GetProgram(), "environmentMap");
glUseProgram(g_sky.GetProgram());
if (g_specularEnvironmentMap)
glUniform1i(environmentMap, 6);
else
glUniform1i(environmentMap, 5);
glUseProgram(0);
g_sky.mvpMatrix = glGetUniformLocation(g_sky.GetProgram(), "ModelViewProjectionMatrix");
}
//------------------------------------------------------------------------------
static void
updateConstantUniformBlock() {
struct Constant {
float ModelViewMatrix[16];
float ProjectionMatrix[16];
float ModelViewProjectionMatrix[16];
float ModelViewInverseMatrix[16];
struct Light {
float position[4];
float ambient[4];
float diffuse[4];
float specular[4];
} lightSource[2];
float TessLevel;
float displacementScale;
float mipmapBias;
} constantData;
// transforms
double aspect = g_width/(double)g_height;
identity(constantData.ModelViewMatrix);
translate(constantData.ModelViewMatrix, -g_pan[0], -g_pan[1], -g_dolly);
rotate(constantData.ModelViewMatrix, g_rotate[1], 1, 0, 0);
rotate(constantData.ModelViewMatrix, g_rotate[0], 0, 1, 0);
if (g_yup)
rotate(constantData.ModelViewMatrix, -90, 1, 0, 0);
translate(constantData.ModelViewMatrix, -g_center[0], -g_center[1], -g_center[2]);
perspective(constantData.ProjectionMatrix, 45.0f, (float)aspect, g_size*0.001f,
g_size+g_dolly);
multMatrix(constantData.ModelViewProjectionMatrix,
constantData.ModelViewMatrix,
constantData.ProjectionMatrix);
inverseMatrix(constantData.ModelViewInverseMatrix,
constantData.ModelViewMatrix);
// lighs
Constant::Light light0 = { { 0.6f, 1.0f, 0.6f, 0.0f },
{ 0.1f, 0.1f, 0.1f, 1.0f },
{ 1.7f, 1.3f, 1.1f, 1.0f },
{ 1.0f, 1.0f, 1.0f, 1.0f } };
Constant::Light light1 = { { -0.8f, 0.6f, -0.7f, 0.0f },
{ 0.0f, 0.0f, 0.0f, 1.0f },
{ 0.8f, 0.8f, 1.5f, 1.0f },
{ 0.4f, 0.4f, 0.4f, 1.0f } };
constantData.lightSource[0] = light0;
constantData.lightSource[1] = light1;
// other
constantData.TessLevel = static_cast<float>(1 << g_tessLevel);
constantData.displacementScale = g_displacementScale;
constantData.mipmapBias = g_mipmapBias;
// update GPU buffer
if (g_constantUB == 0) {
glGenBuffers(1, &g_constantUB);
glBindBuffer(GL_UNIFORM_BUFFER, g_constantUB);
glBufferData(GL_UNIFORM_BUFFER,
sizeof(constantData), NULL, GL_STATIC_DRAW);
};
glBindBuffer(GL_UNIFORM_BUFFER, g_constantUB);
glBufferSubData(GL_UNIFORM_BUFFER,
0, sizeof(constantData), &constantData);
glBindBuffer(GL_UNIFORM_BUFFER, 0);
glBindBufferBase(GL_UNIFORM_BUFFER, g_constantBinding, g_constantUB);
}
static void
bindTextures() {
#if defined(GL_ARB_tessellation_shader) || defined(GL_VERSION_4_0)
if (g_mesh->GetDrawContext()->GetVertexTextureBuffer()) {
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_BUFFER,
g_mesh->GetDrawContext()->GetVertexTextureBuffer());
}
if (g_mesh->GetDrawContext()->GetVertexValenceTextureBuffer()) {
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_BUFFER,
g_mesh->GetDrawContext()->GetVertexValenceTextureBuffer());
}
if (g_mesh->GetDrawContext()->GetQuadOffsetsTextureBuffer()) {
glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_BUFFER,
g_mesh->GetDrawContext()->GetQuadOffsetsTextureBuffer());
}
#endif
if (g_mesh->GetDrawContext()->GetPatchParamTextureBuffer()) {
glActiveTexture(GL_TEXTURE3);
glBindTexture(GL_TEXTURE_BUFFER,
g_mesh->GetDrawContext()->GetPatchParamTextureBuffer());
}
// other textures
if (g_ibl) {
if (g_diffuseEnvironmentMap) {
glActiveTexture(GL_TEXTURE5);
glBindTexture(GL_TEXTURE_2D, g_diffuseEnvironmentMap);
}
if (g_specularEnvironmentMap) {
glActiveTexture(GL_TEXTURE6);
glBindTexture(GL_TEXTURE_2D, g_specularEnvironmentMap);
}
glActiveTexture(GL_TEXTURE0);
}
// color ptex
glActiveTexture(GL_TEXTURE7);
glBindTexture(GL_TEXTURE_2D_ARRAY, g_osdPTexImage->GetTexelsTexture());
glActiveTexture(GL_TEXTURE8);
glBindTexture(GL_TEXTURE_BUFFER, g_osdPTexImage->GetLayoutTextureBuffer());
// displacement ptex
if (g_displacement != DISPLACEMENT_NONE || g_normal) {
glActiveTexture(GL_TEXTURE9);
glBindTexture(GL_TEXTURE_2D_ARRAY, g_osdPTexDisplacement->GetTexelsTexture());
glActiveTexture(GL_TEXTURE10);
glBindTexture(GL_TEXTURE_BUFFER, g_osdPTexDisplacement->GetLayoutTextureBuffer());
}
// occlusion ptex
if (g_occlusion) {
glActiveTexture(GL_TEXTURE11);
glBindTexture(GL_TEXTURE_2D_ARRAY, g_osdPTexOcclusion->GetTexelsTexture());
glActiveTexture(GL_TEXTURE12);
glBindTexture(GL_TEXTURE_BUFFER, g_osdPTexOcclusion->GetLayoutTextureBuffer());
}
// specular ptex
if (g_specular) {
glActiveTexture(GL_TEXTURE13);
glBindTexture(GL_TEXTURE_2D_ARRAY, g_osdPTexSpecular->GetTexelsTexture());
glActiveTexture(GL_TEXTURE14);
glBindTexture(GL_TEXTURE_BUFFER, g_osdPTexSpecular->GetLayoutTextureBuffer());
}
glActiveTexture(GL_TEXTURE0);
}
//------------------------------------------------------------------------------
static GLenum
bindProgram(Effect effect,
OpenSubdiv::Osd::DrawContext::PatchArray const & patch) {
EffectDesc effectDesc(patch.GetDescriptor(), effect);
// only legacy gregory needs maxValence and numElements
typedef OpenSubdiv::Far::PatchDescriptor Descriptor;
if (patch.GetDescriptor().GetType() == Descriptor::GREGORY or
patch.GetDescriptor().GetType() == Descriptor::GREGORY_BOUNDARY) {
int maxValence = g_mesh->GetDrawContext()->GetMaxValence();
effectDesc.maxValence = maxValence;
effectDesc.numElements = 3;
}
GLDrawConfig *config = g_shaderCache.GetDrawConfig(effectDesc);
if (!config) return 0;
GLuint program = config->GetProgram();
glUseProgram(program);
// bind standalone uniforms
GLint uniformGregoryQuadOffsetBase =
glGetUniformLocation(program, "GregoryQuadOffsetBase");
GLint uniformPrimitiveIdBase =
glGetUniformLocation(program, "PrimitiveIdBase");
if (uniformGregoryQuadOffsetBase >= 0)
glUniform1i(uniformGregoryQuadOffsetBase, patch.GetQuadOffsetIndex());
if (uniformPrimitiveIdBase >= 0)
glUniform1i(uniformPrimitiveIdBase, patch.GetPatchIndex());
GLenum primType;
switch(effectDesc.desc.GetType()) {
case OpenSubdiv::Far::PatchDescriptor::QUADS:
primType = GL_LINES_ADJACENCY;
break;
case OpenSubdiv::Far::PatchDescriptor::TRIANGLES:
primType = GL_TRIANGLES;
break;
default:
#if defined(GL_ARB_tessellation_shader) || defined(GL_VERSION_4_0)
primType = GL_PATCHES;
glPatchParameteri(GL_PATCH_VERTICES, effectDesc.desc.GetNumControlVertices());
#else
primType = GL_POINTS;
#endif
}
return primType;
}
//------------------------------------------------------------------------------
void
drawModel() {
g_mesh->BindVertexBuffer();
// bind patch related textures and PtexTexture
bindTextures();
glBindVertexArray(g_vao);
// patch drawing
OpenSubdiv::Osd::DrawContext::PatchArrayVector const & patches =
g_mesh->GetDrawContext()->GetPatchArrays();
for (int i = 0; i < (int)patches.size(); ++i) {
OpenSubdiv::Osd::DrawContext::PatchArray const & patch = patches[i];
Effect effect;
effect.value = 0;
effect.color = g_color;
effect.displacement = g_displacement;
effect.occlusion = g_occlusion;
effect.normal = g_normal;
effect.specular = g_specular;
effect.patchCull = g_patchCull;
effect.screenSpaceTess = g_screenSpaceTess;
effect.fractionalSpacing = g_fractionalSpacing;
effect.ibl = g_ibl;
effect.wire = g_wire;
effect.seamless = g_seamless;
GLenum primType = bindProgram(effect, patch);
glDrawElements(primType,
patch.GetNumIndices(), GL_UNSIGNED_INT,
(void *)(patch.GetVertIndex() * sizeof(unsigned int)));
}
glBindVertexArray(0);
}
//------------------------------------------------------------------------------
void
drawSky() {
glUseProgram(g_sky.GetProgram());
glDisable(GL_DEPTH_TEST);
glDepthMask(GL_FALSE);
float modelView[16], projection[16], mvp[16];
double aspect = g_width/(double)g_height;
identity(modelView);
rotate(modelView, g_rotate[1], 1, 0, 0);
rotate(modelView, g_rotate[0], 0, 1, 0);
perspective(projection, 45.0f, (float)aspect, g_size*0.001f, g_size+g_dolly);
multMatrix(mvp, modelView, projection);
glUniformMatrix4fv(g_sky.mvpMatrix, 1, GL_FALSE, mvp);
glBindVertexArray(g_skyVAO);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, g_sky.vertexBuffer);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 5, 0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 5,
(void*)(sizeof(GLfloat)*3));
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_sky.elementBuffer);
glDrawElements(GL_TRIANGLES, g_sky.numIndices, GL_UNSIGNED_INT, 0);
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glBindVertexArray(0);
glEnable(GL_DEPTH_TEST);
glDepthMask(GL_TRUE);
checkGLErrors("draw model");
}
//------------------------------------------------------------------------------
void
drawCageEdges() {
g_mesh->BindVertexBuffer();
glBindVertexArray(g_cageEdgeVAO);
Effect effect;
effect.value = 0;
EffectDesc effectDesc(
OpenSubdiv::Far::PatchDescriptor(
OpenSubdiv::Far::PatchDescriptor::LINES), effect);
GLDrawConfig *config = g_shaderCache.GetDrawConfig(effectDesc);
if (!config) return;
glUseProgram(config->GetProgram());
glDrawElements(GL_LINES, g_numCageEdges, GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
glUseProgram(0);
checkGLErrors("draw cage edges");
}
//------------------------------------------------------------------------------
void
display() {
g_hud.GetFrameBuffer()->Bind();
Stopwatch s;
s.Start();
glViewport(0, 0, g_width, g_height);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
if (g_ibl) {
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
drawSky();
}
// update transform and light
updateConstantUniformBlock();
glEnable(GL_DEPTH_TEST);
if (g_wire == DISPLAY_WIRE) {
glDisable(GL_CULL_FACE);
}
// primitive counting
glBeginQuery(GL_PRIMITIVES_GENERATED, g_queries[0]);
#if defined(GL_VERSION_3_3)
glBeginQuery(GL_TIME_ELAPSED, g_queries[1]);
#endif
drawModel();
glEndQuery(GL_PRIMITIVES_GENERATED);
#if defined(GL_VERSION_3_3)
glEndQuery(GL_TIME_ELAPSED);
#endif
if (g_drawCageEdges)
drawCageEdges();
if (g_wire == DISPLAY_WIRE) {
glEnable(GL_CULL_FACE);
}
glDisable(GL_DEPTH_TEST);
glUseProgram(0);
s.Stop();
float drawCpuTime = float(s.GetElapsed() * 1000.0f);
GLuint numPrimsGenerated = 0;
GLuint timeElapsed = 0;
glGetQueryObjectuiv(g_queries[0], GL_QUERY_RESULT, &numPrimsGenerated);
#if defined(GL_VERSION_3_3)
glGetQueryObjectuiv(g_queries[1], GL_QUERY_RESULT, &timeElapsed);
#endif
float drawGpuTime = timeElapsed / 1000.0f / 1000.0f;
g_fpsTimer.Stop();
float elapsed = (float)g_fpsTimer.GetElapsed();
if (not g_freeze)
g_animTime += elapsed;
g_fpsTimer.Start();
g_hud.GetFrameBuffer()->ApplyImageShader();
if (g_hud.IsVisible()) {
double fps = 1.0/elapsed;
// Avereage fps over a defined number of time samples for
// easier reading in the HUD
g_fpsTimeSamples[g_currentFpsTimeSample++] = float(fps);
if (g_currentFpsTimeSample >= NUM_FPS_TIME_SAMPLES)
g_currentFpsTimeSample = 0;
double averageFps = 0;
for (int i = 0; i < NUM_FPS_TIME_SAMPLES; ++i) {
averageFps += g_fpsTimeSamples[i]/(float)NUM_FPS_TIME_SAMPLES;
}
g_hud.DrawString(10, -220, "Ptex memory use : %.1f mb", g_ptexMemoryUsage/1024.0/1024.0);
g_hud.DrawString(10, -180, "Tess level (+/-): %d", g_tessLevel);
if (numPrimsGenerated > 1000000) {
g_hud.DrawString(10, -160, "Primitives : %3.1f million",
(float)numPrimsGenerated/1000000.0);
} else if (numPrimsGenerated > 1000) {
g_hud.DrawString(10, -160, "Primitives : %3.1f thousand",
(float)numPrimsGenerated/1000.0);
} else {
g_hud.DrawString(10, -160, "Primitives : %d", numPrimsGenerated);
}
g_hud.DrawString(10, -140, "Vertices : %d", g_mesh->GetNumVertices());
g_hud.DrawString(10, -120, "Scheme : %s", g_scheme == 0 ? "CATMARK" : "LOOP");
g_hud.DrawString(10, -100, "GPU Kernel : %.3f ms", g_gpuTime);
g_hud.DrawString(10, -80, "CPU Kernel : %.3f ms", g_cpuTime);
g_hud.DrawString(10, -60, "GPU Draw : %.3f ms", drawGpuTime);
g_hud.DrawString(10, -40, "CPU Draw : %.3f ms", drawCpuTime);
g_hud.DrawString(10, -20, "FPS : %3.1f", averageFps);
g_hud.Flush();
}
glFinish();
checkGLErrors("draw end");
}
//------------------------------------------------------------------------------
static void
mouse(GLFWwindow *, int button, int state, int /* mods */) {
if (state == GLFW_RELEASE)
g_hud.MouseRelease();
if (button == 0 && state == GLFW_PRESS && g_hud.MouseClick(g_prev_x, g_prev_y))
return;
g_mbutton[button] = (state == GLFW_PRESS);
}
//------------------------------------------------------------------------------
static void
motion(GLFWwindow *, double dx, double dy) {
int x = (int)dx, y = (int)dy;
if (g_hud.MouseCapture()) {
// check gui
g_hud.MouseMotion(x, y);
} else if (g_mbutton[0] && !g_mbutton[1] && !g_mbutton[2]) {
// orbit
g_rotate[0] += x - g_prev_x;
g_rotate[1] += y - g_prev_y;
} else if (!g_mbutton[0] && !g_mbutton[1] && g_mbutton[2]) {
// pan
g_pan[0] -= g_dolly*(x - g_prev_x)/g_width;
g_pan[1] += g_dolly*(y - g_prev_y)/g_height;
} else if ((g_mbutton[0] && !g_mbutton[1] && g_mbutton[2]) or
(!g_mbutton[0] && g_mbutton[1] && !g_mbutton[2])) {
// dolly
g_dolly -= g_dolly*0.01f*(x - g_prev_x);
if (g_dolly <= 0.01) g_dolly = 0.01f;
}
g_prev_x = x;
g_prev_y = y;
}
//------------------------------------------------------------------------------
void uninitGL() {
if (g_osdPTexImage) delete g_osdPTexImage;
if (g_osdPTexDisplacement) delete g_osdPTexDisplacement;
if (g_osdPTexOcclusion) delete g_osdPTexOcclusion;
if (g_osdPTexSpecular) delete g_osdPTexSpecular;
glDeleteQueries(2, g_queries);
glDeleteVertexArrays(1, &g_vao);
glDeleteVertexArrays(1, &g_cageEdgeVAO);
glDeleteVertexArrays(1, &g_skyVAO);
if (g_mesh)
delete g_mesh;
if (g_diffuseEnvironmentMap)
glDeleteTextures(1, &g_diffuseEnvironmentMap);
if (g_specularEnvironmentMap)
glDeleteTextures(1, &g_specularEnvironmentMap);
if (g_sky.vertexBuffer) glDeleteBuffers(1, &g_sky.vertexBuffer);
if (g_sky.elementBuffer) glDeleteBuffers(1, &g_sky.elementBuffer);
}
//------------------------------------------------------------------------------
static void
callbackWireframe(int b) {
g_wire = b;
}
static void
callbackKernel(int k) {
g_kernel = k;
#ifdef OPENSUBDIV_HAS_OPENCL
if (g_kernel == kCL and (not g_clDeviceContext.IsInitialized())) {
// Initialize OpenCL
if (g_clDeviceContext.Initialize() == false) {
printf("Error in initializing OpenCL\n");
exit(1);
}
}
#endif
#ifdef OPENSUBDIV_HAS_CUDA
if (g_kernel == kCUDA and (not g_cudaDeviceContext.IsInitialized())) {
if (g_cudaDeviceContext.Initialize() == false) {
printf("Error in initializing Cuda\n");
exit(1);
}
}
#endif
createOsdMesh(g_level, g_kernel);
}
static void
callbackScheme(int s) {
g_scheme = s;
createOsdMesh(g_level, g_kernel);
}
static void
callbackLevel(int l) {
g_level = l;
createOsdMesh(g_level, g_kernel);
}
static void
callbackColor(int c) {
g_color = c;
}
static void
callbackDisplacement(int d) {
g_displacement = d;
}
static void
callbackNormal(int n) {
g_normal = n;
}
static void
callbackCheckBox(bool checked, int button) {
bool rebuild = false;
switch (button) {
case HUD_CB_ADAPTIVE:
if (OpenSubdiv::Osd::GLDrawContext::SupportsAdaptiveTessellation()) {
g_adaptive = checked;
rebuild = true;
}
break;
case HUD_CB_DISPLAY_OCCLUSION:
g_occlusion = checked;
break;
case HUD_CB_DISPLAY_SPECULAR:
g_specular = checked;
break;
case HUD_CB_CAGE_EDGES:
g_drawCageEdges = checked;
break;
case HUD_CB_ANIMATE_VERTICES:
g_moveScale = checked ? 1.0f : 0.0f;
g_animTime = 0;
break;
case HUD_CB_VIEW_LOD:
g_screenSpaceTess = checked;
break;
case HUD_CB_FRACTIONAL_SPACING:
g_fractionalSpacing = checked;
break;
case HUD_CB_PATCH_CULL:
g_patchCull = checked;
break;
case HUD_CB_IBL:
g_ibl = checked;
break;
case HUD_CB_BLOOM:
g_bloom = checked;
break;
case HUD_CB_SEAMLESS_MIPMAP:
g_seamless = checked;
break;
case HUD_CB_FREEZE:
g_freeze = checked;
break;
}
if (rebuild)
createOsdMesh(g_level, g_kernel);
}
static void
callbackSlider(float value, int data) {
switch (data) {
case 0:
g_mipmapBias = value;
break;
case 1:
g_displacementScale = value;
break;
}
}
//-------------------------------------------------------------------------------
void
reloadShaderFile() {
if (not g_shaderFilename) return;
std::ifstream ifs(g_shaderFilename);
if (not ifs) return;
printf("Load shader %s\n", g_shaderFilename);
std::stringstream ss;
ss << ifs.rdbuf();
ifs.close();
g_shaderSource = ss.str();
g_shaderCache.Reset();
}
//------------------------------------------------------------------------------
static void
toggleFullScreen() {
// XXXX manuelk : to re-implement from glut
}
//------------------------------------------------------------------------------
void
keyboard(GLFWwindow *, int key, int /* scancode */, int event, int /* mods */) {
if (event == GLFW_RELEASE) return;
if (g_hud.KeyDown(tolower(key))) return;
switch (key) {
case 'Q': g_running = 0; break;
case 'E': g_drawNormals = (g_drawNormals+1)%2; break;
case 'F': fitFrame(); break;
case GLFW_KEY_TAB: toggleFullScreen(); break;
case 'R': reloadShaderFile(); createOsdMesh(g_level, g_kernel); break;
case '+':
case '=': g_tessLevel++; break;
case '-': g_tessLevel = std::max(1, g_tessLevel-1); break;
case GLFW_KEY_ESCAPE: g_hud.SetVisible(!g_hud.IsVisible()); break;
case 'X': g_hud.GetFrameBuffer()->Screenshot(); break;
}
}
//------------------------------------------------------------------------------
void
idle() {
if (not g_freeze)
g_frame++;
updateGeom();
if (g_repeatCount != 0 && g_frame >= g_repeatCount)
g_running = 0;
}
//------------------------------------------------------------------------------
void
initGL() {
glClearColor(0.1f, 0.1f, 0.1f, 0.0f);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
glEnable(GL_CULL_FACE);
glGenQueries(2, g_queries);
glGenVertexArrays(1, &g_vao);
glGenVertexArrays(1, &g_cageEdgeVAO);
glGenVertexArrays(1, &g_skyVAO);
glBindTexture(GL_TEXTURE_2D, 0);
}
//------------------------------------------------------------------------------
void usage(const char *program) {
printf("Usage: %s [options] <color.ptx> [<displacement.ptx>] [occlusion.ptx>] "
"[specular.ptx] [pose.obj]...\n", program);
printf("Options: -l level : subdivision level\n");
printf(" -c count : frame count until exit (for profiler)\n");
printf(" -d <diffseEnvMap.hdr> : diffuse environment map for IBL\n");
printf(" -e <specularEnvMap.hdr> : specular environment map for IBL\n");
printf(" -s <shaderfile.glsl> : custom shader file\n");
printf(" -y : Y-up model\n");
printf(" -m level : max mimmap level (default=10)\n");
printf(" -x <ptex limit MB> : ptex target memory size\n");
printf(" --disp <scale> : Displacment scale\n");
}
//------------------------------------------------------------------------------
static void
callbackError(OpenSubdiv::Far::ErrorType err, const char *message) {
printf("Error: %d\n", err);
printf("%s", message);
}
//------------------------------------------------------------------------------
static void
callbackErrorGLFW(int error, const char* description) {
fprintf(stderr, "GLFW Error (%d) : %s\n", error, description);
}
//------------------------------------------------------------------------------
static void
setGLCoreProfile() {
#define glfwOpenWindowHint glfwWindowHint
#define GLFW_OPENGL_VERSION_MAJOR GLFW_CONTEXT_VERSION_MAJOR
#define GLFW_OPENGL_VERSION_MINOR GLFW_CONTEXT_VERSION_MINOR
glfwOpenWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
#if not defined(__APPLE__)
glfwOpenWindowHint(GLFW_OPENGL_VERSION_MAJOR, 4);
#ifdef OPENSUBDIV_HAS_GLSL_COMPUTE
glfwOpenWindowHint(GLFW_OPENGL_VERSION_MINOR, 3);
#else
glfwOpenWindowHint(GLFW_OPENGL_VERSION_MINOR, 2);
#endif
#else
glfwOpenWindowHint(GLFW_OPENGL_VERSION_MAJOR, 3);
glfwOpenWindowHint(GLFW_OPENGL_VERSION_MINOR, 2);
#endif
glfwOpenWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
}
//------------------------------------------------------------------------------
int main(int argc, char ** argv) {
std::vector<std::string> animobjs;
const char *diffuseEnvironmentMap = NULL, *specularEnvironmentMap = NULL;
const char *colorFilename = NULL, *displacementFilename = NULL,
*occlusionFilename = NULL, *specularFilename = NULL;
int memLimit = 0, colorMem = 0, displacementMem = 0,
occlusionMem = 0, specularMem = 0;
bool fullscreen = false;
for (int i = 1; i < argc; ++i) {
if (strstr(argv[i], ".obj"))
animobjs.push_back(argv[i]);
else if (!strcmp(argv[i], "-l"))
g_level = atoi(argv[++i]);
else if (!strcmp(argv[i], "-c"))
g_repeatCount = atoi(argv[++i]);
else if (!strcmp(argv[i], "-d"))
diffuseEnvironmentMap = argv[++i];
else if (!strcmp(argv[i], "-e"))
specularEnvironmentMap = argv[++i];
else if (!strcmp(argv[i], "-s"))
g_shaderFilename = argv[++i];
else if (!strcmp(argv[i], "-f"))
fullscreen = true;
else if (!strcmp(argv[i], "-y"))
g_yup = true;
else if (!strcmp(argv[i], "-m"))
g_maxMipmapLevels = atoi(argv[++i]);
else if (!strcmp(argv[i], "-x"))
memLimit = atoi(argv[++i]);
else if (!strcmp(argv[i], "--disp"))
g_displacementScale = (float)atof(argv[++i]);
else if (colorFilename == NULL) {
colorFilename = argv[i];
colorMem = memLimit;
} else if (displacementFilename == NULL) {
displacementFilename = argv[i];
displacementMem = memLimit;
g_displacement = DISPLACEMENT_BILINEAR;
g_normal = NORMAL_BIQUADRATIC;
} else if (occlusionFilename == NULL) {
occlusionFilename = argv[i];
occlusionMem = memLimit;
g_occlusion = 1;
} else if (specularFilename == NULL) {
specularFilename = argv[i];
specularMem = memLimit;
g_specular = 1;
}
}
OpenSubdiv::Far::SetErrorCallback(callbackError);
g_shaderSource = g_defaultShaderSource;
reloadShaderFile();
g_ptexColorFilename = colorFilename;
if (g_ptexColorFilename == NULL) {
usage(argv[0]);
return 1;
}
glfwSetErrorCallback(callbackErrorGLFW);
if (not glfwInit()) {
printf("Failed to initialize GLFW\n");
return 1;
}
static const char windowTitle[] = "OpenSubdiv glPtexViewer" OPENSUBDIV_VERSION_STRING;
#define CORE_PROFILE
#ifdef CORE_PROFILE
setGLCoreProfile();
#endif
if (fullscreen) {
g_primary = glfwGetPrimaryMonitor();
// apparently glfwGetPrimaryMonitor fails under linux : if no primary,
// settle for the first one in the list
if (not g_primary) {
int count = 0;
GLFWmonitor ** monitors = glfwGetMonitors(&count);
if (count)
g_primary = monitors[0];
}
if (g_primary) {
GLFWvidmode const * vidmode = glfwGetVideoMode(g_primary);
g_width = vidmode->width;
g_height = vidmode->height;
}
}
if (not (g_window=glfwCreateWindow(g_width, g_height, windowTitle,
fullscreen and g_primary ? g_primary : NULL, NULL))) {
printf("Failed to open window.\n");
glfwTerminate();
return 1;
}
glfwMakeContextCurrent(g_window);
glfwSetKeyCallback(g_window, keyboard);
glfwSetCursorPosCallback(g_window, motion);
glfwSetMouseButtonCallback(g_window, mouse);
#if defined(OSD_USES_GLEW)
#ifdef CORE_PROFILE
// this is the only way to initialize glew correctly under core profile context.
glewExperimental = true;
#endif
if (GLenum r = glewInit() != GLEW_OK) {
printf("Failed to initialize glew. error = %d\n", r);
exit(1);
}
#ifdef CORE_PROFILE
// clear GL errors which was generated during glewInit()
glGetError();
#endif
#endif
initGL();
// accommodate high DPI displays (e.g. mac retina displays)
glfwGetFramebufferSize(g_window, &g_width, &g_height);
glfwSetFramebufferSizeCallback(g_window, reshape);
glfwSetWindowCloseCallback(g_window, windowClose);
// as of GLFW 3.0.1 this callback is not implicit
reshape();
// activate feature adaptive tessellation if OSD supports it
g_adaptive = OpenSubdiv::Osd::GLDrawContext::SupportsAdaptiveTessellation();
int windowWidth = g_width, windowHeight = g_height;
// window size might not match framebuffer size on a high DPI display
glfwGetWindowSize(g_window, &windowWidth, &windowHeight);
g_hud.Init(windowWidth, windowHeight, g_width, g_height);
g_hud.SetFrameBuffer(new GLFrameBuffer);
if (occlusionFilename != NULL) {
g_hud.AddCheckBox("Ambient Occlusion (A)", g_occlusion,
-200, 570, callbackCheckBox, HUD_CB_DISPLAY_OCCLUSION, 'a');
}
if (specularFilename != NULL)
g_hud.AddCheckBox("Specular (S)", g_specular,
-200, 590, callbackCheckBox, HUD_CB_DISPLAY_SPECULAR, 's');
if (diffuseEnvironmentMap or specularEnvironmentMap) {
g_hud.AddCheckBox("IBL (I)", g_ibl,
-200, 610, callbackCheckBox, HUD_CB_IBL, 'i');
}
g_hud.AddCheckBox("Cage Edges (H)", g_drawCageEdges != 0,
10, 10, callbackCheckBox, HUD_CB_CAGE_EDGES, 'h');
g_hud.AddCheckBox("Animate vertices (M)", g_moveScale != 0.0,
10, 30, callbackCheckBox, HUD_CB_ANIMATE_VERTICES, 'm');
g_hud.AddCheckBox("Screen space LOD (V)", g_screenSpaceTess,
10, 50, callbackCheckBox, HUD_CB_VIEW_LOD, 'v');
g_hud.AddCheckBox("Fractional spacing (T)", g_fractionalSpacing,
10, 70, callbackCheckBox, HUD_CB_FRACTIONAL_SPACING, 't');
g_hud.AddCheckBox("Frustum Patch Culling (B)", g_patchCull,
10, 90, callbackCheckBox, HUD_CB_PATCH_CULL, 'b');
g_hud.AddCheckBox("Bloom (Y)", g_bloom,
10, 110, callbackCheckBox, HUD_CB_BLOOM, 'y');
g_hud.AddCheckBox("Freeze (spc)", g_freeze,
10, 130, callbackCheckBox, HUD_CB_FREEZE, ' ');
g_hud.AddRadioButton(HUD_RB_SCHEME, "CATMARK", true, 10, 190, callbackScheme, 0);
g_hud.AddRadioButton(HUD_RB_SCHEME, "BILINEAR", false, 10, 210, callbackScheme, 1);
if (OpenSubdiv::Osd::GLDrawContext::SupportsAdaptiveTessellation())
g_hud.AddCheckBox("Adaptive (`)", g_adaptive,
10, 300, callbackCheckBox, HUD_CB_ADAPTIVE, '`');
for (int i = 1; i < 8; ++i) {
char level[16];
sprintf(level, "Lv. %d", i);
g_hud.AddRadioButton(HUD_RB_LEVEL, level, i == g_level,
10, 320+i*20, callbackLevel, i, '0'+i);
}
int compute_pulldown = g_hud.AddPullDown("Compute (K)", 475, 10, 300, callbackKernel, 'k');
g_hud.AddPullDownButton(compute_pulldown, "CPU", kCPU);
#ifdef OPENSUBDIV_HAS_OPENMP
g_hud.AddPullDownButton(compute_pulldown, "OpenMP", kOPENMP);
#endif
#ifdef OPENSUBDIV_HAS_TBB
g_hud.AddPullDownButton(compute_pulldown, "TBB", kTBB);
#endif
#ifdef OPENSUBDIV_HAS_CUDA
g_hud.AddPullDownButton(compute_pulldown, "CUDA", kCUDA);
#endif
#ifdef OPENSUBDIV_HAS_OPENCL
if (CLDeviceContext::HAS_CL_VERSION_1_1()) {
g_hud.AddPullDownButton(compute_pulldown, "OpenCL", kCL);
}
#endif
#ifdef OPENSUBDIV_HAS_GLSL_TRANSFORM_FEEDBACK
g_hud.AddPullDownButton(compute_pulldown, "GLSL TransformFeedback", kGLSL);
#endif
#ifdef OPENSUBDIV_HAS_GLSL_COMPUTE
// Must also check at run time for OpenGL 4.3
if (GLEW_VERSION_4_3) {
g_hud.AddPullDownButton(compute_pulldown, "GLSL Compute", kGLSLCompute);
}
#endif
int shading_pulldown = g_hud.AddPullDown("Shading (W)", 250, 10, 250, callbackWireframe, 'w');
g_hud.AddPullDownButton(shading_pulldown, "Wire", DISPLAY_WIRE, g_wire==DISPLAY_WIRE);
g_hud.AddPullDownButton(shading_pulldown, "Shaded", DISPLAY_SHADED, g_wire==DISPLAY_SHADED);
g_hud.AddPullDownButton(shading_pulldown, "Wire+Shaded", DISPLAY_WIRE_ON_SHADED, g_wire==DISPLAY_WIRE_ON_SHADED);
g_hud.AddLabel("Color (C)", -200, 10);
g_hud.AddRadioButton(HUD_RB_COLOR, "None", (g_color == COLOR_NONE),
-200, 30, callbackColor, COLOR_NONE, 'c');
g_hud.AddRadioButton(HUD_RB_COLOR, "Ptex Nearest", (g_color == COLOR_PTEX_NEAREST),
-200, 50, callbackColor, COLOR_PTEX_NEAREST, 'c');
g_hud.AddRadioButton(HUD_RB_COLOR, "Ptex HW bilinear", (g_color == COLOR_PTEX_HW_BILINEAR),
-200, 70, callbackColor, COLOR_PTEX_HW_BILINEAR, 'c');
g_hud.AddRadioButton(HUD_RB_COLOR, "Ptex bilinear", (g_color == COLOR_PTEX_BILINEAR),
-200, 90, callbackColor, COLOR_PTEX_BILINEAR, 'c');
g_hud.AddRadioButton(HUD_RB_COLOR, "Ptex biquadratic", (g_color == COLOR_PTEX_BIQUADRATIC),
-200, 110, callbackColor, COLOR_PTEX_BIQUADRATIC, 'c');
g_hud.AddRadioButton(HUD_RB_COLOR, "Patch type", (g_color == COLOR_PATCHTYPE),
-200, 130, callbackColor, COLOR_PATCHTYPE, 'c');
g_hud.AddRadioButton(HUD_RB_COLOR, "Patch coord", (g_color == COLOR_PATCHCOORD),
-200, 150, callbackColor, COLOR_PATCHCOORD, 'c');
g_hud.AddRadioButton(HUD_RB_COLOR, "Normal", (g_color == COLOR_NORMAL),
-200, 170, callbackColor, COLOR_NORMAL, 'c');
if (displacementFilename != NULL) {
g_hud.AddLabel("Displacement (D)", -200, 200);
g_hud.AddRadioButton(HUD_RB_DISPLACEMENT, "None",
(g_displacement == DISPLACEMENT_NONE),
-200, 220, callbackDisplacement, DISPLACEMENT_NONE, 'd');
g_hud.AddRadioButton(HUD_RB_DISPLACEMENT, "HW bilinear",
(g_displacement == DISPLACEMENT_HW_BILINEAR),
-200, 240, callbackDisplacement, DISPLACEMENT_HW_BILINEAR, 'd');
g_hud.AddRadioButton(HUD_RB_DISPLACEMENT, "Bilinear",
(g_displacement == DISPLACEMENT_BILINEAR),
-200, 260, callbackDisplacement, DISPLACEMENT_BILINEAR, 'd');
g_hud.AddRadioButton(HUD_RB_DISPLACEMENT, "Biquadratic",
(g_displacement == DISPLACEMENT_BIQUADRATIC),
-200, 280, callbackDisplacement, DISPLACEMENT_BIQUADRATIC, 'd');
g_hud.AddLabel("Normal (N)", -200, 310);
g_hud.AddRadioButton(HUD_RB_NORMAL, "Surface",
(g_normal == NORMAL_SURFACE),
-200, 330, callbackNormal, NORMAL_SURFACE, 'n');
g_hud.AddRadioButton(HUD_RB_NORMAL, "Facet",
(g_normal == NORMAL_FACET),
-200, 350, callbackNormal, NORMAL_FACET, 'n');
g_hud.AddRadioButton(HUD_RB_NORMAL, "HW Screen space",
(g_normal == NORMAL_HW_SCREENSPACE),
-200, 370, callbackNormal, NORMAL_HW_SCREENSPACE, 'n');
g_hud.AddRadioButton(HUD_RB_NORMAL, "Screen space",
(g_normal == NORMAL_SCREENSPACE),
-200, 390, callbackNormal, NORMAL_SCREENSPACE, 'n');
g_hud.AddRadioButton(HUD_RB_NORMAL, "Biquadratic",
(g_normal == NORMAL_BIQUADRATIC),
-200, 410, callbackNormal, NORMAL_BIQUADRATIC, 'n');
g_hud.AddRadioButton(HUD_RB_NORMAL, "Biquadratic WG",
(g_normal == NORMAL_BIQUADRATIC_WG),
-200, 430, callbackNormal, NORMAL_BIQUADRATIC_WG, 'n');
}
g_hud.AddSlider("Mipmap Bias", 0, 5, 0,
-200, 450, 20, false, callbackSlider, 0);
g_hud.AddSlider("Displacement", 0, 5, 1,
-200, 490, 20, false, callbackSlider, 1);
g_hud.AddCheckBox("Seamless Mipmap", g_seamless,
-200, 530, callbackCheckBox, HUD_CB_SEAMLESS_MIPMAP, 'j');
g_hud.Rebuild(windowWidth, windowHeight, g_width, g_height);
// create mesh from ptex metadata
createOsdMesh(g_level, g_kernel);
// load ptex files
if (colorFilename)
g_osdPTexImage = createPtex(colorFilename, colorMem);
if (displacementFilename)
g_osdPTexDisplacement = createPtex(displacementFilename, displacementMem);
if (occlusionFilename)
g_osdPTexOcclusion = createPtex(occlusionFilename, occlusionMem);
if (specularFilename)
g_osdPTexSpecular = createPtex(specularFilename, specularMem);
g_ptexMemoryUsage =
(g_osdPTexImage ? g_osdPTexImage->GetMemoryUsage() : 0)
+ (g_osdPTexDisplacement ? g_osdPTexDisplacement->GetMemoryUsage() : 0)
+ (g_osdPTexOcclusion ? g_osdPTexOcclusion->GetMemoryUsage() : 0)
+ (g_osdPTexSpecular ? g_osdPTexSpecular->GetMemoryUsage() : 0);
// load animation obj sequences (optional)
if (not animobjs.empty()) {
for (int i = 0; i < (int)animobjs.size(); ++i) {
std::ifstream ifs(animobjs[i].c_str());
if (ifs) {
std::stringstream ss;
ss << ifs.rdbuf();
ifs.close();
printf("Reading %s\r", animobjs[i].c_str());
std::string str = ss.str();
Shape *shape = Shape::parseObj(str.c_str(), kCatmark);
if (shape->verts.size() != g_positions.size()) {
printf("Error: vertex count doesn't match.\n");
goto error;
}
g_animPositions.push_back(shape->verts);
delete shape;
} else {
printf("Error in reading %s\n", animobjs[i].c_str());
goto error;
}
}
printf("\n");
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
if (diffuseEnvironmentMap) {
HdrInfo info;
unsigned char * image = loadHdr(diffuseEnvironmentMap, &info, /*convertToFloat=*/true);
if (image) {
glGenTextures(1, &g_diffuseEnvironmentMap);
glBindTexture(GL_TEXTURE_2D, g_diffuseEnvironmentMap);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, info.width, info.height,
0, GL_RGBA, GL_FLOAT, image);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_2D, 0);
free(image);
}
}
if (specularEnvironmentMap) {
HdrInfo info;
unsigned char * image = loadHdr(specularEnvironmentMap, &info, /*convertToFloat=*/true);
if (image) {
glGenTextures(1, &g_specularEnvironmentMap);
glBindTexture(GL_TEXTURE_2D, g_specularEnvironmentMap);
// glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); // deprecated
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, info.width, info.height,
0, GL_RGBA, GL_FLOAT, image);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_2D, 0);
free(image);
}
}
if (diffuseEnvironmentMap || specularEnvironmentMap) {
createSky();
}
fitFrame();
while (g_running) {
idle();
display();
glfwPollEvents();
glfwSwapBuffers(g_window);
glFinish();
}
error:
uninitGL();
glfwTerminate();
}