mirror of
https://github.com/PixarAnimationStudios/OpenSubdiv
synced 2024-11-23 12:10:08 +00:00
43aa2500c4
This change moves all gregory patch generation from Far::PatchTablesFactory so that we can construct patch tables without stencil tables as well as client can chose any end patch strategies (we have 3 options for now: legacy 2.x style gregory patch, gregory basis patch and experimental regular patch approximation). Also Far::EndCapGregoryBasisPatchFactory provides index mapping from patch index to vtr face index, which can be used for single gregory patch evaluation on top of refined points, without involving heavier stencil tables generation.
1672 lines
53 KiB
C++
1672 lines
53 KiB
C++
//
|
|
// Copyright 2013 Pixar
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "Apache License")
|
|
// with the following modification; you may not use this file except in
|
|
// compliance with the Apache License and the following modification to it:
|
|
// Section 6. Trademarks. is deleted and replaced with:
|
|
//
|
|
// 6. Trademarks. This License does not grant permission to use the trade
|
|
// names, trademarks, service marks, or product names of the Licensor
|
|
// and its affiliates, except as required to comply with Section 4(c) of
|
|
// the License and to reproduce the content of the NOTICE file.
|
|
//
|
|
// You may obtain a copy of the Apache License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the Apache License with the above modification is
|
|
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
// KIND, either express or implied. See the Apache License for the specific
|
|
// language governing permissions and limitations under the Apache License.
|
|
//
|
|
|
|
#if defined(__APPLE__)
|
|
#if defined(OSD_USES_GLEW)
|
|
#include <GL/glew.h>
|
|
#else
|
|
#include <OpenGL/gl3.h>
|
|
#endif
|
|
#define GLFW_INCLUDE_GL3
|
|
#define GLFW_NO_GLU
|
|
#else
|
|
#include <stdlib.h>
|
|
#include <GL/glew.h>
|
|
#if defined(WIN32)
|
|
#include <GL/wglew.h>
|
|
#endif
|
|
#endif
|
|
|
|
#include <GLFW/glfw3.h>
|
|
GLFWwindow* g_window=0;
|
|
GLFWmonitor* g_primary=0;
|
|
|
|
#if _MSC_VER
|
|
#define snprintf _snprintf
|
|
#endif
|
|
|
|
#include <osd/vertex.h>
|
|
#include <osd/cpuGLVertexBuffer.h>
|
|
|
|
#include <far/gregoryBasis.h>
|
|
#include <far/endCapGregoryBasisPatchFactory.h>
|
|
#include <far/patchTablesFactory.h>
|
|
#include <far/stencilTables.h>
|
|
#include <far/stencilTablesFactory.h>
|
|
|
|
#include <common/vtr_utils.h>
|
|
#include <common/hbr_utils.h>
|
|
|
|
#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 <typeinfo>
|
|
#include <cfloat>
|
|
#include <vector>
|
|
#include <set>
|
|
#include <fstream>
|
|
#include <sstream>
|
|
|
|
//------------------------------------------------------------------------------
|
|
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<Shape> FTopologyRefinerFactory;
|
|
|
|
//------------------------------------------------------------------------------
|
|
// generate display IDs for Hbr faces
|
|
static void
|
|
createFaceNumbers(std::vector<Hface const *> 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; j<nv; ++j) {
|
|
center.AddWithWeight(f->GetVertex(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<Hface const *> faces) {
|
|
|
|
typedef std::map<Hhalfedge const *, int> EdgeMap;
|
|
EdgeMap edgeMap;
|
|
|
|
typedef std::vector<Hhalfedge const *> 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; j<f->GetNumVertices(); ++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<Hface const *> faces) {
|
|
|
|
assert(not faces.empty());
|
|
|
|
static char buf[16];
|
|
|
|
std::vector<Hvertex const *> 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; j<nv; ++j) {
|
|
Hvertex const * v = f->GetVertex(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<Vertex>(shape->scheme, /*fvarwidth*/ 0);
|
|
|
|
createVerticesWithPositions<Vertex>(shape, hmesh);
|
|
|
|
createTopology<Vertex>(shape, hmesh, shape->scheme);
|
|
s.Stop();
|
|
|
|
std::vector<Hface const *> 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; i<nfaces; ++i) {
|
|
coarseFaces[i] = hmesh->GetFace(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<Vertex> const & vertexBuffer) {
|
|
|
|
int maxlevel = refiner.GetMaxLevel(),
|
|
firstvert = 0;
|
|
|
|
if (refiner.IsUniform()) {
|
|
for (int i=0; i<maxlevel; ++i) {
|
|
firstvert += refiner.GetNumVertices(i);
|
|
}
|
|
}
|
|
|
|
static char buf[16];
|
|
if (refiner.IsUniform()) {
|
|
for (int i=firstvert; i<(int)vertexBuffer.size(); ++i) {
|
|
snprintf(buf, 16, "%d", i);
|
|
g_font->Print3D(vertexBuffer[i].GetPos(), buf, 1);
|
|
}
|
|
} else {
|
|
|
|
for (int level=0, vert=0; level<=refiner.GetMaxLevel(); ++level) {
|
|
for (int i=0; i<refiner.GetNumVertices(level); ++i, ++vert) {
|
|
snprintf(buf, 16, "%d", i);
|
|
g_font->Print3D(vertexBuffer[vert].GetPos(), buf, 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// generate display IDs for Vtr edges
|
|
static void
|
|
createEdgeNumbers(OpenSubdiv::Far::TopologyRefiner const & refiner,
|
|
std::vector<Vertex> const & vertexBuffer, bool ids=false, bool sharpness=false) {
|
|
|
|
if (ids or sharpness) {
|
|
|
|
int maxlevel = refiner.GetMaxLevel(),
|
|
firstvert = 0;
|
|
|
|
for (int i=0; i<maxlevel; ++i) {
|
|
firstvert += refiner.GetNumVertices(i);
|
|
}
|
|
|
|
static char buf[16];
|
|
for (int i=0; i<refiner.GetNumEdges(maxlevel); ++i) {
|
|
|
|
Vertex center(0.0f, 0.0f, 0.0f);
|
|
|
|
OpenSubdiv::Far::ConstIndexArray const verts =
|
|
refiner.GetEdgeVertices(maxlevel, i);
|
|
assert(verts.size()==2);
|
|
|
|
center.AddWithWeight(vertexBuffer[firstvert+verts[0]], 0.5f);
|
|
center.AddWithWeight(vertexBuffer[firstvert+verts[1]], 0.5f);
|
|
|
|
if (ids) {
|
|
snprintf(buf, 16, "%d", i);
|
|
g_font->Print3D(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<Vertex> const & vertexBuffer) {
|
|
|
|
static char buf[16];
|
|
|
|
if (refiner.IsUniform()) {
|
|
int maxlevel = refiner.GetMaxLevel(),
|
|
firstvert = 0;
|
|
|
|
for (int i=0; i<maxlevel; ++i) {
|
|
firstvert += refiner.GetNumVertices(i);
|
|
}
|
|
|
|
for (int face=0; face<refiner.GetNumFaces(maxlevel); ++face) {
|
|
|
|
Vertex center(0.0f, 0.0f, 0.0f);
|
|
|
|
OpenSubdiv::Far::ConstIndexArray const verts =
|
|
refiner.GetFaceVertices(maxlevel, face);
|
|
|
|
float weight = 1.0f / (float)verts.size();
|
|
|
|
for (int vert=0; vert<verts.size(); ++vert) {
|
|
center.AddWithWeight(vertexBuffer[firstvert+verts[vert]], weight);
|
|
}
|
|
|
|
snprintf(buf, 16, "%d", face);
|
|
g_font->Print3D(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; face<nfaces; ++face /*, ++patch */) {
|
|
|
|
Vertex center(0.0f, 0.0f, 0.0f);
|
|
|
|
OpenSubdiv::Far::ConstIndexArray const verts =
|
|
refiner.GetFaceVertices(level, face);
|
|
|
|
float weight = 1.0f / (float)verts.size();
|
|
|
|
for (int vert=0; vert<verts.size(); ++vert) {
|
|
center.AddWithWeight(vertexBuffer[firstvert+verts[vert]], weight);
|
|
}
|
|
snprintf(buf, 16, "%d", face);
|
|
g_font->Print3D(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<Vertex> 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; i<cvs.size(); ++i) {
|
|
snprintf(buf, 16, "%d", i);
|
|
g_font->Print3D(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<Vertex> const & fvarBuffer) {
|
|
|
|
static int channel = 0;
|
|
|
|
int patch = g_currentPatch-1;
|
|
static char buf[16];
|
|
|
|
if (patch>=0 and patch<patchTables.GetNumPatchesTotal()) {
|
|
|
|
OpenSubdiv::Far::PatchTables::PatchHandle handle;
|
|
handle.patchIndex = patch;
|
|
|
|
OpenSubdiv::Far::ConstIndexArray const cvs =
|
|
patchTables.GetFVarPatchValues(channel, handle);
|
|
|
|
for (int i=0; i<cvs.size(); ++i) {
|
|
snprintf(buf, 16, "%d", i);
|
|
g_font->Print3D(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<Vertex> 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<tessFactor; ++i) {
|
|
uvs[i] = (float)i/(tessFactor-1.0f);
|
|
}
|
|
|
|
std::vector<Vertex> verts(nverts);
|
|
memset(&verts[0], 0, verts.size()*sizeof(Vertex));
|
|
|
|
OpenSubdiv::Far::PatchTables::PatchHandle handle;
|
|
|
|
Vertex * vert = &verts[0];
|
|
for (int patch=0; patch<npatches; ++patch) {
|
|
for (int i=0; i<tessFactor; ++i) {
|
|
for (int j=0; j<tessFactor; ++j, ++vert) {
|
|
handle.patchIndex = patch;
|
|
patchTables.EvaluateFaceVarying(channel, handle, uvs[i], uvs[j], fvarBuffer, *vert);
|
|
}
|
|
}
|
|
}
|
|
|
|
GLMesh::Options options;
|
|
options.edgeColorMode = GLMesh::EDGECOLOR_BY_PATCHTYPE;
|
|
fvarWire.InitializeFVar(options, refiner, &patchTables, channel, tessFactor, (float *)(&verts[0]));
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// generate display IDs for Vtr Gregory basis
|
|
|
|
static GLMesh gregoryWire;
|
|
|
|
static void
|
|
createGregoryBasis(OpenSubdiv::Far::PatchTables const & patchTables,
|
|
std::vector<Vertex> const & vertexBuffer) {
|
|
|
|
typedef OpenSubdiv::Far::PatchTables PatchTables;
|
|
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<int> vertsperedge(nedges), edgeindices(nedges*2);
|
|
|
|
for (int patch=0; patch<npatches; ++patch) {
|
|
|
|
static int basisedges[40] = { 0, 1, 0, 2, 1, 3, 2, 4,
|
|
5, 6, 5, 7, 6, 8, 7, 9,
|
|
10, 11, 10, 12, 11, 13, 12, 14,
|
|
15, 16, 15, 17, 16, 18, 17, 19,
|
|
1, 7, 6, 12, 11, 17, 16, 2 };
|
|
|
|
int offset = patch * 20,
|
|
* vpe = &vertsperedge[offset],
|
|
* indices = &edgeindices[patch * 40];
|
|
|
|
OpenSubdiv::Far::ConstIndexArray const cvs =
|
|
patchTables.GetPatchVertices(patchArray, patch);
|
|
|
|
for (int i=0; i<20; ++i) {
|
|
vpe[i] = 2;
|
|
indices[i*2] = cvs[basisedges[i*2]];
|
|
indices[i*2+1] = cvs[basisedges[i*2+1]];
|
|
}
|
|
|
|
//Vertex const * verts = &edgeverts[offset];
|
|
static char buf[16];
|
|
for (int i=0; i<4; ++i) {
|
|
int vid = patch * 20 + i * 5;
|
|
|
|
const float *P = vertexBuffer[cvs[i*5+0]].GetPos();
|
|
const float *Ep = vertexBuffer[cvs[i*5+1]].GetPos();
|
|
const float *Em = vertexBuffer[cvs[i*5+2]].GetPos();
|
|
const float *Fp = vertexBuffer[cvs[i*5+3]].GetPos();
|
|
const float *Fm = vertexBuffer[cvs[i*5+4]].GetPos();
|
|
|
|
snprintf(buf, 16, " P%d (%d)", i, vid);
|
|
g_font->Print3D(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<Vertex> const & vertexBuffer) {
|
|
|
|
typedef OpenSubdiv::Far::PatchDescriptor Descriptor;
|
|
|
|
static char buf[16];
|
|
|
|
static int regular[4] = {5, 6, 9, 10},
|
|
boundary[4] = {1, 2, 5, 6},
|
|
corner[4] = {1, 2, 4, 5},
|
|
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::BOUNDARY: remap = boundary; break;
|
|
case Descriptor::CORNER: remap = corner; 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<Shape>::Create(*shape,
|
|
Far::TopologyRefinerFactory<Shape>::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<Vertex> fvarBuffer;
|
|
Far::PatchTables * patchTables = 0;
|
|
bool createFVarWire = g_VtrDrawFVarPatches or g_VtrDrawFVarVerts;
|
|
|
|
// for stencil based gregory evaluation
|
|
Far::EndCapGregoryBasisPatchFactory *gregoryBasisFactory = NULL;
|
|
|
|
if (g_Adaptive) {
|
|
Far::PatchTablesFactory::Options options;
|
|
options.generateFVarTables = createFVarWire;
|
|
|
|
// use GregoryBasis as EndPatch strategy.
|
|
// we want to share boundary vertices of marginal gregory patches
|
|
// only if using stencils.
|
|
bool shareBoundaryVertices = g_useStencils;
|
|
gregoryBasisFactory = new Far::EndCapGregoryBasisPatchFactory(
|
|
*refiner, shareBoundaryVertices);
|
|
|
|
patchTables =
|
|
Far::PatchTablesFactoryT<Far::EndCapGregoryBasisPatchFactory>::Create(
|
|
*refiner, options, gregoryBasisFactory);
|
|
|
|
// increase vertex buffer for the additional gregory verts
|
|
numTotalVerts += gregoryBasisFactory->GetNumGregoryBasisVertices();
|
|
|
|
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.0f);
|
|
Vertex * values = &fvarBuffer[0];
|
|
|
|
int nCoarseValues = refiner->GetNumFVarValues(0);
|
|
|
|
for (int i=0; i<nCoarseValues; ++i) {
|
|
float const * ptr = &shape->uvs[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<Vertex> vertexBuffer(numTotalVerts);
|
|
Vertex * verts = &vertexBuffer[0];
|
|
|
|
// copy coarse vertices positions
|
|
int ncoarseverts = shape->GetNumVertices();
|
|
for (int i=0; i<ncoarseverts; ++i) {
|
|
float * ptr = &shape->verts[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 gregory basis stencils if needed
|
|
if (gregoryBasisFactory) {
|
|
if (Far::StencilTables const * stencilTablesWithGregoryBasis =
|
|
gregoryBasisFactory->CreateVertexStencilTables(stencilTables, /*append=*/true)) {
|
|
delete stencilTables;
|
|
stencilTables = stencilTablesWithGregoryBasis;
|
|
}
|
|
}
|
|
|
|
//
|
|
// 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);
|
|
|
|
// gregory basis evaluation (without stencils)
|
|
if (gregoryBasisFactory) {
|
|
|
|
// gregory basis is defined at the maximum level.
|
|
// all src verts exist in maximum level and indexed within the level
|
|
// and resulting gregory verts will be placed after all refined verts
|
|
Vertex * src = verts + refiner->GetNumVerticesTotal() - refiner->GetNumVertices(maxlevel);
|
|
Vertex * dst = verts + refiner->GetNumVerticesTotal();
|
|
int nPatchArrays = patchTables->GetNumPatchArrays();
|
|
|
|
for (int i = 0; i < nPatchArrays; ++i) {
|
|
Far::PatchDescriptor desc =
|
|
patchTables->GetPatchArrayDescriptor(i);
|
|
if (desc.GetType() == Far::PatchDescriptor::GREGORY_BASIS) {
|
|
|
|
int nPatches = patchTables->GetNumPatches(i);
|
|
for (int j = 0; j < nPatches; ++j) {
|
|
// GregoryBasisFactory knows faceIndex in VtrLevel for this patch
|
|
int faceIndex = gregoryBasisFactory->GetFaceIndex(j);
|
|
|
|
Far::GregoryBasis const *gregoryBasis =
|
|
Far::EndCapGregoryBasisPatchFactory::Create(*refiner, faceIndex);
|
|
|
|
gregoryBasis->Evaluate(src, dst);
|
|
dst += 20;
|
|
|
|
delete gregoryBasis;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
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;
|
|
delete gregoryBasisFactory;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
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, 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();
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|