// // 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 #else #include #endif #define GLFW_INCLUDE_GL3 #define GLFW_NO_GLU #else #include #include #if defined(WIN32) #include #endif #endif #include GLFWwindow* g_window = 0; GLFWmonitor* g_primary = 0; #include #include #include #include #include #include #include #include #include #include #ifdef OPENSUBDIV_HAS_OPENMP #include #endif #ifdef OPENSUBDIV_HAS_TBB #include #endif #ifdef OPENSUBDIV_HAS_OPENCL #include #include #include "../common/clDeviceContext.h" CLDeviceContext g_clDeviceContext; #endif #ifdef OPENSUBDIV_HAS_CUDA #include #include #include "../common/cudaDeviceContext.h" CudaDeviceContext g_cudaDeviceContext; #endif #ifdef OPENSUBDIV_HAS_GLSL_TRANSFORM_FEEDBACK #include #include #endif #ifdef OPENSUBDIV_HAS_GLSL_COMPUTE #include #include #endif #include OpenSubdiv::Osd::GLMeshInterface *g_mesh; #include "Ptexture.h" #include "PtexUtils.h" #include "../../regression/common/far_utils.h" #include "../common/stopwatch.h" #include "../common/simple_math.h" #include "../common/glControlMeshDisplay.h" #include "../common/glHud.h" #include "../common/glUtils.h" #include "../common/hdr_reader.h" #include "../common/glPtexMipmapTexture.h" #include "../common/glShaderCache.h" #include 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_CONTROL_MESH_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_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; float g_modelViewProjection[16]; int g_prev_x = 0, g_prev_y = 0; // viewport int g_width = 1024, g_height = 1024; GLhud g_hud; GLControlMeshDisplay g_controlMeshDisplay; // 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 g_positions, g_normals; std::vector > g_animPositions; GLuint g_queries[2] = {0, 0}; GLuint g_vao = 0; GLuint g_skyVAO = 0; GLuint g_edgeIndexBuffer = 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 calcNormals(OpenSubdiv::Far::TopologyRefiner * refiner, std::vector const & pos, std::vector & result ) { typedef OpenSubdiv::Far::ConstIndexArray IndexArray; OpenSubdiv::Far::TopologyLevel const & refBaseLevel = refiner->GetLevel(0); // calc normal vectors int nverts = refBaseLevel.GetNumVertices(), nfaces = refBaseLevel.GetNumFaces(); for (int face = 0; face < nfaces; ++face) { IndexArray fverts = refBaseLevel.GetFaceVertices(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 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 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; iverts[i] = vp[i]; } shape->nvertsPerFace.resize(nvc); for (int i=0; invertsPerFace[i] = vc[i]; } shape->faceverts.resize(nvi); for (int i=0; ifaceverts[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); glBindTexture(GL_TEXTURE_2D, 0); GLUtils::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 { 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, "OsdPatchParamBuffer")) != -1) { glUniform1i(loc, 0); // GL_TEXTURE0 } // 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) { GLUtils::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; // create Far mesh (topology) OpenSubdiv::Sdc::SchemeType sdctype = GetSdcType(*shape); OpenSubdiv::Sdc::Options sdcoptions = GetSdcOptions(*shape); OpenSubdiv::Far::TopologyRefiner * refiner = OpenSubdiv::Far::TopologyRefinerFactory::Create(*shape, OpenSubdiv::Far::TopologyRefinerFactory::Options(sdctype, sdcoptions)); // save coarse topology (used for coarse mesh drawing) g_controlMeshDisplay.SetTopology(refiner->GetLevel(0)); 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( refiner, numVertexElements, numVaryingElements, level, bits); #ifdef OPENSUBDIV_HAS_OPENMP } else if (kernel == kOPENMP) { g_mesh = new OpenSubdiv::Osd::Mesh( refiner, numVertexElements, numVaryingElements, level, bits); #endif #ifdef OPENSUBDIV_HAS_TBB } else if (kernel == kTBB) { g_mesh = new OpenSubdiv::Osd::Mesh( refiner, numVertexElements, numVaryingElements, level, bits); #endif #ifdef OPENSUBDIV_HAS_OPENCL } else if (kernel == kCL) { static OpenSubdiv::Osd::EvaluatorCacheT clEvaluatorCache; g_mesh = new OpenSubdiv::Osd::Mesh( refiner, numVertexElements, numVaryingElements, level, bits, &clEvaluatorCache, &g_clDeviceContext); #endif #ifdef OPENSUBDIV_HAS_CUDA } else if (kernel == kCUDA) { g_mesh = new OpenSubdiv::Osd::Mesh( refiner, numVertexElements, numVaryingElements, level, bits); #endif #ifdef OPENSUBDIV_HAS_GLSL_TRANSFORM_FEEDBACK } else if (kernel == kGLSL) { static OpenSubdiv::Osd::EvaluatorCacheT glXFBEvaluatorCache; g_mesh = new OpenSubdiv::Osd::Mesh( refiner, numVertexElements, numVaryingElements, level, bits, &glXFBEvaluatorCache); #endif #ifdef OPENSUBDIV_HAS_GLSL_COMPUTE } else if (kernel == kGLSLCompute) { static OpenSubdiv::Osd::EvaluatorCacheT glComputeEvaluatorCache; g_mesh = new OpenSubdiv::Osd::Mesh( 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->GetPatchTable()->GetPatchIndexBuffer()); glBindVertexArray(0); } //------------------------------------------------------------------------------ void createSky() { const int U_DIV = 20; const int V_DIV = 20; std::vector vbo; std::vector 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); // save mvp for the control mesh drawing memcpy(g_modelViewProjection, constantData.ModelViewProjectionMatrix, 16*sizeof(float)); // 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(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 (g_mesh->GetPatchTable()->GetPatchParamTextureBuffer()) { glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_BUFFER, g_mesh->GetPatchTable()->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::PatchArray const & patch) { EffectDesc effectDesc(patch.GetDescriptor(), effect); GLDrawConfig *config = g_shaderCache.GetDrawConfig(effectDesc); if (!config) return 0; GLuint program = config->GetProgram(); glUseProgram(program); // bind standalone uniforms GLint uniformPrimitiveIdBase = glGetUniformLocation(program, "PrimitiveIdBase"); if (uniformPrimitiveIdBase >= 0) glUniform1i(uniformPrimitiveIdBase, patch.GetPrimitiveIdBase()); 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::PatchArrayVector const & patches = g_mesh->GetPatchTable()->GetPatchArrays(); for (int i = 0; i < (int)patches.size(); ++i) { OpenSubdiv::Osd::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.GetNumPatches() * patch.GetDescriptor().GetNumControlVertices(), GL_UNSIGNED_INT, (void *)(patch.GetIndexBase() * 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); GLUtils::CheckGLErrors("draw model"); } //------------------------------------------------------------------------------ void display() { Stopwatch s; s.Start(); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glViewport(0, 0, g_width, g_height); g_hud.FillBackground(); 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 // draw the control mesh { GLuint vbo = g_mesh->BindVertexBuffer(); int stride = g_adaptive ? 3 : 6; g_controlMeshDisplay.Draw(vbo, stride*sizeof(float), g_modelViewProjection); } 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(); 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(); GLUtils::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_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 (GLUtils::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_CONTROL_MESH_EDGES: g_controlMeshDisplay.SetEdgesDisplay(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': GLUtils::WriteScreenshot(g_width, g_height); 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_skyVAO); glBindTexture(GL_TEXTURE_2D, 0); } //------------------------------------------------------------------------------ void usage(const char *program) { printf("Usage: %s [options] [] [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 : diffuse environment map for IBL\n"); printf(" -e : specular environment map for IBL\n"); printf(" -s : custom shader file\n"); printf(" -y : Y-up model\n"); printf(" -m level : max mimmap level (default=10)\n"); printf(" -x : ptex target memory size\n"); printf(" --disp : 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); } //------------------------------------------------------------------------------ int main(int argc, char ** argv) { std::vector 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; GLUtils::SetMinimumGLVersion(); 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))) { std::cerr << "Failed to create OpenGL context.\n"; glfwTerminate(); return 1; } glfwMakeContextCurrent(g_window); GLUtils::PrintGLVersion(); 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 = GLUtils::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_controlMeshDisplay.SetEdgesDisplay(false); 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("Control edges (H)", g_controlMeshDisplay.GetEdgesDisplay(), 10, 10, callbackCheckBox, HUD_CB_CONTROL_MESH_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 (GLUtils::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 if (GLUtils::GL_ARBComputeShaderOrGL_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(); }