// // 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; #if _MSC_VER #define snprintf _snprintf #endif #include #include #include #include #include #include #include #include #include "hbr_refine.h" #include "../common/stopwatch.h" #include "../common/simple_math.h" #include "../common/gl_common.h" #include "../common/gl_hud.h" #include "init_shapes.h" #include "gl_mesh.h" #include "gl_fontutils.h" #include #include #include #include #include #include //------------------------------------------------------------------------------ int g_level = 3, g_currentShape = 7; enum HudCheckBox { kHUD_CB_DISPLAY_CAGE_EDGES, kHUD_CB_DISPLAY_CAGE_VERTS, kHUD_CB_ANIMATE_VERTICES, kHUD_CB_DISPLAY_PATCH_COLOR }; enum DrawMode { kDRAW_NONE = 0, kDRAW_VERTICES, kDRAW_WIREFRAME, kDRAW_FACES }; int g_frame = 0, g_repeatCount = 0; // GUI variables int g_fullscreen = 0, g_mbutton[3] = {0, 0, 0}, g_running = 1; int g_displayPatchColor = 1, g_drawCageEdges = 1, g_drawCageVertices = 0, g_HbrDrawMode = kDRAW_WIREFRAME, g_HbrDrawVertIDs = false, g_HbrDrawEdgeSharpness = false, g_HbrDrawFaceIDs = false, g_HbrDrawPtexIDs = false, g_VtrDrawMode = kDRAW_NONE, g_VtrDrawVertIDs = false, g_VtrDrawEdgeIDs = false, g_VtrDrawFaceIDs = false, g_VtrDrawPtexIDs = false, g_VtrDrawEdgeSharpness = false, g_VtrDrawGregogyBasis = false, g_VtrDrawFVarVerts = false, g_VtrDrawFVarPatches = false, g_VtrDrawFVarPatchTess = 5, g_numPatches = 0, g_maxValence = 0, g_currentPatch = 0, g_Adaptive = true, g_useStencils = true; typedef OpenSubdiv::Sdc::Options SdcOptions; SdcOptions::FVarLinearInterpolation g_fvarInterpolation = SdcOptions::FVAR_LINEAR_ALL; OpenSubdiv::Far::PatchDescriptor g_currentPatchDesc; OpenSubdiv::Far::PatchDescriptor::Type g_currentFVarPatchType; 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; int g_width = 1024, g_height = 1024; GLhud g_hud; GLFont * g_font=0; // performance float g_cpuTime = 0; float g_gpuTime = 0; Stopwatch g_fpsTimer; GLuint g_queries[2] = {0, 0}; GLuint g_transformUB = 0, g_lightingUB = 0; struct Transform { float ModelViewMatrix[16]; float ProjectionMatrix[16]; float ModelViewProjectionMatrix[16]; } g_transformData; static GLMesh g_base_glmesh, g_hbr_glmesh, g_vtr_glmesh; //------------------------------------------------------------------------------ typedef OpenSubdiv::Far::TopologyRefiner FTopologyRefiner; typedef OpenSubdiv::Far::TopologyRefinerFactory FTopologyRefinerFactory; //------------------------------------------------------------------------------ // generate display IDs for Hbr faces static void createFaceNumbers(std::vector faces, bool doPtex=false) { static char buf[16]; for (int i=0; i<(int)faces.size(); ++i) { Hface const * f = faces[i]; Vertex center(0.0f, 0.0f, 0.0f); int nv = f->GetNumVertices(); float weight = 1.0f / nv; for (int j=0; jGetVertex(j)->GetData(), weight); } if (doPtex) { snprintf(buf, 16, "%d", f->GetPtexIndex()); } else { snprintf(buf, 16, "%d", f->GetID()); } g_font->Print3D(center.GetPos(), buf, 2); } } //------------------------------------------------------------------------------ // generate display IDs for Hbr edges static void createEdgeNumbers(std::vector faces) { typedef std::map EdgeMap; EdgeMap edgeMap; typedef std::vector EdgeVec; EdgeVec edgeVec; { // map half-edges into unique edge id's for (int i=0; i<(int)faces.size(); ++i) { Hface const * f = faces[i]; for (int j=0; jGetNumVertices(); ++j) { Hhalfedge const * e = f->GetEdge(j); if (e->IsBoundary() or (e->GetRightFace()->GetID()>f->GetID())) { int id = (int)edgeMap.size(); edgeMap[e] = id; } } } edgeVec.resize(edgeMap.size()); for (EdgeMap::const_iterator it=edgeMap.begin(); it!=edgeMap.end(); ++it) { edgeVec[it->second] = it->first; } } static char buf[16]; for (int i=0; i<(int)edgeVec.size(); ++i) { Hhalfedge const * e = edgeVec[i]; float sharpness = e->GetSharpness(); if (sharpness>0.0f) { Vertex center(0.0f, 0.0f, 0.0f); center.AddWithWeight(e->GetOrgVertex()->GetData(), 0.5f); center.AddWithWeight(e->GetDestVertex()->GetData(), 0.5f); snprintf(buf, 16, "%g", sharpness); g_font->Print3D(center.GetPos(), buf, std::min(8,(int)sharpness+4)); } } } //------------------------------------------------------------------------------ // generate display IDs for Hbr verts static void createVertNumbers(std::vector faces) { assert(not faces.empty()); static char buf[16]; std::vector verts(faces.size()*4, 0); for (int i=0; i<(int)faces.size(); ++i) { Hface const * f = faces[i]; int nv = f->GetNumVertices(); for (int j=0; jGetVertex(j); verts[v->GetID()] = v; } } for (int i=0; i<(int)verts.size(); ++i) { if (verts[i]) { snprintf(buf, 16, "%d", verts[i]->GetID()); g_font->Print3D(verts[i]->GetData().GetPos(), buf, 1); } } } //------------------------------------------------------------------------------ static void createHbrMesh(Shape * shape, int maxlevel) { Stopwatch s; s.Start(); // create Hbr mesh using functions from hbr_utils Hmesh * hmesh = createMesh(shape->scheme, /*fvarwidth*/ 0); createVerticesWithPositions(shape, hmesh); createTopology(shape, hmesh, shape->scheme); s.Stop(); std::vector coarseFaces, // list of Hbr coarse faces refinedFaces; // list of Hbr faces refined at maxlevel int nfaces = hmesh->GetNumFaces(); { // create control cage GL mesh coarseFaces.resize(nfaces); for (int i=0; iGetFace(i); } GLMesh::Options coarseOptions; coarseOptions.vertColorMode=GLMesh::VERTCOLOR_BY_SHARPNESS; coarseOptions.edgeColorMode=GLMesh::EDGECOLOR_BY_SHARPNESS; coarseOptions.faceColorMode=GLMesh::FACECOLOR_SOLID; g_base_glmesh.Initialize(coarseOptions, coarseFaces); g_base_glmesh.InitializeDeviceBuffers(); } { // create maxlevel refined GL mesh s.Start(); OpenSubdiv::Far::PatchTables const * patchTables = 0; if (g_Adaptive) { int maxvalence = RefineAdaptive(*hmesh, maxlevel, refinedFaces); patchTables = CreatePatchTables(*hmesh, maxvalence); patchTables->GetNumPatchesTotal(); delete patchTables; } else { RefineUniform(*hmesh, maxlevel, refinedFaces); } s.Stop(); //printf("Hbr time: %f ms\n", float(s.GetElapsed())*1000.0f); if (g_HbrDrawVertIDs) { createVertNumbers(refinedFaces); } // Hbr is a half-edge rep, so edges do not have unique IDs that // can be displayed if (g_HbrDrawEdgeSharpness) { createEdgeNumbers(refinedFaces); } if (g_HbrDrawFaceIDs) { createFaceNumbers(refinedFaces, /*ptex*/ false); } if (g_HbrDrawPtexIDs) { createFaceNumbers(refinedFaces, /*ptex*/ true); } GLMesh::Options refinedOptions; refinedOptions.vertColorMode=GLMesh::VERTCOLOR_BY_SHARPNESS; refinedOptions.edgeColorMode=GLMesh::EDGECOLOR_BY_SHARPNESS; refinedOptions.faceColorMode=GLMesh::FACECOLOR_SOLID; g_hbr_glmesh.Initialize(refinedOptions, refinedFaces); g_hbr_glmesh.SetDiffuseColor(1.0f,0.75f,0.9f, 1.0f); } g_hbr_glmesh.InitializeDeviceBuffers(); delete hmesh; } //------------------------------------------------------------------------------ // generate display IDs for Vtr verts static void createVertNumbers(OpenSubdiv::Far::TopologyRefiner const & refiner, std::vector const & vertexBuffer) { int maxlevel = refiner.GetMaxLevel(), firstvert = 0; if (refiner.IsUniform()) { for (int i=0; iPrint3D(vertexBuffer[i].GetPos(), buf, 1); } } else { for (int level=0, vert=0; level<=refiner.GetMaxLevel(); ++level) { for (int i=0; iPrint3D(vertexBuffer[vert].GetPos(), buf, 1); } } } } //------------------------------------------------------------------------------ // generate display IDs for Vtr edges static void createEdgeNumbers(OpenSubdiv::Far::TopologyRefiner const & refiner, std::vector const & vertexBuffer, bool ids=false, bool sharpness=false) { if (ids or sharpness) { int maxlevel = refiner.GetMaxLevel(), firstvert = 0; for (int i=0; iPrint3D(center.GetPos(), buf, 3); } if (sharpness) { float sharpness = refiner.GetEdgeSharpness(maxlevel, i); if (sharpness>0.0f) { snprintf(buf, 16, "%g", sharpness); g_font->Print3D(center.GetPos(), buf, std::min(8,(int)sharpness+4)); } } } } } //------------------------------------------------------------------------------ // generate display IDs for Vtr faces static void createFaceNumbers(OpenSubdiv::Far::TopologyRefiner const & refiner, std::vector const & vertexBuffer) { static char buf[16]; if (refiner.IsUniform()) { int maxlevel = refiner.GetMaxLevel(), firstvert = 0; for (int i=0; iPrint3D(center.GetPos(), buf, 2); } } else { int maxlevel = refiner.GetMaxLevel(), // patch = refiner.GetNumFaces(0), firstvert = refiner.GetNumVertices(0); for (int level=1; level<=maxlevel; ++level) { int nfaces = refiner.GetNumFaces(level); for (int face=0; facePrint3D(center.GetPos(), buf, 2); } firstvert+=refiner.GetNumVertices(level); } } } //------------------------------------------------------------------------------ // generate display vert IDs for the selected Vtr patch static void createPatchNumbers(OpenSubdiv::Far::PatchTables const & patchTables, std::vector const & vertexBuffer) { if (not g_currentPatch) return; int patchID = g_currentPatch-1, patchArray = -1; // Find PatchArray containing our patch for (int array=0; array<(int)patchTables.GetNumPatchArrays(); ++array) { int npatches = patchTables.GetNumPatches(array); if (patchID >= npatches) { patchID -= npatches; } else { patchArray = array; break; } } if (patchArray==-1) { return; } g_currentPatchDesc = patchTables.GetPatchArrayDescriptor(patchArray); OpenSubdiv::Far::ConstIndexArray const cvs = patchTables.GetPatchVertices(patchArray, patchID); static char buf[16]; for (int i=0; iPrint3D(vertexBuffer[cvs[i]].GetPos(), buf, 1); } } //------------------------------------------------------------------------------ // generate display vert IDs for the selected Vtr FVar patch static void createFVarPatchNumbers(OpenSubdiv::Far::PatchTables const & patchTables, std::vector const & fvarBuffer) { static int channel = 0; int patch = g_currentPatch-1; static char buf[16]; if (patch>=0 and patchPrint3D(fvarBuffer[cvs[i]].GetPos(), buf, 2); } g_currentFVarPatchType = patchTables.GetFVarPatchType(channel, handle); } } //------------------------------------------------------------------------------ // generate display for Vtr FVar patches static GLMesh fvarVerts, fvarWire; static void createFVarPatches(OpenSubdiv::Far::TopologyRefiner const & refiner, OpenSubdiv::Far::PatchTables const & patchTables, std::vector const & fvarBuffer) { assert(not fvarBuffer.empty()); static int channel = 0; if (g_VtrDrawFVarVerts) { GLMesh::Options options; options.vertColorMode = GLMesh::VERTCOLOR_BY_LEVEL; fvarVerts.InitializeFVar(options, refiner, &patchTables, channel, 0, (float *)(&fvarBuffer[0])); } if (g_VtrDrawFVarPatches) { // generate uniform tessellation for patches int tessFactor = g_VtrDrawFVarPatchTess, npatches = patchTables.GetNumPatchesTotal(), nvertsperpatch = (tessFactor) * (tessFactor), nverts = npatches * nvertsperpatch; float * uvs = (float *)alloca(tessFactor); for (int i=0; i verts(nverts); memset(&verts[0], 0, verts.size()*sizeof(Vertex)); OpenSubdiv::Far::PatchTables::PatchHandle handle; Vertex * vert = &verts[0]; for (int patch=0; patch const & vertexBuffer) { typedef OpenSubdiv::Far::PatchDescriptor PatchDescriptor; int npatches = 0; int patchArray = 0; for (int array=0; array<(int)patchTables.GetNumPatchArrays(); ++array) { if (patchTables.GetPatchArrayDescriptor(array).GetType()== PatchDescriptor::GREGORY_BASIS) { npatches = patchTables.GetNumPatches(array); patchArray = array; break; } } int nedges = npatches * 20; std::vector vertsperedge(nedges), edgeindices(nedges*2); for (int patch=0; patchPrint3D(P, buf, 3); snprintf(buf, 16, " Ep%d (%d)", i, vid+1); g_font->Print3D(Ep, buf, 3); snprintf(buf, 16, " Em%d (%d)", i, vid+2); g_font->Print3D(Em, buf, 3); snprintf(buf, 16, " Fp%d (%d)", i, vid+3); g_font->Print3D(Fp, buf, 3); snprintf(buf, 16, " Fm%d (%d)", i, vid+4); g_font->Print3D(Fm, buf, 3); } } GLMesh::Options options; gregoryWire.Initialize(options, (int)vertexBuffer.size(), (int)vertsperedge.size(), &vertsperedge[0], &edgeindices[0], (float const *)&vertexBuffer[0]); } //------------------------------------------------------------------------------ // generate display IDs for Vtr faces static void createPtexNumbers(OpenSubdiv::Far::PatchTables const & patchTables, std::vector const & vertexBuffer) { typedef OpenSubdiv::Far::PatchDescriptor Descriptor; static char buf[16]; static int regular[4] = {5, 6, 9, 10}, gregory[4] = {0, 1, 2, 3}; for (int array=0; array<(int)patchTables.GetNumPatchArrays(); ++array) { for (int patch=0; patch<(int)patchTables.GetNumPatches(array); ++patch) { OpenSubdiv::Far::ConstIndexArray const cvs = patchTables.GetPatchVertices(array, patch); int * remap = 0; switch (patchTables.GetPatchArrayDescriptor(array).GetType()) { case Descriptor::REGULAR: remap = regular; break; case Descriptor::GREGORY: case Descriptor::GREGORY_BOUNDARY: case Descriptor::GREGORY_BASIS: remap = gregory; break; default: assert(0); } Vertex center(0.0f, 0.0f, 0.0f); for (int k=0; k<4; ++k) { center.AddWithWeight(vertexBuffer[cvs[remap[k]]], 0.25f); } snprintf(buf, 16, "%d", patchTables.GetPatchParam(array, patch).faceIndex); g_font->Print3D(center.GetPos(), buf, 1); } } } //------------------------------------------------------------------------------ static void createVtrMesh(Shape * shape, int maxlevel) { Stopwatch s; s.Start(); using namespace OpenSubdiv; // create Vtr mesh (topology) Sdc::SchemeType sdctype = GetSdcType(*shape); Sdc::Options sdcoptions = GetSdcOptions(*shape); sdcoptions.SetFVarLinearInterpolation(g_fvarInterpolation); Far::TopologyRefiner * refiner = Far::TopologyRefinerFactory::Create(*shape, Far::TopologyRefinerFactory::Options(sdctype, sdcoptions)); if (g_Adaptive) { Far::TopologyRefiner::AdaptiveOptions options(maxlevel); options.useSingleCreasePatch = false; refiner->RefineAdaptive(options); } else { Far::TopologyRefiner::UniformOptions options(maxlevel); options.fullTopologyInLastLevel = true; refiner->RefineUniform(options); } int numTotalVerts = refiner->GetNumVerticesTotal(); // // Patch tables // std::vector fvarBuffer; Far::PatchTables * patchTables = 0; bool createFVarWire = g_VtrDrawFVarPatches or g_VtrDrawFVarVerts; if (g_Adaptive) { Far::PatchTablesFactory::Options options; options.generateFVarTables = createFVarWire; options.shareEndCapPatchPoints = false; patchTables = Far::PatchTablesFactory::Create(*refiner, options); // increase vertex buffer for the additional endcap verts if (patchTables->GetEndCapVertexStencilTables()) { numTotalVerts += patchTables->GetEndCapVertexStencilTables()->GetNumStencils(); } g_numPatches = patchTables->GetNumPatchesTotal(); g_maxValence = patchTables->GetMaxValence(); if (createFVarWire) { // interpolate fvar values //Far::FVarPatchTables const * fvarTables = // patchTables->GetFVarPatchTables(); //assert(fvarTables); int channel = 0; // XXXX should use a (u,v) vertex class fvarBuffer.resize(refiner->GetNumFVarValuesTotal(channel), 0); Vertex * values = &fvarBuffer[0]; int nCoarseValues = refiner->GetNumFVarValues(0); for (int i=0; iuvs[i*2]; values[i].SetPosition(ptr[0], ptr[1], 0.0f); } refiner->InterpolateFaceVarying(values, values + nCoarseValues); } } // // interpolate vertices // // create vertex primvar data buffer std::vector vertexBuffer(numTotalVerts); Vertex * verts = &vertexBuffer[0]; // copy coarse vertices positions int ncoarseverts = shape->GetNumVertices(); for (int i=0; iverts[i*3]; verts[i].SetPosition(ptr[0], ptr[1], ptr[2]); } s.Start(); if (g_useStencils) { // // Stencil interpolation // Far::StencilTables const * stencilTables = 0; Far::StencilTablesFactory::Options options; options.generateOffsets=true; options.generateIntermediateLevels=true; stencilTables = Far::StencilTablesFactory::Create(*refiner, options); // append endpatch stencils if needed if (patchTables->GetEndCapVertexStencilTables()) { if (Far::StencilTables const * stencilTablesWithEndCap = Far::StencilTablesFactory::AppendEndCapStencilTables( *refiner, stencilTables, patchTables->GetEndCapVertexStencilTables())) { delete stencilTables; stencilTables = stencilTablesWithEndCap; } } // // apply stencils // stencilTables->UpdateValues(verts, verts + ncoarseverts); delete stencilTables; } else { // // TopologyRefiner interpolation // // populate buffer with Vtr interpolated vertex data refiner->Interpolate(verts, verts + ncoarseverts); //printf(" %f ms (interpolate)\n", float(s.GetElapsed())*1000.0f); //printf(" %f ms (total)\n", float(s.GetTotalElapsed())*1000.0f); // TODO: endpatch basis conversion comes here } s.Stop(); // // Misc display // //printf("Vtr time: %f ms (topology)\n", float(s.GetElapsed())*1000.0f); if (g_VtrDrawVertIDs) { createVertNumbers(*refiner, vertexBuffer); } if (g_VtrDrawFaceIDs) { createFaceNumbers(*refiner, vertexBuffer); } if (g_VtrDrawPtexIDs and patchTables) { createPtexNumbers(*patchTables, vertexBuffer); } if (g_Adaptive) { createPatchNumbers(*patchTables, vertexBuffer); } if (g_Adaptive and g_VtrDrawGregogyBasis) { createGregoryBasis(*patchTables, vertexBuffer); } if (g_Adaptive and createFVarWire) { createFVarPatches(*refiner, *patchTables, fvarBuffer); createFVarPatchNumbers(*patchTables, fvarBuffer); } createEdgeNumbers(*refiner, vertexBuffer, g_VtrDrawEdgeIDs!=0, g_VtrDrawEdgeSharpness!=0); GLMesh::Options options; options.vertColorMode=g_Adaptive ? GLMesh::VERTCOLOR_BY_LEVEL : GLMesh::VERTCOLOR_BY_SHARPNESS; options.edgeColorMode=g_Adaptive ? GLMesh::EDGECOLOR_BY_PATCHTYPE : GLMesh::EDGECOLOR_BY_SHARPNESS; options.faceColorMode=g_Adaptive ? GLMesh::FACECOLOR_BY_PATCHTYPE :GLMesh::FACECOLOR_SOLID; if (g_Adaptive) { g_vtr_glmesh.Initialize(options, *refiner, patchTables, (float *)&verts[0]); g_vtr_glmesh.SetDiffuseColor(1.0f, 1.0f, 1.0f, 1.0f); } else { g_vtr_glmesh.Initialize(options, *refiner, patchTables, (float *)&verts[0]); g_vtr_glmesh.SetDiffuseColor(0.75f, 0.9f, 1.0f, 1.0f); } //setFaceColors(*refiner); g_vtr_glmesh.InitializeDeviceBuffers(); delete refiner; delete patchTables; } //------------------------------------------------------------------------------ static void createMeshes(ShapeDesc const & desc, int maxlevel) { if (not g_font) { g_font = new GLFont(g_hud.GetFontTexture()); } g_font->Clear(); Shape * shape = Shape::parseObj(desc.data.c_str(), desc.scheme); createHbrMesh(shape, maxlevel); createVtrMesh(shape, maxlevel); delete shape; } //------------------------------------------------------------------------------ static void fitFrame() { g_pan[0] = g_pan[1] = 0; g_dolly = g_size; } //------------------------------------------------------------------------------ static void display() { g_hud.GetFrameBuffer()->Bind(); Stopwatch s; s.Start(); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glViewport(0, 0, g_width, g_height); // prepare view matrix double aspect = g_width/(double)g_height; identity(g_transformData.ModelViewMatrix); translate(g_transformData.ModelViewMatrix, -g_pan[0], -g_pan[1], -g_dolly); rotate(g_transformData.ModelViewMatrix, g_rotate[1], 1, 0, 0); rotate(g_transformData.ModelViewMatrix, g_rotate[0], 0, 1, 0); rotate(g_transformData.ModelViewMatrix, -90, 1, 0, 0); translate(g_transformData.ModelViewMatrix, -g_center[0], -g_center[1], -g_center[2]); perspective(g_transformData.ProjectionMatrix, 45.0f, (float)aspect, 0.1f, 500.0f); multMatrix(g_transformData.ModelViewProjectionMatrix, g_transformData.ModelViewMatrix, g_transformData.ProjectionMatrix); glEnable(GL_DEPTH_TEST); s.Stop(); float drawCpuTime = float(s.GetElapsed() * 1000.0f); glBindVertexArray(0); glUseProgram(0); // primitive counting glBeginQuery(GL_PRIMITIVES_GENERATED, g_queries[0]); #if defined(GL_VERSION_3_3) glBeginQuery(GL_TIME_ELAPSED, g_queries[1]); #endif // Update and bind transform state --------------------- if (! g_transformUB) { glGenBuffers(1, &g_transformUB); glBindBuffer(GL_UNIFORM_BUFFER, g_transformUB); glBufferData(GL_UNIFORM_BUFFER, sizeof(g_transformData), NULL, GL_STATIC_DRAW); }; glBindBuffer(GL_UNIFORM_BUFFER, g_transformUB); glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(g_transformData), &g_transformData); glBindBuffer(GL_UNIFORM_BUFFER, 0); // Update and bind lighting state ---------------------- struct Lighting { struct Light { float position[4]; float ambient[4]; float diffuse[4]; float specular[4]; } lightSource[2]; } lightingData = { {{ { 0.5, 0.2f, 1.0f, 0.0f }, { 0.1f, 0.1f, 0.1f, 1.0f }, { 0.7f, 0.7f, 0.7f, 1.0f }, { 0.8f, 0.8f, 0.8f, 1.0f } }, { { -0.8f, 0.4f, -1.0f, 0.0f }, { 0.0f, 0.0f, 0.0f, 1.0f }, { 0.5f, 0.5f, 0.5f, 1.0f }, { 0.8f, 0.8f, 0.8f, 1.0f } }} }; if (! g_lightingUB) { glGenBuffers(1, &g_lightingUB); glBindBuffer(GL_UNIFORM_BUFFER, g_lightingUB); glBufferData(GL_UNIFORM_BUFFER, sizeof(lightingData), NULL, GL_STATIC_DRAW); }; glBindBuffer(GL_UNIFORM_BUFFER, g_lightingUB); glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(lightingData), &lightingData); glBindBuffer(GL_UNIFORM_BUFFER, 0); // Draw stuff ------------------------------------------ // control cage edges & verts if (g_drawCageVertices) { g_base_glmesh.Draw(GLMesh::COMP_VERT, g_transformUB, g_lightingUB); } if (g_drawCageEdges) { g_base_glmesh.Draw(GLMesh::COMP_EDGE, g_transformUB, g_lightingUB); } // Hbr mesh if (g_HbrDrawMode!=kDRAW_NONE) { GLMesh::Component comp=GLMesh::COMP_VERT; switch (g_HbrDrawMode) { case kDRAW_VERTICES : comp=GLMesh::COMP_VERT; break; case kDRAW_WIREFRAME : comp=GLMesh::COMP_EDGE; break; case kDRAW_FACES : comp=GLMesh::COMP_FACE; break; default: assert(0); } g_hbr_glmesh.Draw(comp, g_transformUB, g_lightingUB); } // Vtr mesh if (g_VtrDrawMode!=kDRAW_NONE) { GLMesh::Component comp=GLMesh::COMP_VERT; switch (g_VtrDrawMode) { case kDRAW_VERTICES : comp=GLMesh::COMP_VERT; break; case kDRAW_WIREFRAME : comp=GLMesh::COMP_EDGE; break; case kDRAW_FACES : comp=GLMesh::COMP_FACE; break; default: assert(0); } g_vtr_glmesh.Draw(comp, g_transformUB, g_lightingUB); } if (g_Adaptive and g_VtrDrawGregogyBasis) { gregoryWire.Draw(GLMesh::COMP_VERT, g_transformUB, g_lightingUB); gregoryWire.Draw(GLMesh::COMP_EDGE, g_transformUB, g_lightingUB); } if (g_Adaptive and g_VtrDrawFVarVerts) { fvarVerts.Draw(GLMesh::COMP_VERT, g_transformUB, g_lightingUB); } if (g_Adaptive and g_VtrDrawFVarPatches) { fvarWire.Draw(GLMesh::COMP_EDGE, g_transformUB, g_lightingUB); } assert(g_font); g_font->Draw(g_transformUB); // ----------------------------------------------------- g_hud.GetFrameBuffer()->ApplyImageShader(); 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; if (g_hud.IsVisible()) { g_fpsTimer.Stop(); double fps = 1.0/g_fpsTimer.GetElapsed(); g_fpsTimer.Start(); { // display selected patch info static char const * patchTypes[13] = { "undefined", "points", "lines", "quads", "tris", "loop", "regular", "single crease", "boundary", "corner", "gregory", "gregory-boundary", "gregory-basis" }, * format0 = "Current Patch : %d/%d (%s - %d CVs)", * format1 = "Current Patch : %d/%d (%s - %d CVs) fvar: (%s - %d CVs)"; if (g_Adaptive and g_currentPatch) { if (g_VtrDrawFVarPatches or g_VtrDrawFVarVerts) { g_hud.DrawString(g_width/2-200, 225, format1, g_currentPatch-1, g_numPatches-1, patchTypes[g_currentPatchDesc.GetType()], g_currentPatchDesc.GetNumControlVertices(), patchTypes[g_currentFVarPatchType], OpenSubdiv::Far::PatchDescriptor::GetNumFVarControlVertices( g_currentFVarPatchType)); } else { g_hud.DrawString(g_width/2-200, 225, format0, g_currentPatch-1, g_numPatches-1, patchTypes[g_currentPatchDesc.GetType()], g_currentPatchDesc.GetNumControlVertices(), patchTypes[g_currentFVarPatchType]); } } } static char const * schemeNames[3] = { "BILINEAR", "CATMARK", "LOOP" }; g_hud.DrawString(10, -140, "Primitives : %d", numPrimsGenerated); g_hud.DrawString(10, -120, "Scheme : %s", schemeNames[ g_shapes[g_currentShape].scheme ]); 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", fps); g_hud.Flush(); } glFinish(); //checkGLErrors("display leave"); } //------------------------------------------------------------------------------ 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; } //------------------------------------------------------------------------------ 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; if (button < 3) { g_mbutton[button] = (state == GLFW_PRESS); } } //------------------------------------------------------------------------------ static 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); } //------------------------------------------------------------------------------ void windowClose(GLFWwindow*) { g_running = false; } //------------------------------------------------------------------------------ static void toggleFullScreen() { // XXXX manuelk : to re-implement from glut } //------------------------------------------------------------------------------ static void rebuildOsdMeshes() { createMeshes(g_shapes[ g_currentShape ], g_level); } //------------------------------------------------------------------------------ static 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 'F': fitFrame(); break; case '[': if (g_currentPatch > 0) { --g_currentPatch; rebuildOsdMeshes(); } break; case ']': if (g_currentPatch < g_numPatches) { ++g_currentPatch; rebuildOsdMeshes(); } break; case GLFW_KEY_TAB: toggleFullScreen(); break; case GLFW_KEY_ESCAPE: g_hud.SetVisible(!g_hud.IsVisible()); break; } } static void callbackLevel(int l) { g_level = l; rebuildOsdMeshes(); } static void callbackModel(int m) { if (m < 0) m = 0; if (m >= (int)g_shapes.size()) m = (int)g_shapes.size() - 1; g_currentShape = m; rebuildOsdMeshes(); } static void callbackAdaptive(bool checked, int /* a */) { g_Adaptive = checked; rebuildOsdMeshes(); } static void callbackUseStencils(bool checked, int /* a */) { g_useStencils = checked; rebuildOsdMeshes(); } static void callbackCheckBox(bool checked, int button) { switch (button) { case kHUD_CB_DISPLAY_CAGE_EDGES : g_drawCageEdges = checked; break; case kHUD_CB_DISPLAY_CAGE_VERTS : g_drawCageVertices = checked; break; case kHUD_CB_DISPLAY_PATCH_COLOR: g_displayPatchColor = checked; break; } } static void callbackHbrDrawMode(int m) { g_HbrDrawMode = m; } static void callbackVtrDrawMode(int m) { g_VtrDrawMode = m; } static void callbackDrawIDs(bool checked, int button) { switch (button) { case 0: g_HbrDrawVertIDs = checked; break; case 1: g_HbrDrawFaceIDs = checked; break; case 2: g_HbrDrawPtexIDs = checked; break; case 3: g_HbrDrawEdgeSharpness = checked; break; case 4: g_VtrDrawVertIDs = checked; break; case 5: g_VtrDrawEdgeIDs = checked; break; case 6: g_VtrDrawFaceIDs = checked; break; case 7: g_VtrDrawPtexIDs = checked; break; case 8: g_VtrDrawEdgeSharpness = checked; break; case 9: g_VtrDrawGregogyBasis = checked; break; case 10: g_VtrDrawFVarVerts = checked; break; case 11: g_VtrDrawFVarPatches = checked; break; default: break; } rebuildOsdMeshes(); } static void callbackScale(float value, int) { g_font->SetFontScale(value); } static void callbackFVarTess(float value, int) { g_VtrDrawFVarPatchTess = (int)value; rebuildOsdMeshes(); } static void callbackFVarInterpolation(int b) { switch (b) { case SdcOptions::FVAR_LINEAR_NONE : g_fvarInterpolation = SdcOptions::FVAR_LINEAR_NONE; break; case SdcOptions::FVAR_LINEAR_CORNERS_ONLY : g_fvarInterpolation = SdcOptions::FVAR_LINEAR_CORNERS_ONLY; break; case SdcOptions::FVAR_LINEAR_CORNERS_PLUS1 : g_fvarInterpolation = SdcOptions::FVAR_LINEAR_CORNERS_PLUS1; break; case SdcOptions::FVAR_LINEAR_CORNERS_PLUS2 : g_fvarInterpolation = SdcOptions::FVAR_LINEAR_CORNERS_PLUS2; break; case SdcOptions::FVAR_LINEAR_BOUNDARIES : g_fvarInterpolation = SdcOptions::FVAR_LINEAR_BOUNDARIES; break; case SdcOptions::FVAR_LINEAR_ALL : g_fvarInterpolation = SdcOptions::FVAR_LINEAR_ALL; break; } rebuildOsdMeshes(); } //------------------------------------------------------------------------------ static void initHUD() { int windowWidth = g_width, windowHeight = g_height; int frameBufferWidth = g_width, frameBufferHeight = g_height; // window size might not match framebuffer size on a high DPI display glfwGetWindowSize(g_window, &windowWidth, &windowHeight); glfwGetFramebufferSize(g_window, &frameBufferWidth, &frameBufferHeight); g_hud.Init(windowWidth, windowHeight, frameBufferWidth, frameBufferHeight); g_hud.SetFrameBuffer(new GLFrameBuffer); g_hud.AddCheckBox("Cage Edges (e)", g_drawCageEdges != 0, 10, 10, callbackCheckBox, kHUD_CB_DISPLAY_CAGE_EDGES, 'e'); g_hud.AddCheckBox("Cage Verts (r)", g_drawCageVertices != 0, 10, 30, callbackCheckBox, kHUD_CB_DISPLAY_CAGE_VERTS, 'r'); int pulldown = g_hud.AddPullDown("Hbr Draw Mode (h)", 10, 75, 250, callbackHbrDrawMode, 'h'); g_hud.AddPullDownButton(pulldown, "None", 0, g_HbrDrawMode==kDRAW_NONE); g_hud.AddPullDownButton(pulldown, "Vertices", 1, g_HbrDrawMode==kDRAW_VERTICES); g_hud.AddPullDownButton(pulldown, "Wireframe", 2, g_HbrDrawMode==kDRAW_WIREFRAME); g_hud.AddPullDownButton(pulldown, "Faces", 3, g_HbrDrawMode==kDRAW_FACES); g_hud.AddCheckBox("Vert IDs", g_HbrDrawVertIDs!=0, 10, 95, callbackDrawIDs, 0); g_hud.AddCheckBox("Face IDs", g_HbrDrawFaceIDs!=0, 10, 115, callbackDrawIDs, 1); g_hud.AddCheckBox("Ptex IDs", g_HbrDrawPtexIDs!=0, 10, 135, callbackDrawIDs, 2); g_hud.AddCheckBox("Edge Sharp", g_HbrDrawEdgeSharpness!=0, 10, 155, callbackDrawIDs, 3); pulldown = g_hud.AddPullDown("Vtr Draw Mode (v)", 10, 195, 250, callbackVtrDrawMode, 'v'); g_hud.AddPullDownButton(pulldown, "None", 0, g_VtrDrawMode==kDRAW_NONE); g_hud.AddPullDownButton(pulldown, "Vertices", 1, g_VtrDrawMode==kDRAW_VERTICES); g_hud.AddPullDownButton(pulldown, "Wireframe", 2, g_VtrDrawMode==kDRAW_WIREFRAME); g_hud.AddPullDownButton(pulldown, "Faces", 3, g_VtrDrawMode==kDRAW_FACES); g_hud.AddCheckBox("Vert IDs", g_VtrDrawVertIDs!=0, 10, 215, callbackDrawIDs, 4); g_hud.AddCheckBox("Edge IDs", g_VtrDrawEdgeIDs!=0, 10, 235, callbackDrawIDs, 5); g_hud.AddCheckBox("Face IDs", g_VtrDrawFaceIDs!=0, 10, 255, callbackDrawIDs, 6); g_hud.AddCheckBox("Ptex IDs", g_VtrDrawPtexIDs!=0, 10, 275, callbackDrawIDs, 7); g_hud.AddCheckBox("Edge Sharp", g_VtrDrawEdgeSharpness!=0, 10, 295, callbackDrawIDs, 8); g_hud.AddCheckBox("Gregory Basis", g_VtrDrawGregogyBasis!=0, 10, 315, callbackDrawIDs, 9); g_hud.AddCheckBox("Use Stencils (s)", g_useStencils!=0, 10, 350, callbackUseStencils, 0, 's'); g_hud.AddCheckBox("Adaptive (`)", g_Adaptive!=0, 10, 370, callbackAdaptive, 0, '`'); g_hud.AddSlider("Font Scale", 0.0f, 0.1f, 0.01f, -900, -50, 100, false, callbackScale, 0); for (int i = 1; i < 11; ++i) { char level[16]; sprintf(level, "Lv. %d", i); g_hud.AddRadioButton(3, level, i==g_level, 10, 380+i*20, callbackLevel, i, '0'+(i%10)); } int shapes_pulldown = g_hud.AddPullDown("Shape (N)", -300, 10, 300, callbackModel, 'n'); for (int i = 0; i < (int)g_shapes.size(); ++i) { g_hud.AddPullDownButton(shapes_pulldown, g_shapes[i].name.c_str(),i, (g_currentShape==i)); } g_hud.AddCheckBox("FVar Verts", g_VtrDrawFVarVerts!=0, 300, 10, callbackDrawIDs, 10); g_hud.AddCheckBox("FVar Patches", g_VtrDrawFVarPatches!=0, 300, 30, callbackDrawIDs, 11); g_hud.AddSlider("FVar Tess", 1.0f, 10.0f, (float)g_VtrDrawFVarPatchTess, 300, 50, 25, true, callbackFVarTess, 0); int fvar_pulldown = g_hud.AddPullDown("FVar Interpolation (i)", 300, 90, 250, callbackFVarInterpolation, 'i'); g_hud.AddPullDownButton(fvar_pulldown, "FVAR_LINEAR_NONE", SdcOptions::FVAR_LINEAR_NONE, g_fvarInterpolation==SdcOptions::FVAR_LINEAR_NONE); g_hud.AddPullDownButton(fvar_pulldown, "FVAR_LINEAR_CORNERS_ONLY", SdcOptions::FVAR_LINEAR_CORNERS_ONLY, g_fvarInterpolation==SdcOptions::FVAR_LINEAR_CORNERS_ONLY); g_hud.AddPullDownButton(fvar_pulldown, "FVAR_LINEAR_CORNERS_PLUS1", SdcOptions::FVAR_LINEAR_CORNERS_PLUS1, g_fvarInterpolation==SdcOptions::FVAR_LINEAR_CORNERS_PLUS1); g_hud.AddPullDownButton(fvar_pulldown, "FVAR_LINEAR_CORNERS_PLUS2", SdcOptions::FVAR_LINEAR_CORNERS_PLUS2, g_fvarInterpolation==SdcOptions::FVAR_LINEAR_CORNERS_PLUS2); g_hud.AddPullDownButton(fvar_pulldown, "FVAR_LINEAR_BOUNDARIES", SdcOptions::FVAR_LINEAR_BOUNDARIES, g_fvarInterpolation==SdcOptions::FVAR_LINEAR_BOUNDARIES); g_hud.AddPullDownButton(fvar_pulldown, "FVAR_LINEAR_ALL", SdcOptions::FVAR_LINEAR_ALL, g_fvarInterpolation==SdcOptions::FVAR_LINEAR_ALL); if (not g_font) { g_font = new GLFont( g_hud.GetFontTexture() ); } } //------------------------------------------------------------------------------ static void initGL() { glClearColor(0.1f, 0.1f, 0.1f, 0.0f); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LEQUAL); glCullFace(GL_BACK); glEnable(GL_CULL_FACE); glGenQueries(2, g_queries); } //------------------------------------------------------------------------------ static void uninitGL() { glDeleteQueries(2, g_queries); } //------------------------------------------------------------------------------ static void idle() { if (g_repeatCount != 0 and g_frame >= g_repeatCount) g_running = 0; } //------------------------------------------------------------------------------ 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) { bool fullscreen = false; std::string str; for (int i = 1; i < argc; ++i) { if (!strcmp(argv[i], "-d")) g_level = atoi(argv[++i]); else if (!strcmp(argv[i], "-c")) g_repeatCount = atoi(argv[++i]); else if (!strcmp(argv[i], "-f")) fullscreen = true; else { std::ifstream ifs(argv[1]); if (ifs) { std::stringstream ss; ss << ifs.rdbuf(); ifs.close(); str = ss.str(); g_shapes.push_back(ShapeDesc(argv[1], str.c_str(), kCatmark)); } } } initShapes(); if (not glfwInit()) { printf("Failed to initialize GLFW\n"); return 1; } static const char windowTitle[] = "OpenSubdiv vtrViewer"; #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); // accommocate high DPI displays (e.g. mac retina displays) glfwGetFramebufferSize(g_window, &g_width, &g_height); glfwSetFramebufferSizeCallback(g_window, reshape); glfwSetKeyCallback(g_window, keyboard); glfwSetCursorPosCallback(g_window, motion); glfwSetMouseButtonCallback(g_window, mouse); glfwSetWindowCloseCallback(g_window, windowClose); #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 = %s\n", glewGetErrorString(r)); exit(1); } #ifdef CORE_PROFILE // clear GL errors which was generated during glewInit() glGetError(); #endif #endif initGL(); glfwSwapInterval(0); initHUD(); rebuildOsdMeshes(); checkGLErrors("before loop"); while (g_running) { idle(); display(); glfwPollEvents(); glfwSwapBuffers(g_window); glFinish(); } uninitGL(); glfwTerminate(); } //------------------------------------------------------------------------------