mirror of
https://github.com/PixarAnimationStudios/OpenSubdiv
synced 2025-01-09 08:10:07 +00:00
33bfbf699b
In OpenSubdiv 2.x, we encapsulated subdivision tables into compute context in osd layer since those tables are order-dependent and have to be applied in a certain manner. In 3.0, we adopted stencil table based refinement. It's more simple and such an encapsulation is no longer needed. Also 2.0 API has several ownership issues of GPU kernel caching, and forces unnecessary instantiation of controllers even though the cpu kernels typically don't need instances unlike GPU ones. This change completely revisit osd client facing APIs. All contexts and controllers were replaced with device-specific tables and evaluators. While we can still use consistent API across various device backends, unnecessary complexities have been removed. For example, cpu evaluator is just a set of static functions and also there's no need to replicate FarStencilTables to ComputeContext. Also the new API delegates the ownership of compiled GPU kernels to clients, for the better management of resources especially in multiple GPU environment. In addition to integrating ComputeController and EvalStencilController into a single function Evaluator::EvalStencils(), EvalLimit API is also added into Evaluator. This is working but still in progress, and we'll make a followup change for the complete implementation. -some naming convention changes: GLSLTransformFeedback to GLXFBEvaluator GLSLCompute to GLComputeEvaluator -move LimitLocation struct into examples/glEvalLimit. We're still discussing patch evaluation interface. Basically we'd like to tease all ptex-specific parametrization out of far/osd layer. TODO: -implments EvalPatches() in the right way -derivative evaluation API is still interim. -VertexBufferDescriptor needs a better API to advance its location -synchronization mechanism is not ideal (too global). -OsdMesh class is hacky. need to fix it.
1346 lines
42 KiB
C++
1346 lines
42 KiB
C++
//
|
|
// Copyright 2013 Pixar
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "Apache License")
|
|
// with the following modification; you may not use this file except in
|
|
// compliance with the Apache License and the following modification to it:
|
|
// Section 6. Trademarks. is deleted and replaced with:
|
|
//
|
|
// 6. Trademarks. This License does not grant permission to use the trade
|
|
// names, trademarks, service marks, or product names of the Licensor
|
|
// and its affiliates, except as required to comply with Section 4(c) of
|
|
// the License and to reproduce the content of the NOTICE file.
|
|
//
|
|
// You may obtain a copy of the Apache License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the Apache License with the above modification is
|
|
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
// KIND, either express or implied. See the Apache License for the specific
|
|
// language governing permissions and limitations under the Apache License.
|
|
//
|
|
|
|
#if defined(__APPLE__)
|
|
#if defined(OSD_USES_GLEW)
|
|
#include <GL/glew.h>
|
|
#else
|
|
#include <OpenGL/gl3.h>
|
|
#endif
|
|
#define GLFW_INCLUDE_GL3
|
|
#define GLFW_NO_GLU
|
|
#else
|
|
#include <stdlib.h>
|
|
#include <GL/glew.h>
|
|
#if defined(WIN32)
|
|
#include <GL/wglew.h>
|
|
#endif
|
|
#endif
|
|
|
|
#include <GLFW/glfw3.h>
|
|
GLFWwindow* g_window = 0;
|
|
GLFWmonitor* g_primary = 0;
|
|
|
|
#include <osd/glDrawContext.h>
|
|
#include <osd/glDrawRegistry.h>
|
|
#include <far/error.h>
|
|
|
|
#include <osd/cpuEvaluator.h>
|
|
#include <osd/cpuGLVertexBuffer.h>
|
|
|
|
#include <osd/glMesh.h>
|
|
OpenSubdiv::Osd::GLMeshInterface *g_mesh = NULL;
|
|
|
|
#include <common/vtr_utils.h>
|
|
#include "../common/stopwatch.h"
|
|
#include "../common/simple_math.h"
|
|
#include "../common/gl_hud.h"
|
|
|
|
static const char *shaderSource =
|
|
#if defined(GL_ARB_tessellation_shader) || defined(GL_VERSION_4_0)
|
|
#include "shader.gen.h"
|
|
#else
|
|
#include "shader_gl3.gen.h"
|
|
#endif
|
|
;
|
|
|
|
#include <cfloat>
|
|
#include <vector>
|
|
#include <fstream>
|
|
#include <sstream>
|
|
#include <utility>
|
|
#include <string>
|
|
#include <algorithm>
|
|
|
|
enum DisplayStyle { kWire = 0,
|
|
kShaded,
|
|
kWireShaded };
|
|
|
|
int g_currentShape = 0;
|
|
|
|
int g_frame = 0,
|
|
g_repeatCount = 0;
|
|
|
|
OpenSubdiv::Sdc::Options::FVarLinearInterpolation g_fvarBoundary =
|
|
OpenSubdiv::Sdc::Options::FVAR_LINEAR_ALL;
|
|
|
|
// GUI variables
|
|
int g_fullscreen = 0,
|
|
g_freeze = 0,
|
|
g_displayStyle = kWireShaded,
|
|
g_adaptive = 0,
|
|
g_mbutton[3] = {0, 0, 0},
|
|
g_mouseUvView = 0,
|
|
g_running = 1;
|
|
|
|
float g_moveScale = 0.0f;
|
|
|
|
float g_rotate[2] = {0, 0},
|
|
g_dolly = 5,
|
|
g_pan[2] = {0, 0},
|
|
g_center[3] = {0, 0, 0},
|
|
g_size = 0,
|
|
g_uvPan[2] = {0, 0},
|
|
g_uvScale = 1.0;
|
|
|
|
int g_prev_x = 0,
|
|
g_prev_y = 0;
|
|
|
|
int g_width = 1600,
|
|
g_height = 800;
|
|
|
|
GLhud g_hud;
|
|
|
|
// geometry
|
|
std::vector<float> g_orgPositions,
|
|
g_positions,
|
|
g_normals;
|
|
|
|
Scheme g_scheme;
|
|
|
|
int g_level = 2;
|
|
int g_tessLevel = 1;
|
|
int g_tessLevelMin = 1;
|
|
|
|
GLuint g_transformUB = 0,
|
|
g_transformBinding = 0,
|
|
g_tessellationUB = 0,
|
|
g_tessellationBinding = 0;
|
|
|
|
struct Transform {
|
|
float ModelViewMatrix[16];
|
|
float ProjectionMatrix[16];
|
|
float ModelViewProjectionMatrix[16];
|
|
float ModelViewInverseMatrix[16];
|
|
float UvViewMatrix[16];
|
|
} g_transformData;
|
|
|
|
GLuint g_vao = 0;
|
|
GLuint g_cageEdgeVAO = 0,
|
|
g_cageEdgeVBO = 0,
|
|
g_cageVertexVAO = 0,
|
|
g_cageVertexVBO = 0;
|
|
|
|
std::vector<int> g_coarseEdges;
|
|
std::vector<float> g_coarseEdgeSharpness;
|
|
std::vector<float> g_coarseVertexSharpness;
|
|
|
|
struct Program {
|
|
GLuint program;
|
|
GLuint uniformModelViewProjectionMatrix;
|
|
GLuint attrPosition;
|
|
GLuint attrColor;
|
|
} g_defaultProgram;
|
|
|
|
//------------------------------------------------------------------------------
|
|
static GLuint
|
|
compileShader(GLenum shaderType, const char *source) {
|
|
|
|
GLuint shader = glCreateShader(shaderType);
|
|
glShaderSource(shader, 1, &source, NULL);
|
|
glCompileShader(shader);
|
|
return shader;
|
|
}
|
|
|
|
static bool
|
|
linkDefaultProgram() {
|
|
|
|
#if defined(GL_ARB_tessellation_shader) || defined(GL_VERSION_4_0)
|
|
#define GLSL_VERSION_DEFINE "#version 400\n"
|
|
#else
|
|
#define GLSL_VERSION_DEFINE "#version 150\n"
|
|
#endif
|
|
|
|
static const char *vsSrc =
|
|
GLSL_VERSION_DEFINE
|
|
"in vec3 position;\n"
|
|
"in vec3 color;\n"
|
|
"out vec4 fragColor;\n"
|
|
"uniform mat4 ModelViewProjectionMatrix;\n"
|
|
"void main() {\n"
|
|
" fragColor = vec4(color, 1);\n"
|
|
" gl_Position = ModelViewProjectionMatrix * "
|
|
" vec4(position, 1);\n"
|
|
"}\n";
|
|
|
|
static const char *fsSrc =
|
|
GLSL_VERSION_DEFINE
|
|
"in vec4 fragColor;\n"
|
|
"out vec4 color;\n"
|
|
"void main() {\n"
|
|
" color = fragColor;\n"
|
|
"}\n";
|
|
|
|
GLuint program = glCreateProgram();
|
|
GLuint vertexShader = compileShader(GL_VERTEX_SHADER, vsSrc);
|
|
GLuint fragmentShader = compileShader(GL_FRAGMENT_SHADER, fsSrc);
|
|
|
|
glAttachShader(program, vertexShader);
|
|
glAttachShader(program, fragmentShader);
|
|
|
|
glLinkProgram(program);
|
|
|
|
GLint status;
|
|
glGetProgramiv(program, GL_LINK_STATUS, &status);
|
|
if (status == GL_FALSE) {
|
|
GLint infoLogLength;
|
|
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLogLength);
|
|
char *infoLog = new char[infoLogLength];
|
|
glGetProgramInfoLog(program, infoLogLength, NULL, infoLog);
|
|
printf("%s\n", infoLog);
|
|
delete[] infoLog;
|
|
exit(1);
|
|
}
|
|
|
|
g_defaultProgram.program = program;
|
|
g_defaultProgram.uniformModelViewProjectionMatrix =
|
|
glGetUniformLocation(program, "ModelViewProjectionMatrix");
|
|
g_defaultProgram.attrPosition = glGetAttribLocation(program, "position");
|
|
g_defaultProgram.attrColor = glGetAttribLocation(program, "color");
|
|
|
|
return true;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
#include "init_shapes.h"
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
static void
|
|
calcNormals(OpenSubdiv::Far::TopologyRefiner const & refiner,
|
|
std::vector<float> const & pos, std::vector<float> & normals) {
|
|
|
|
typedef OpenSubdiv::Far::ConstIndexArray IndexArray;
|
|
|
|
// calc normal vectors
|
|
int nverts = (int)pos.size()/3;
|
|
|
|
int nfaces = refiner.GetNumFaces(0);
|
|
for (int face = 0; face < nfaces; ++face) {
|
|
|
|
IndexArray fverts = refiner.GetFaceVertices(0, face);
|
|
|
|
assert(fverts.size()>=2);
|
|
|
|
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 j = 0; j < fverts.size(); j++) {
|
|
int idx = fverts[j] * 3;
|
|
normals[idx ] += n[0];
|
|
normals[idx+1] += n[1];
|
|
normals[idx+2] += n[2];
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < nverts; ++i)
|
|
normalize(&normals[i*3]);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
static void
|
|
updateGeom() {
|
|
|
|
int nverts = (int)g_orgPositions.size() / 3;
|
|
|
|
std::vector<float> vertex;
|
|
vertex.reserve(nverts*3);
|
|
|
|
const float *p = &g_orgPositions[0];
|
|
|
|
float r = sin(g_frame*0.001f) * g_moveScale;
|
|
for (int i = 0; i < nverts; ++i) {
|
|
float ct = cos(p[2] * r);
|
|
float st = sin(p[2] * r);
|
|
g_positions[i*3+0] = p[0]*ct + p[1]*st;
|
|
g_positions[i*3+1] = -p[0]*st + p[1]*ct;
|
|
g_positions[i*3+2] = p[2];
|
|
|
|
p += 3;
|
|
}
|
|
|
|
p = &g_orgPositions[0];
|
|
const float *pp = &g_positions[0];
|
|
for (int i = 0; i < nverts; ++i) {
|
|
vertex.push_back(pp[0]);
|
|
vertex.push_back(pp[1]);
|
|
vertex.push_back(pp[2]);
|
|
pp += 3;
|
|
}
|
|
|
|
g_mesh->UpdateVertexBuffer(&vertex[0], 0, nverts);
|
|
|
|
g_mesh->Refine();
|
|
|
|
g_mesh->Synchronize();
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
static void
|
|
createOsdMesh(ShapeDesc const & shapeDesc, int level, Scheme scheme = kCatmark) {
|
|
|
|
typedef OpenSubdiv::Far::ConstIndexArray IndexArray;
|
|
|
|
Shape * shape = Shape::parseObj(shapeDesc.data.c_str(), shapeDesc.scheme);
|
|
|
|
// create Vtr mesh (topology)
|
|
OpenSubdiv::Sdc::SchemeType sdctype = GetSdcType(*shape);
|
|
OpenSubdiv::Sdc::Options sdcoptions = GetSdcOptions(*shape);
|
|
|
|
sdcoptions.SetFVarLinearInterpolation(g_fvarBoundary);
|
|
|
|
OpenSubdiv::Far::TopologyRefiner * refiner =
|
|
OpenSubdiv::Far::TopologyRefinerFactory<Shape>::Create(*shape,
|
|
OpenSubdiv::Far::TopologyRefinerFactory<Shape>::Options(sdctype, sdcoptions));
|
|
|
|
// save coarse topology (used for coarse mesh drawing)
|
|
int nedges = refiner->GetNumEdges(0),
|
|
nverts = refiner->GetNumVertices(0);
|
|
|
|
g_coarseEdges.resize(nedges*2);
|
|
g_coarseEdgeSharpness.resize(nedges);
|
|
g_coarseVertexSharpness.resize(nverts);
|
|
|
|
for(int i=0; i<nedges; ++i) {
|
|
IndexArray verts = refiner->GetEdgeVertices(0, i);
|
|
g_coarseEdges[i*2 ]=verts[0];
|
|
g_coarseEdges[i*2+1]=verts[1];
|
|
g_coarseEdgeSharpness[i]=refiner->GetEdgeSharpness(0, i);
|
|
}
|
|
|
|
for(int i=0; i<nverts; ++i) {
|
|
g_coarseVertexSharpness[i]=refiner->GetVertexSharpness(0, i);
|
|
}
|
|
|
|
g_orgPositions=shape->verts;
|
|
g_normals.resize(g_orgPositions.size(), 0.0f);
|
|
calcNormals(*refiner, g_orgPositions, g_normals);
|
|
|
|
g_positions.resize(g_orgPositions.size(),0.0f);
|
|
|
|
g_scheme = scheme;
|
|
|
|
// Adaptive refinement currently supported only for catmull-clark scheme
|
|
bool doAdaptive = (g_adaptive!=0 and g_scheme==kCatmark);
|
|
|
|
OpenSubdiv::Osd::MeshBitset bits;
|
|
bits.set(OpenSubdiv::Osd::MeshAdaptive, doAdaptive);
|
|
bits.set(OpenSubdiv::Osd::MeshFVarData, 1);
|
|
|
|
int numVertexElements = 3;
|
|
int numVaryingElements = 0;
|
|
|
|
delete g_mesh;
|
|
g_mesh = new OpenSubdiv::Osd::Mesh<OpenSubdiv::Osd::CpuGLVertexBuffer,
|
|
OpenSubdiv::Far::StencilTables,
|
|
OpenSubdiv::Osd::CpuEvaluator,
|
|
OpenSubdiv::Osd::GLDrawContext>(
|
|
refiner,
|
|
numVertexElements,
|
|
numVaryingElements,
|
|
level, bits);
|
|
|
|
std::vector<float> fvarData;
|
|
|
|
InterpolateFVarData(*refiner, *shape, fvarData);
|
|
|
|
g_mesh->SetFVarDataChannel(shape->GetFVarWidth(), fvarData);
|
|
|
|
delete shape;
|
|
|
|
// compute model bounding
|
|
float min[3] = { FLT_MAX, FLT_MAX, FLT_MAX};
|
|
float max[3] = {-FLT_MAX, -FLT_MAX, -FLT_MAX};
|
|
for (size_t i = 0; i < g_orgPositions.size()/3; ++i) {
|
|
for (int j = 0; j < 3; ++j) {
|
|
float v = g_orgPositions[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);
|
|
|
|
updateGeom();
|
|
|
|
// -------- VAO
|
|
glBindVertexArray(g_vao);
|
|
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_mesh->GetDrawContext()->GetPatchIndexBuffer());
|
|
glBindBuffer(GL_ARRAY_BUFFER, g_mesh->BindVertexBuffer());
|
|
|
|
glEnableVertexAttribArray(0);
|
|
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 3, 0);
|
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
|
glBindVertexArray(0);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
static void
|
|
fitFrame() {
|
|
|
|
g_pan[0] = g_pan[1] = 0;
|
|
g_dolly = g_size;
|
|
g_uvPan[0] = g_uvPan[1] = 0;
|
|
g_uvScale = 1.0;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
static inline void
|
|
setSharpnessColor(float s, float *r, float *g, float *b) {
|
|
|
|
// 0.0 2.0 4.0
|
|
// green --- yellow --- red
|
|
*r = std::min(1.0f, s * 0.5f);
|
|
*g = std::min(1.0f, 2.0f - s*0.5f);
|
|
*b = 0;
|
|
}
|
|
|
|
static void
|
|
drawCageEdges() {
|
|
|
|
glUseProgram(g_defaultProgram.program);
|
|
glUniformMatrix4fv(g_defaultProgram.uniformModelViewProjectionMatrix,
|
|
1, GL_FALSE, g_transformData.ModelViewProjectionMatrix);
|
|
|
|
std::vector<float> vbo;
|
|
vbo.reserve(g_coarseEdges.size() * 6);
|
|
float r, g, b;
|
|
for (int i = 0; i < (int)g_coarseEdges.size(); i+=2) {
|
|
setSharpnessColor(g_coarseEdgeSharpness[i/2], &r, &g, &b);
|
|
for (int j = 0; j < 2; ++j) {
|
|
vbo.push_back(g_positions[g_coarseEdges[i+j]*3]);
|
|
vbo.push_back(g_positions[g_coarseEdges[i+j]*3+1]);
|
|
vbo.push_back(g_positions[g_coarseEdges[i+j]*3+2]);
|
|
vbo.push_back(r);
|
|
vbo.push_back(g);
|
|
vbo.push_back(b);
|
|
}
|
|
}
|
|
|
|
glBindVertexArray(g_cageEdgeVAO);
|
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, g_cageEdgeVBO);
|
|
glBufferData(GL_ARRAY_BUFFER, (int)vbo.size() * sizeof(float), &vbo[0],
|
|
GL_STATIC_DRAW);
|
|
|
|
glEnableVertexAttribArray(g_defaultProgram.attrPosition);
|
|
glEnableVertexAttribArray(g_defaultProgram.attrColor);
|
|
glVertexAttribPointer(g_defaultProgram.attrPosition,
|
|
3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 6, 0);
|
|
glVertexAttribPointer(g_defaultProgram.attrColor,
|
|
3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 6, (void*)12);
|
|
|
|
glDrawArrays(GL_LINES, 0, (int)g_coarseEdges.size());
|
|
|
|
glBindVertexArray(0);
|
|
glUseProgram(0);
|
|
}
|
|
|
|
static void
|
|
drawCageVertices() {
|
|
|
|
glUseProgram(g_defaultProgram.program);
|
|
glUniformMatrix4fv(g_defaultProgram.uniformModelViewProjectionMatrix,
|
|
1, GL_FALSE, g_transformData.ModelViewProjectionMatrix);
|
|
|
|
int numPoints = (int)g_positions.size()/3;
|
|
std::vector<float> vbo;
|
|
vbo.reserve(numPoints*6);
|
|
float r, g, b;
|
|
for (int i = 0; i < numPoints; ++i) {
|
|
setSharpnessColor(g_coarseVertexSharpness[i], &r, &g, &b);
|
|
vbo.push_back(g_positions[i*3+0]);
|
|
vbo.push_back(g_positions[i*3+1]);
|
|
vbo.push_back(g_positions[i*3+2]);
|
|
vbo.push_back(r);
|
|
vbo.push_back(g);
|
|
vbo.push_back(b);
|
|
}
|
|
|
|
glBindVertexArray(g_cageVertexVAO);
|
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, g_cageVertexVBO);
|
|
glBufferData(GL_ARRAY_BUFFER, (int)vbo.size() * sizeof(float), &vbo[0],
|
|
GL_STATIC_DRAW);
|
|
|
|
glEnableVertexAttribArray(g_defaultProgram.attrPosition);
|
|
glEnableVertexAttribArray(g_defaultProgram.attrColor);
|
|
glVertexAttribPointer(g_defaultProgram.attrPosition,
|
|
3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 6, 0);
|
|
glVertexAttribPointer(g_defaultProgram.attrColor,
|
|
3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 6, (void*)12);
|
|
|
|
glPointSize(10.0f);
|
|
glDrawArrays(GL_POINTS, 0, numPoints);
|
|
glPointSize(1.0f);
|
|
|
|
glBindVertexArray(0);
|
|
glUseProgram(0);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
union Effect {
|
|
|
|
Effect(int displayStyle_, int uvDraw_) : value(0) {
|
|
displayStyle = displayStyle_;
|
|
uvDraw = uvDraw_;
|
|
}
|
|
|
|
struct {
|
|
unsigned int displayStyle:3;
|
|
unsigned int uvDraw:1;
|
|
};
|
|
int value;
|
|
|
|
bool operator < (const Effect &e) const {
|
|
return value < e.value;
|
|
}
|
|
};
|
|
|
|
typedef std::pair<OpenSubdiv::Osd::DrawContext::PatchDescriptor, Effect> EffectDesc;
|
|
|
|
class EffectDrawRegistry : public OpenSubdiv::Osd::GLDrawRegistry<EffectDesc> {
|
|
|
|
protected:
|
|
virtual ConfigType *
|
|
_CreateDrawConfig(DescType const & desc, SourceConfigType const * sconfig);
|
|
|
|
virtual SourceConfigType *
|
|
_CreateDrawSourceConfig(DescType const & desc);
|
|
};
|
|
|
|
EffectDrawRegistry::SourceConfigType *
|
|
EffectDrawRegistry::_CreateDrawSourceConfig(DescType const & desc) {
|
|
|
|
Effect effect = desc.second;
|
|
|
|
SourceConfigType * sconfig =
|
|
BaseRegistry::_CreateDrawSourceConfig(desc.first);
|
|
|
|
assert(sconfig);
|
|
|
|
#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
|
|
|
|
typedef OpenSubdiv::Far::PatchDescriptor Descriptor;
|
|
|
|
if (desc.first.GetType() == Descriptor::QUADS or
|
|
desc.first.GetType() == Descriptor::TRIANGLES) {
|
|
sconfig->vertexShader.source = shaderSource;
|
|
sconfig->vertexShader.version = glslVersion;
|
|
sconfig->vertexShader.AddDefine("VERTEX_SHADER");
|
|
} else {
|
|
sconfig->geometryShader.AddDefine("SMOOTH_NORMALS");
|
|
}
|
|
|
|
sconfig->geometryShader.source = shaderSource;
|
|
sconfig->geometryShader.version = glslVersion;
|
|
sconfig->geometryShader.AddDefine("GEOMETRY_SHADER");
|
|
|
|
sconfig->fragmentShader.source = shaderSource;
|
|
sconfig->fragmentShader.version = glslVersion;
|
|
sconfig->fragmentShader.AddDefine("FRAGMENT_SHADER");
|
|
|
|
sconfig->commonShader.AddDefine("OSD_FVAR_WIDTH", "2");
|
|
|
|
|
|
if (desc.first.GetType() == Descriptor::QUADS) {
|
|
// uniform catmark, bilinear
|
|
sconfig->geometryShader.AddDefine("PRIM_QUAD");
|
|
sconfig->fragmentShader.AddDefine("PRIM_QUAD");
|
|
sconfig->commonShader.AddDefine("UNIFORM_SUBDIVISION");
|
|
} else if (desc.first.GetType() == Descriptor::TRIANGLES) {
|
|
// uniform loop
|
|
sconfig->geometryShader.AddDefine("PRIM_TRI");
|
|
sconfig->fragmentShader.AddDefine("PRIM_TRI");
|
|
sconfig->commonShader.AddDefine("LOOP");
|
|
sconfig->commonShader.AddDefine("UNIFORM_SUBDIVISION");
|
|
} else {
|
|
// adaptive
|
|
sconfig->vertexShader.source = shaderSource + sconfig->vertexShader.source;
|
|
sconfig->tessControlShader.source = shaderSource + sconfig->tessControlShader.source;
|
|
sconfig->tessEvalShader.source = shaderSource + sconfig->tessEvalShader.source;
|
|
|
|
sconfig->geometryShader.AddDefine("PRIM_TRI");
|
|
sconfig->fragmentShader.AddDefine("PRIM_TRI");
|
|
}
|
|
|
|
if (effect.uvDraw) {
|
|
sconfig->commonShader.AddDefine("GEOMETRY_OUT_FILL");
|
|
sconfig->commonShader.AddDefine("GEOMETRY_UV_VIEW");
|
|
} else {
|
|
switch (effect.displayStyle) {
|
|
case kWire:
|
|
sconfig->commonShader.AddDefine("GEOMETRY_OUT_WIRE");
|
|
break;
|
|
case kWireShaded:
|
|
sconfig->commonShader.AddDefine("GEOMETRY_OUT_LINE");
|
|
break;
|
|
case kShaded:
|
|
sconfig->commonShader.AddDefine("GEOMETRY_OUT_FILL");
|
|
break;
|
|
}
|
|
}
|
|
|
|
return sconfig;
|
|
}
|
|
|
|
EffectDrawRegistry::ConfigType *
|
|
EffectDrawRegistry::_CreateDrawConfig(
|
|
DescType const & desc,
|
|
SourceConfigType const * sconfig) {
|
|
|
|
ConfigType * config = BaseRegistry::_CreateDrawConfig(desc.first, sconfig);
|
|
assert(config);
|
|
|
|
GLuint uboIndex;
|
|
|
|
// XXXdyu can use layout(binding=) with GLSL 4.20 and beyond
|
|
g_transformBinding = 0;
|
|
uboIndex = glGetUniformBlockIndex(config->program, "Transform");
|
|
if (uboIndex != GL_INVALID_INDEX)
|
|
glUniformBlockBinding(config->program, uboIndex, g_transformBinding);
|
|
|
|
g_tessellationBinding = 1;
|
|
uboIndex = glGetUniformBlockIndex(config->program, "Tessellation");
|
|
if (uboIndex != GL_INVALID_INDEX)
|
|
glUniformBlockBinding(config->program, uboIndex, g_tessellationBinding);
|
|
|
|
GLint loc;
|
|
#if not defined(GL_ARB_separate_shader_objects) || defined(GL_VERSION_4_1)
|
|
glUseProgram(config->program);
|
|
if ((loc = glGetUniformLocation(config->program, "OsdVertexBuffer")) != -1) {
|
|
glUniform1i(loc, 0); // GL_TEXTURE0
|
|
}
|
|
if ((loc = glGetUniformLocation(config->program, "OsdValenceBuffer")) != -1) {
|
|
glUniform1i(loc, 1); // GL_TEXTURE1
|
|
}
|
|
if ((loc = glGetUniformLocation(config->program, "OsdQuadOffsetBuffer")) != -1) {
|
|
glUniform1i(loc, 2); // GL_TEXTURE2
|
|
}
|
|
if ((loc = glGetUniformLocation(config->program, "OsdPatchParamBuffer")) != -1) {
|
|
glUniform1i(loc, 3); // GL_TEXTURE3
|
|
}
|
|
if ((loc = glGetUniformLocation(config->program, "OsdFVarDataBuffer")) != -1) {
|
|
glUniform1i(loc, 4); // GL_TEXTURE4
|
|
}
|
|
#else
|
|
if ((loc = glGetUniformLocation(config->program, "OsdVertexBuffer")) != -1) {
|
|
glProgramUniform1i(config->program, loc, 0); // GL_TEXTURE0
|
|
}
|
|
if ((loc = glGetUniformLocation(config->program, "OsdValenceBuffer")) != -1) {
|
|
glProgramUniform1i(config->program, loc, 1); // GL_TEXTURE1
|
|
}
|
|
if ((loc = glGetUniformLocation(config->program, "OsdQuadOffsetBuffer")) != -1) {
|
|
glProgramUniform1i(config->program, loc, 2); // GL_TEXTURE2
|
|
}
|
|
if ((loc = glGetUniformLocation(config->program, "OsdPatchParamBuffer")) != -1) {
|
|
glProgramUniform1i(config->program, loc, 3); // GL_TEXTURE3
|
|
}
|
|
if ((loc = glGetUniformLocation(config->program, "OsdFVarDataBuffer")) != -1) {
|
|
glProgramUniform1i(config->program, loc, 4); // GL_TEXTURE4
|
|
}
|
|
#endif
|
|
|
|
return config;
|
|
}
|
|
|
|
EffectDrawRegistry effectRegistry;
|
|
|
|
static Effect
|
|
GetEffect(bool uvDraw = false) {
|
|
|
|
return Effect(g_displayStyle, uvDraw);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
static GLuint
|
|
bindProgram(Effect effect, OpenSubdiv::Osd::DrawContext::PatchArray const & patch) {
|
|
|
|
EffectDesc effectDesc(patch.GetDescriptor(), effect);
|
|
EffectDrawRegistry::ConfigType *
|
|
config = effectRegistry.GetDrawConfig(effectDesc);
|
|
|
|
GLuint program = config->program;
|
|
|
|
glUseProgram(program);
|
|
|
|
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);
|
|
|
|
glBindBufferBase(GL_UNIFORM_BUFFER, g_transformBinding, g_transformUB);
|
|
|
|
// Update and bind tessellation state
|
|
struct Tessellation {
|
|
float TessLevel;
|
|
} tessellationData;
|
|
|
|
tessellationData.TessLevel = static_cast<float>(1 << g_tessLevel);
|
|
|
|
if (!g_tessellationUB) {
|
|
glGenBuffers(1, &g_tessellationUB);
|
|
glBindBuffer(GL_UNIFORM_BUFFER, g_tessellationUB);
|
|
glBufferData(GL_UNIFORM_BUFFER,
|
|
sizeof(tessellationData), NULL, GL_STATIC_DRAW);
|
|
};
|
|
glBindBuffer(GL_UNIFORM_BUFFER, g_tessellationUB);
|
|
glBufferSubData(GL_UNIFORM_BUFFER,
|
|
0, sizeof(tessellationData), &tessellationData);
|
|
glBindBuffer(GL_UNIFORM_BUFFER, 0);
|
|
|
|
glBindBufferBase(GL_UNIFORM_BUFFER, g_tessellationBinding, g_tessellationUB);
|
|
|
|
if (g_mesh->GetDrawContext()->GetVertexTextureBuffer()) {
|
|
glActiveTexture(GL_TEXTURE0);
|
|
glBindTexture(GL_TEXTURE_BUFFER,
|
|
g_mesh->GetDrawContext()->GetVertexTextureBuffer());
|
|
}
|
|
if (g_mesh->GetDrawContext()->GetVertexValenceTextureBuffer()) {
|
|
glActiveTexture(GL_TEXTURE1);
|
|
glBindTexture(GL_TEXTURE_BUFFER,
|
|
g_mesh->GetDrawContext()->GetVertexValenceTextureBuffer());
|
|
}
|
|
if (g_mesh->GetDrawContext()->GetQuadOffsetsTextureBuffer()) {
|
|
glActiveTexture(GL_TEXTURE2);
|
|
glBindTexture(GL_TEXTURE_BUFFER,
|
|
g_mesh->GetDrawContext()->GetQuadOffsetsTextureBuffer());
|
|
}
|
|
if (g_mesh->GetDrawContext()->GetPatchParamTextureBuffer()) {
|
|
glActiveTexture(GL_TEXTURE3);
|
|
glBindTexture(GL_TEXTURE_BUFFER,
|
|
g_mesh->GetDrawContext()->GetPatchParamTextureBuffer());
|
|
}
|
|
if (g_mesh->GetDrawContext()->GetFvarDataTextureBuffer()) {
|
|
glActiveTexture(GL_TEXTURE4);
|
|
glBindTexture(GL_TEXTURE_BUFFER,
|
|
g_mesh->GetDrawContext()->GetFvarDataTextureBuffer());
|
|
}
|
|
|
|
glActiveTexture(GL_TEXTURE0);
|
|
|
|
return program;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
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/2, g_height);
|
|
|
|
// prepare view matrix
|
|
double aspect = (g_width/2)/(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.01f, 500.0f);
|
|
multMatrix(g_transformData.ModelViewProjectionMatrix,
|
|
g_transformData.ModelViewMatrix,
|
|
g_transformData.ProjectionMatrix);
|
|
|
|
identity(g_transformData.UvViewMatrix);
|
|
scale(g_transformData.UvViewMatrix, g_uvScale, g_uvScale, 1);
|
|
translate(g_transformData.UvViewMatrix, -g_uvPan[0], -g_uvPan[1], 0);
|
|
|
|
glEnable(GL_DEPTH_TEST);
|
|
|
|
// make sure that the vertex buffer is interoped back as a GL resources.
|
|
g_mesh->BindVertexBuffer();
|
|
|
|
glBindVertexArray(g_vao);
|
|
|
|
OpenSubdiv::Osd::DrawContext::PatchArrayVector const & patches =
|
|
g_mesh->GetDrawContext()->GetPatchArrays();
|
|
|
|
if (g_displayStyle == kWire)
|
|
glDisable(GL_CULL_FACE);
|
|
|
|
// patch drawing
|
|
for (int i = 0; i < (int)patches.size(); ++i) {
|
|
OpenSubdiv::Osd::DrawContext::PatchArray const & patch = patches[i];
|
|
|
|
OpenSubdiv::Osd::DrawContext::PatchDescriptor desc = patch.GetDescriptor();
|
|
OpenSubdiv::Far::PatchDescriptor::Type patchType = desc.GetType();
|
|
|
|
GLenum primType;
|
|
|
|
switch (patchType) {
|
|
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, desc.GetNumControlVertices());
|
|
#else
|
|
primType = GL_POINTS;
|
|
#endif
|
|
}
|
|
|
|
#if defined(GL_ARB_tessellation_shader) || defined(GL_VERSION_4_0)
|
|
GLuint program = bindProgram(GetEffect(), patch);
|
|
|
|
GLuint uniformGregoryQuadOffsetBase =
|
|
glGetUniformLocation(program, "GregoryQuadOffsetBase");
|
|
GLuint uniformPrimitiveIdBase =
|
|
glGetUniformLocation(program, "PrimitiveIdBase");
|
|
|
|
glProgramUniform1i(program, uniformGregoryQuadOffsetBase,
|
|
patch.GetQuadOffsetIndex());
|
|
glProgramUniform1i(program, uniformPrimitiveIdBase,
|
|
patch.GetPatchIndex());
|
|
#else
|
|
GLuint program = bindProgram(GetEffect(), patch);
|
|
GLint uniformPrimitiveIdBase =
|
|
glGetUniformLocation(program, "PrimitiveIdBase");
|
|
if (uniformPrimitiveIdBase != -1)
|
|
glUniform1i(uniformPrimitiveIdBase, patch.GetPatchIndex());
|
|
#endif
|
|
glDrawElements(primType, patch.GetNumIndices(), GL_UNSIGNED_INT,
|
|
(void *)(patch.GetVertIndex() * sizeof(unsigned int)));
|
|
}
|
|
if (g_displayStyle == kWire)
|
|
glEnable(GL_CULL_FACE);
|
|
|
|
glBindVertexArray(0);
|
|
glUseProgram(0);
|
|
|
|
drawCageEdges();
|
|
drawCageVertices();
|
|
|
|
g_hud.GetFrameBuffer()->ApplyImageShader();
|
|
|
|
// ---------------------------------------------
|
|
// uv viewport
|
|
glViewport(g_width/2, 0, g_width/2, g_height);
|
|
|
|
g_mesh->BindVertexBuffer();
|
|
|
|
glBindVertexArray(g_vao);
|
|
|
|
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
|
|
|
|
for (int i = 0; i < (int)patches.size(); ++i) {
|
|
OpenSubdiv::Osd::DrawContext::PatchArray const & patch = patches[i];
|
|
|
|
OpenSubdiv::Osd::DrawContext::PatchDescriptor desc = patch.GetDescriptor();
|
|
OpenSubdiv::Far::PatchDescriptor::Type patchType = desc.GetType();
|
|
|
|
GLenum primType;
|
|
|
|
switch (patchType) {
|
|
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, desc.GetNumControlVertices());
|
|
#else
|
|
primType = GL_POINTS;
|
|
#endif
|
|
}
|
|
|
|
#if defined(GL_ARB_tessellation_shader) || defined(GL_VERSION_4_0)
|
|
GLuint program = bindProgram(GetEffect(/*uvDraw=*/ true), patch);
|
|
|
|
GLuint uniformGregoryQuadOffsetBase =
|
|
glGetUniformLocation(program, "GregoryQuadOffsetBase");
|
|
GLuint uniformPrimitiveIdBase =
|
|
glGetUniformLocation(program, "PrimitiveIdBase");
|
|
|
|
glProgramUniform1i(program, uniformGregoryQuadOffsetBase,
|
|
patch.GetQuadOffsetIndex());
|
|
glProgramUniform1i(program, uniformPrimitiveIdBase,
|
|
patch.GetPatchIndex());
|
|
#else
|
|
GLuint program = bindProgram(GetEffect(/*uvDraw=*/ true), patch);
|
|
GLint uniformPrimitiveIdBase =
|
|
glGetUniformLocation(program, "PrimitiveIdBase");
|
|
if (uniformPrimitiveIdBase != -1)
|
|
glUniform1i(uniformPrimitiveIdBase, patch.GetPatchIndex());
|
|
#endif
|
|
glDrawElements(primType, patch.GetNumIndices(), GL_UNSIGNED_INT,
|
|
(void *)(patch.GetVertIndex() * sizeof(unsigned int)));
|
|
}
|
|
|
|
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
|
|
|
// full viewport
|
|
glViewport(0, 0, g_width, g_height);
|
|
|
|
if (g_hud.IsVisible()) {
|
|
g_hud.DrawString(10, -40, "Tess level : %d", g_tessLevel);
|
|
g_hud.Flush();
|
|
}
|
|
|
|
glFinish();
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
static void
|
|
motion(GLFWwindow *, double dx, double dy) {
|
|
int x=(int)dx, y=(int)dy;
|
|
|
|
if (g_mouseUvView) {
|
|
if (!g_mbutton[0] && !g_mbutton[1] && g_mbutton[2]) {
|
|
// pan
|
|
g_uvPan[0] -= (x - g_prev_x) * 2 / g_uvScale / static_cast<float>(g_width/2);
|
|
g_uvPan[1] += (y - g_prev_y) * 2 / g_uvScale / static_cast<float>(g_height);
|
|
} else if ((g_mbutton[0] && !g_mbutton[1] && g_mbutton[2]) or
|
|
(!g_mbutton[0] && g_mbutton[1] && !g_mbutton[2])) {
|
|
// scale
|
|
g_uvScale += g_uvScale*0.01f*(x - g_prev_x);
|
|
g_uvScale = std::max(std::min(g_uvScale, 100.0f), 0.01f);
|
|
}
|
|
} 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 (button == 0 && state == GLFW_PRESS && g_hud.MouseClick(g_prev_x, g_prev_y))
|
|
return;
|
|
|
|
if (button < 3) {
|
|
g_mbutton[button] = (state == GLFW_PRESS);
|
|
}
|
|
|
|
g_mouseUvView = (g_prev_x > g_width/2);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
static void
|
|
uninitGL() {
|
|
|
|
glDeleteBuffers(1, &g_cageVertexVBO);
|
|
glDeleteBuffers(1, &g_cageEdgeVBO);
|
|
glDeleteVertexArrays(1, &g_vao);
|
|
glDeleteVertexArrays(1, &g_cageVertexVAO);
|
|
glDeleteVertexArrays(1, &g_cageEdgeVAO);
|
|
|
|
if (g_mesh)
|
|
delete g_mesh;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
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
|
|
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 GLFW_KEY_TAB: toggleFullScreen(); break;
|
|
case '+':
|
|
case '=': g_tessLevel++; break;
|
|
case '-': g_tessLevel = std::max(g_tessLevelMin, g_tessLevel-1); break;
|
|
case GLFW_KEY_ESCAPE: g_hud.SetVisible(!g_hud.IsVisible()); break;
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
static void
|
|
rebuildOsdMesh() {
|
|
|
|
createOsdMesh(g_defaultShapes[g_currentShape],
|
|
g_level,
|
|
g_defaultShapes[g_currentShape].scheme);
|
|
}
|
|
|
|
static void
|
|
callbackDisplayStyle(int b) {
|
|
|
|
g_displayStyle = b;
|
|
}
|
|
|
|
static void
|
|
callbackLevel(int l) {
|
|
|
|
g_level = l;
|
|
rebuildOsdMesh();
|
|
}
|
|
|
|
static void
|
|
callbackModel(int m) {
|
|
|
|
int maxShapes = static_cast<int>(g_defaultShapes.size());
|
|
g_currentShape = std::max(0, std::min(m, maxShapes-1));
|
|
rebuildOsdMesh();
|
|
}
|
|
|
|
static void
|
|
callbackAdaptive(bool checked, int /* a */) {
|
|
|
|
if (OpenSubdiv::Osd::GLDrawContext::SupportsAdaptiveTessellation()) {
|
|
g_adaptive = checked;
|
|
rebuildOsdMesh();
|
|
}
|
|
}
|
|
|
|
static void
|
|
callbackBoundary(int b) {
|
|
|
|
typedef OpenSubdiv::Sdc::Options SdcOptions;
|
|
|
|
switch (b) {
|
|
|
|
case SdcOptions::FVAR_LINEAR_NONE :
|
|
g_fvarBoundary = SdcOptions::FVAR_LINEAR_NONE; break;
|
|
|
|
case SdcOptions::FVAR_LINEAR_CORNERS_ONLY :
|
|
g_fvarBoundary = SdcOptions::FVAR_LINEAR_CORNERS_ONLY; break;
|
|
|
|
case SdcOptions::FVAR_LINEAR_CORNERS_PLUS1 :
|
|
g_fvarBoundary = SdcOptions::FVAR_LINEAR_CORNERS_PLUS1; break;
|
|
|
|
case SdcOptions::FVAR_LINEAR_CORNERS_PLUS2 :
|
|
g_fvarBoundary = SdcOptions::FVAR_LINEAR_CORNERS_PLUS2; break;
|
|
|
|
case SdcOptions::FVAR_LINEAR_BOUNDARIES :
|
|
g_fvarBoundary = SdcOptions::FVAR_LINEAR_BOUNDARIES; break;
|
|
|
|
case SdcOptions::FVAR_LINEAR_ALL :
|
|
g_fvarBoundary = SdcOptions::FVAR_LINEAR_ALL; break;
|
|
|
|
}
|
|
rebuildOsdMesh();
|
|
}
|
|
|
|
static void
|
|
initHUD() {
|
|
|
|
int windowWidth = g_width, windowHeight = g_height,
|
|
frameBufferWidth = g_width, frameBufferHeight = g_height;
|
|
|
|
// window size might not match framebuffer size on a high DPI display
|
|
glfwGetWindowSize(g_window, &windowWidth, &windowHeight);
|
|
|
|
g_hud.Init(windowWidth, windowHeight, frameBufferWidth, frameBufferHeight);
|
|
|
|
g_hud.SetFrameBuffer(new GLFrameBuffer);
|
|
|
|
int shading_pulldown = g_hud.AddPullDown("Shading (W)", 375, 10, 250, callbackDisplayStyle, 'w');
|
|
g_hud.AddPullDownButton(shading_pulldown, "Wire", kWire, g_displayStyle==kWire);
|
|
g_hud.AddPullDownButton(shading_pulldown, "Shaded", kShaded, g_displayStyle==kShaded);
|
|
g_hud.AddPullDownButton(shading_pulldown, "Wire+Shaded", kWireShaded, g_displayStyle==kWireShaded);
|
|
|
|
if (OpenSubdiv::Osd::GLDrawContext::SupportsAdaptiveTessellation())
|
|
g_hud.AddCheckBox("Adaptive (`)", g_adaptive != 0, 10, 250, callbackAdaptive, 0, '`');
|
|
|
|
for (int i = 1; i < 11; ++i) {
|
|
char level[16];
|
|
sprintf(level, "Lv. %d", i);
|
|
g_hud.AddRadioButton(3, level, i == 2, 10, 270 + i*20, callbackLevel, i, '0'+(i%10));
|
|
}
|
|
|
|
typedef OpenSubdiv::Sdc::Options SdcOptions;
|
|
|
|
int boundary_pulldown = g_hud.AddPullDown("Boundary (B)", 10, 10, 250, callbackBoundary, 'b');
|
|
g_hud.AddPullDownButton(boundary_pulldown, "None (edge only)",
|
|
SdcOptions::FVAR_LINEAR_NONE, g_fvarBoundary==SdcOptions::FVAR_LINEAR_NONE);
|
|
g_hud.AddPullDownButton(boundary_pulldown, "Corners Only",
|
|
SdcOptions::FVAR_LINEAR_CORNERS_ONLY, g_fvarBoundary==SdcOptions::FVAR_LINEAR_CORNERS_ONLY);
|
|
g_hud.AddPullDownButton(boundary_pulldown, "Corners 1 (edge corner)",
|
|
SdcOptions::FVAR_LINEAR_CORNERS_PLUS1, g_fvarBoundary==SdcOptions::FVAR_LINEAR_CORNERS_PLUS1);
|
|
g_hud.AddPullDownButton(boundary_pulldown, "Corners 2 (edge corner prop)",
|
|
SdcOptions::FVAR_LINEAR_CORNERS_PLUS2, g_fvarBoundary==SdcOptions::FVAR_LINEAR_CORNERS_PLUS2);
|
|
g_hud.AddPullDownButton(boundary_pulldown, "Boundaries (always sharp)",
|
|
SdcOptions::FVAR_LINEAR_BOUNDARIES, g_fvarBoundary==SdcOptions::FVAR_LINEAR_BOUNDARIES);
|
|
g_hud.AddPullDownButton(boundary_pulldown, "All (bilinear)",
|
|
SdcOptions::FVAR_LINEAR_ALL, g_fvarBoundary==SdcOptions::FVAR_LINEAR_ALL);
|
|
|
|
int pulldown_handle = g_hud.AddPullDown("Shape (N)", -300, 10, 300, callbackModel, 'n');
|
|
for (int i = 0; i < (int)g_defaultShapes.size(); ++i) {
|
|
g_hud.AddPullDownButton(pulldown_handle, g_defaultShapes[i].name.c_str(),i);
|
|
}
|
|
|
|
g_hud.Rebuild(windowWidth, windowHeight, frameBufferWidth, frameBufferHeight);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
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);
|
|
|
|
glGenVertexArrays(1, &g_vao);
|
|
glGenVertexArrays(1, &g_cageVertexVAO);
|
|
glGenVertexArrays(1, &g_cageEdgeVAO);
|
|
glGenBuffers(1, &g_cageVertexVBO);
|
|
glGenBuffers(1, &g_cageEdgeVBO);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
static void
|
|
idle() {
|
|
|
|
if (not g_freeze)
|
|
g_frame++;
|
|
|
|
updateGeom();
|
|
|
|
if (g_repeatCount != 0 and g_frame >= g_repeatCount)
|
|
g_running = 0;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
static void
|
|
callbackError(OpenSubdiv::Far::ErrorType err, const char *message) {
|
|
printf("Error: %d\n", err);
|
|
printf("%s", message);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
static void
|
|
callbackErrorGLFW(int error, const char* description) {
|
|
fprintf(stderr, "GLFW Error (%d) : %s\n", error, description);
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
static void
|
|
setGLCoreProfile() {
|
|
|
|
#define glfwOpenWindowHint glfwWindowHint
|
|
#define GLFW_OPENGL_VERSION_MAJOR GLFW_CONTEXT_VERSION_MAJOR
|
|
#define GLFW_OPENGL_VERSION_MINOR GLFW_CONTEXT_VERSION_MINOR
|
|
|
|
glfwOpenWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
|
#if not defined(__APPLE__)
|
|
glfwOpenWindowHint(GLFW_OPENGL_VERSION_MAJOR, 4);
|
|
glfwOpenWindowHint(GLFW_OPENGL_VERSION_MINOR, 2);
|
|
#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_defaultShapes.push_back(ShapeDesc(argv[1], str.c_str(), kCatmark));
|
|
}
|
|
}
|
|
}
|
|
|
|
initShapes();
|
|
|
|
OpenSubdiv::Far::SetErrorCallback(callbackError);
|
|
|
|
glfwSetErrorCallback(callbackErrorGLFW);
|
|
if (not glfwInit()) {
|
|
printf("Failed to initialize GLFW\n");
|
|
return 1;
|
|
}
|
|
|
|
static const char windowTitle[] = "OpenSubdiv glFVarViewer " OPENSUBDIV_VERSION_STRING;
|
|
|
|
#define CORE_PROFILE
|
|
#ifdef CORE_PROFILE
|
|
setGLCoreProfile();
|
|
#endif
|
|
|
|
if (fullscreen) {
|
|
g_primary = glfwGetPrimaryMonitor();
|
|
|
|
// apparently glfwGetPrimaryMonitor fails under linux : if no primary,
|
|
// settle for the first one in the list
|
|
if (not g_primary) {
|
|
int count = 0;
|
|
GLFWmonitor ** monitors = glfwGetMonitors(&count);
|
|
|
|
if (count)
|
|
g_primary = monitors[0];
|
|
}
|
|
|
|
if (g_primary) {
|
|
GLFWvidmode const * vidmode = glfwGetVideoMode(g_primary);
|
|
g_width = vidmode->width;
|
|
g_height = vidmode->height;
|
|
}
|
|
}
|
|
|
|
if (not (g_window=glfwCreateWindow(g_width, g_height, windowTitle,
|
|
fullscreen and g_primary ? g_primary : NULL, NULL))) {
|
|
printf("Failed to open window.\n");
|
|
glfwTerminate();
|
|
return 1;
|
|
}
|
|
glfwMakeContextCurrent(g_window);
|
|
|
|
// accommodate 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();
|
|
linkDefaultProgram();
|
|
|
|
glfwSwapInterval(0);
|
|
|
|
initHUD();
|
|
rebuildOsdMesh();
|
|
|
|
while (g_running) {
|
|
idle();
|
|
display();
|
|
|
|
glfwPollEvents();
|
|
glfwSwapBuffers(g_window);
|
|
|
|
glFinish();
|
|
}
|
|
|
|
uninitGL();
|
|
glfwTerminate();
|
|
}
|