Skip to content

Instantly share code, notes, and snippets.

@davidgyu
Created February 5, 2013 20:48
Show Gist options
  • Save davidgyu/4717523 to your computer and use it in GitHub Desktop.
Save davidgyu/4717523 to your computer and use it in GitHub Desktop.
OpenSubdiv Example: Uniform Subdivision with Face-varying Data
//
// This is a simple example that uses face-varying u,v data
// with uniform catmull-clark subdivision using OpenSubdiv
//
// c++ -o displayOsdFVarCoord displayOsdFVarCoord.cpp -I$OPENSUBDIV/include -L$OPENSUBDIV/lib -losdCPU -lglut -lGL
//
#if defined(__APPLE__)
#include <GLUT/glut.h>
#else
#include <stdlib.h>
#include <GL/glew.h>
#if defined(WIN32)
#include <GL/wglew.h>
#endif
#include <GL/glut.h>
#endif
#include <osd/error.h>
#include <osd/vertex.h>
#include <far/mesh.h>
#include <far/meshFactory.h>
#include <hbr/mesh.h>
#include <osd/cpuDispatcher.h>
#include <osd/cpuVertexBuffer.h>
#include <osd/cpuComputeContext.h>
#include <osd/cpuComputeController.h>
#include <iostream>
#include <vector>
std::vector<float> vertexBufferData;
std::vector<int> indexBufferData;
std::vector<float> uvBufferData;
typedef OpenSubdiv::HbrMesh<OpenSubdiv::OsdVertex> OsdHbrMesh;
typedef OpenSubdiv::HbrVertex<OpenSubdiv::OsdVertex> OsdHbrVertex;
typedef OpenSubdiv::HbrFace<OpenSubdiv::OsdVertex> OsdHbrFace;
typedef OpenSubdiv::HbrHalfedge<OpenSubdiv::OsdVertex> OsdHbrHalfedge;
typedef OpenSubdiv::HbrFVarData<OpenSubdiv::OsdVertex> OsdHbrFVarData;
typedef OpenSubdiv::FarMeshFactory<OpenSubdiv::OsdVertex> OsdFarMeshFactory;
typedef OpenSubdiv::FarMesh<OpenSubdiv::OsdVertex> OsdFarMesh;
static OsdFarMesh *
buildMesh(const float *vertexData, int numVertices,
const int *indexData, int numIndices,
const int *faceData, int numFaces,
const float *uvData = NULL)
{
// Build HbrMesh to specify mesh topology
static OpenSubdiv::HbrCatmarkSubdivision<OpenSubdiv::OsdVertex> _catmark;
OsdHbrMesh *hbrMesh = NULL;
if (uvData) {
// This set of descriptors describes u,v per face vert
static const int uvFVarCount = 2;
static const int uvFVarIndices[] = { 0, 1 };
static const int uvFVarWidths[] = { 1, 1 };
static const int uvFVarTotalWidth = 2;
hbrMesh = new OsdHbrMesh(&_catmark,
uvFVarCount,
uvFVarIndices,
uvFVarWidths,
uvFVarTotalWidth);
} else {
hbrMesh = new OsdHbrMesh(&_catmark);
}
// This example has boundary edges.
hbrMesh->SetInterpolateBoundaryMethod(
OsdHbrMesh::k_InterpolateBoundaryEdgeAndCorner);
OpenSubdiv::OsdVertex v;
for (int i = 0; i < numVertices; ++i) {
hbrMesh->NewVertex(i, v);
}
std::vector<int> faceVerts;
int faceDataOffset = 0;
int ptexFaceIndex = 0;
for (int fi = 0; fi<numFaces; ++fi) {
int numFaceVerts = faceData[fi];
bool valid = true;
faceVerts.resize(numFaceVerts);
for (int fvi = 0; fvi < numFaceVerts; ++fvi) {
int v0 = indexData[fvi + faceDataOffset];
int v1 = indexData[((fvi+1) % numFaceVerts) + faceDataOffset];
faceVerts[fvi] = v0;
OsdHbrVertex * origin = hbrMesh->GetVertex(v0);
OsdHbrVertex * destination = hbrMesh->GetVertex(v1);
if (!origin || !destination) {
std::cerr << "topology error: non-existent vertex\n";
valid = false;
}
if (origin == destination) {
std::cerr << "topology error: vertex connected to itself\n";
valid = false;
}
OsdHbrHalfedge * opposite = destination->GetEdge(origin);
if (opposite && opposite->GetOpposite()) {
std::cerr << "topology error: non-manifold edge\n";
valid = false;
}
if (origin->GetEdge(destination)) {
std::cerr << "topology error: duplicate edge\n";
valid = false;
}
}
if (valid) {
OsdHbrFace *face = hbrMesh->NewFace(numFaceVerts, &faceVerts[0], 0);
face->SetPtexIndex(ptexFaceIndex);
ptexFaceIndex += (numFaceVerts == 4) ? 1 : numFaceVerts;
if (uvData) {
int fvarWidth = hbrMesh->GetTotalFVarWidth();
const float *fvarFaceData = &uvData[faceDataOffset*fvarWidth];
for(int fvi=0; fvi<numFaceVerts; ++fvi) {
OsdHbrVertex *v = hbrMesh->GetVertex( faceVerts[fvi] );
OsdHbrFVarData& fvarData = v->GetFVarData(face);
if ( ! fvarData.IsInitialized() ) {
fvarData.SetAllData(fvarWidth, fvarFaceData);
} else
if (!fvarData.CompareAll(fvarWidth, fvarFaceData)) {
OsdHbrFVarData& fvarData = v->NewFVarData(face);
fvarData.SetAllData(fvarWidth, fvarFaceData);
}
fvarFaceData += fvarWidth;
}
}
}
faceDataOffset += numFaceVerts;
}
hbrMesh->Finish();
int level = 1;
OsdFarMeshFactory meshFactory(hbrMesh, level, false /*uniform*/);
OsdFarMesh * farMesh = meshFactory.Create(true /*ptex*/, true /*fvar*/);
delete hbrMesh;
std::cerr << "==== vertices ====\n";
OpenSubdiv::OsdCpuComputeContext *
computeContext = OpenSubdiv::OsdCpuComputeContext::Create(farMesh);
OpenSubdiv::OsdCpuComputeController *
computeController = new OpenSubdiv::OsdCpuComputeController();
OpenSubdiv::OsdCpuVertexBuffer * vertexBuffer =
OpenSubdiv::OsdCpuVertexBuffer::Create(3, farMesh->GetNumVertices());
vertexBuffer->UpdateData(vertexData, numVertices);
computeController->Refine(computeContext, vertexBuffer);
const float *vbuffer = vertexBuffer->BindCpuBuffer();
vertexBufferData.assign(vbuffer,
vbuffer+vertexBuffer->GetNumVertices() *
vertexBuffer->GetNumElements());
for (int i=0; i<vertexBuffer->GetNumVertices(); ++i) {
std::cerr << "(" << vbuffer[i*3+0] << ", "
<< vbuffer[i*3+1] << ", "
<< vbuffer[i*3+2] << ")\n";
}
std::cerr << "==== indices ====\n";
const std::vector<int> &ibuffer = farMesh->GetFaceVertices(level);
indexBufferData = ibuffer;
for (int i=0; i<ibuffer.size(); i+=4) {
std::cerr << "[" << ibuffer[i+0] << " "
<< ibuffer[i+1] << " "
<< ibuffer[i+2] << " "
<< ibuffer[i+3] << "]\n";
}
std::cerr << "==== ptex coords data ====\n";
std::vector<int> const &
ptexCoords = farMesh->GetPtexCoordinates(level);
for (int i=0; i<ptexCoords.size(); i+=2) {
const int *p = &ptexCoords[i];
int faceIndex = i / 2;
int ptexFaceIndex = p[0];
int u = (p[1] >> 16) & 0xffff;
int v = (p[1] & 0xffff);
std::cerr << faceIndex << ": ";
std::cerr << " ptex face: " << ptexFaceIndex;
std::cerr << " uvoffset: ("
<< (float)u/(1<<level) << ", " << (float)v/(1<<level)
<< ")";
if (faceIndex < 0) {
std::cerr << " non-quad coarse face";
}
std::cerr << "\n";
}
std::cerr << "==== fvar coords data ====\n";
if (uvData) {
std::vector<float> const & fvarCoords = farMesh->GetFVarData(level);
uvBufferData = fvarCoords;
for (int i=0; i<fvarCoords.size(); i+=4*2) {
const float *uv = &fvarCoords[i];
int faceIndex = i / (4*2);
std::cerr << faceIndex << ":";
std::cerr << " (" << uv[0*2+0] << "," << uv[0*2+1] << ")";
std::cerr << " (" << uv[1*2+0] << "," << uv[1*2+1] << ")";
std::cerr << " (" << uv[2*2+0] << "," << uv[2*2+1] << ")";
std::cerr << " (" << uv[3*2+0] << "," << uv[3*2+1] << ")";
std::cerr << "\n";
}
}
return farMesh;
}
static void
buildCylinder()
{
float vertexData[] = {
1.0,-1.0,-1.0,
1.0, 1.0,-1.0,
-1.0,-1.0,-1.0,
-1.0, 1.0,-1.0,
-1.0,-1.0, 1.0,
-1.0, 1.0, 1.0,
1.0,-1.0, 1.0,
1.0, 1.0, 1.0,
};
int numVertices = (sizeof(vertexData) / sizeof(vertexData[0])) / 3;
int indexData[] = {
0, 1, 3, 2,
2, 3, 5, 4,
4, 5, 7, 6,
6, 7, 1, 0,
};
int numIndices = sizeof(indexData) / sizeof(indexData[0]);
int faceData[] = {
4, 4, 4, 4,
};
int numFaces = sizeof(faceData) / sizeof(faceData[0]);
float uvData[] = {
0.0,0.0, 0.0,1.0, 0.25,1.0, 0.25,0.0,
0.25,0.0, 0.25,1.0, 0.5,1.0, 0.5,0.0,
0.5,0.0, 0.5,1.0, 0.75,1.0, 0.75,0.0,
0.75,0.0, 0.75,1.0, 1.0,1.0, 1.0,0.0,
};
buildMesh(vertexData, numVertices,
indexData, numIndices,
faceData, numFaces, uvData);
}
int vpWidth = 512, vpHeight = 512;
bool pressed = false;
int prevX = 0, prevY = 0;
float rotX = 0, rotY = 0;
void
reshape(int width, int height)
{
vpWidth = width;
vpHeight = height;
glutPostRedisplay();
}
void
display()
{
glClearColor(0.1, 0.1, 0.1, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
glViewport(0, 0, vpWidth, vpHeight);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glFrustum(-1, 1, -1, 1, 1, 10);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0, 0, -3);
glRotatef(rotX, 1, 0, 0);
glRotatef(rotY, 0, 1, 0);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glBegin(GL_QUADS);
int numQuads = indexBufferData.size() / 4;
for (int face=0; face<numQuads; ++face) {
for (int vert=0; vert<4; ++vert) {
const float *uv0 = &uvBufferData[(face*4+vert)*2];
glColor3f(uv0[0], uv0[1], 0.0);
glVertex3fv(&vertexBufferData[indexBufferData[face*4+vert]*3]);
}
}
glEnd();
glutSwapBuffers();
}
void
mouse(int button, int state, int x, int y)
{
if (button == GLUT_LEFT_BUTTON and state == GLUT_DOWN) {
pressed = true;
prevX = x;
prevY = y;
} else if (button == GLUT_LEFT_BUTTON and state == GLUT_UP) {
pressed = false;
}
}
void
motion(int x, int y)
{
if (pressed) {
rotX += float(y - prevY) / vpHeight * 360.0f;
rotY += float(x - prevX) / vpHeight * 360.0f;
prevX = x;
prevY = y;
}
glutPostRedisplay();
}
void
keyboard(unsigned char key, int, int)
{
switch (key) {
case 27:
case 'q':
exit(1);
default:
break;
}
}
int
main(int argc, char **argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE);
glutInitWindowSize(vpWidth, vpHeight);
glutCreateWindow("Test");
buildCylinder();
glutReshapeFunc(reshape);
glutDisplayFunc(display);
glutMouseFunc(mouse);
glutMotionFunc(motion);
glutKeyboardFunc(keyboard);
glutMainLoop();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment