Created
February 5, 2013 20:48
-
-
Save davidgyu/4717523 to your computer and use it in GitHub Desktop.
OpenSubdiv Example: Uniform Subdivision with Face-varying Data
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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