mayaViewer.cpp | |
---|---|
Consolidated documentation for the source code collectively known
as | |
--------------------------------------------------------------------------------------OpenSubdivShader.hClass definition of custom MPxHwShaderNode for drawing OpenSubdiv patches. | class OpenSubdivShader : public MPxHwShaderNode
{ |
| public:
/* ... Standard MPxHwShader methods and definitions ... */
private: |
OSD attributes | static MObject aLevel; // Subdivision level
static MObject aTessFactor; // GPU tesselation factor
static MObject aScheme; // Subdivision scheme
static MObject aAdaptive; // Feature-adaptive toggle
static MObject aWireframe; // Wireframe display toggle
static MObject aKernel; // Computation kernel
static MObject aInterpolateBoundary; // Boundary interpolation flag |
Material attributes | static MObject aDiffuse;
static MObject aAmbient;
static MObject aSpecular;
static MObject aShininess; |
Texture attributes | static MObject aDiffuseMapFile; // Texture filename
static MObject aUVSet; // UV set (defaults to current UV set)
static MObject aInterpolateUVBoundary; // Boundary interpolation flag for face-varying data (UVs) |
Shader attribute | static MObject aShaderSource; // Optional shader file
private:
/* ... private methods and member variables ... */
}; |
--------------------------------------------------------------------------------------OpenSubdivShader.cppCustom MPxHwShaderNode for drawing OpenSubdiv patches | |
OSD include initializing OpenSubdiv library version | /* Must be included before all other OSD includes */
#include <version.h>
/* GLEW needs to be included before OSD and Maya headers */
#include <GL/glew.h>
/* ... System includes ... */
/* ... Maya includes ... */ |
| #include <osd/glDrawContext.h>
#include <osd/glDrawRegistry.h> |
Attribute declarations | /* MObject OpenSubdivShader::... */ |
shaderSource attribute | MObject OpenSubdivShader::aShaderSource; // Optional shader file
static const char *defaultShaderSource =
#include "shader.inc"
; |
EffectDrawRegistry | |
Draw styles for EffectDrawRegistry | enum Effect
{
kQuadFill = 0,
kQuadLine = 1,
kTriFill = 2,
kTriLine = 3,
kPoint = 4,
};
typedef std::pair<OpenSubdiv::OsdPatchDescriptor, Effect> EffectDesc; |
Override of OpenSubdiv::OsdGLDrawInstance
| struct EffectDrawInstance : public OpenSubdiv::OsdGLDrawInstance
{
}; |
Override of OpenSubdiv::OsdGLDrawRegistryAt the very least this class needs to define _CreateDrawConfig and _CreateDrawInstance in order to define shader content. | class EffectDrawRegistry :
public OpenSubdiv::OsdGLDrawRegistry<EffectDesc, EffectDrawInstance>
{
public:
EffectDrawRegistry() : _isAdaptive(true),
_diffuseId(0),
_shaderSource( defaultShaderSource )
{} |
Accessors | /* isAdaptive */
void setIsAdaptive( bool ad ) { resetIfChanged(ad, _isAdaptive); }
bool getIsAdaptive() const { return _isAdaptive; }
/* diffuseId */
void setDiffuseId( GLuint dId ) { resetIfChanged(dId, _diffuseId); }
GLuint getDiffuseId() const { return _diffuseId; }
/* shaderSource */
void setShaderSource( std::string const & src ) { resetIfChanged(src, _shaderSource); }
std::string const & getShaderSource() const { return _shaderSource; }
protected: |
Build shader configuration | virtual ConfigType * _CreateDrawConfig(DescType const & desc); |
Compile and link the shader | virtual InstanceType * _CreateDrawInstance(DescType const & desc);
private: |
Clear the registry if a value has changed, triggering a rebuild | template< typename T>
void resetIfChanged( T newVal, T& curVal )
{
if ( newVal != curVal ) {
curVal = newVal;
Reset();
}
} |
Parameters mirroring attributes that affect shader generation | bool _isAdaptive;
GLuint _diffuseId;
std::string _shaderSource;
}; |
_CreateDrawConfig | EffectDrawRegistry::ConfigType *
EffectDrawRegistry::_CreateDrawConfig(DescType const & desc)
{
Effect effect = desc.second; |
Create base draw configuration | ConfigType * config = BaseRegistry::_CreateDrawConfig(desc.first);
if (desc.first.type != OpenSubdiv::kPerVertex) { |
Per-vertex descriptors are use for uniform refinement | if (effect == kQuadFill) effect = kTriFill;
if (effect == kQuadLine) effect = kTriLine;
config->geometryShader.AddDefine("SMOOTH_NORMALS");
} else { |
Configuration for adaptive refinement | config->vertexShader.version = "#version 410\n";
config->vertexShader.source = _shaderSource;
config->vertexShader.AddDefine("VERTEX_SHADER");
}
assert(config); |
Enable feature-adaptive face-varying UV generation | if (_isAdaptive) {
config->geometryShader.AddDefine("FVAR_ADAPTIVE");
} |
Enable diffuse texture display if there is a valid texture map | if (_diffuseId != 0) {
config->fragmentShader.AddDefine("USE_DIFFUSE_MAP");
} |
NUM_ELEMENTS should be set to the same value that is specified for the "numElements" argument when creating OsdVertexBuffers, e.g. OsdGLVertexBuffer | config->vertexShader.AddDefine("NUM_ELEMENTS", "3"); |
Initialize geometry shader | config->geometryShader.version = "#version 410\n";
config->geometryShader.source = _shaderSource;
config->geometryShader.AddDefine("GEOMETRY_SHADER"); |
Initialize fragment shader | config->fragmentShader.version = "#version 410\n";
config->fragmentShader.source = _shaderSource;
config->fragmentShader.AddDefine("FRAGMENT_SHADER"); |
Set up directives according to draw style | switch (effect) {
case kQuadFill:
config->geometryShader.AddDefine("PRIM_QUAD");
config->geometryShader.AddDefine("GEOMETRY_OUT_FILL");
config->fragmentShader.AddDefine("PRIM_QUAD");
config->fragmentShader.AddDefine("GEOMETRY_OUT_FILL");
break;
case kQuadLine:
config->geometryShader.AddDefine("PRIM_QUAD");
config->geometryShader.AddDefine("GEOMETRY_OUT_LINE");
config->fragmentShader.AddDefine("PRIM_QUAD");
config->fragmentShader.AddDefine("GEOMETRY_OUT_LINE");
break;
case kTriFill:
config->geometryShader.AddDefine("PRIM_TRI");
config->geometryShader.AddDefine("GEOMETRY_OUT_FILL");
config->fragmentShader.AddDefine("PRIM_TRI");
config->fragmentShader.AddDefine("GEOMETRY_OUT_FILL");
break;
case kTriLine:
config->geometryShader.AddDefine("PRIM_TRI");
config->geometryShader.AddDefine("GEOMETRY_OUT_LINE");
config->fragmentShader.AddDefine("PRIM_TRI");
config->fragmentShader.AddDefine("GEOMETRY_OUT_LINE");
break;
case kPoint:
config->geometryShader.AddDefine("PRIM_POINT");
config->fragmentShader.AddDefine("PRIM_POINT");
break;
}
return config;
} |
Global GL buffer IDs and binding points | GLuint g_transformUB = 0,
g_transformBinding = 0,
g_tessellationUB = 0,
g_tessellationBinding = 0,
g_lightingUB = 0,
g_lightingBinding = 0; |
compileShader | static GLuint
compileShader(GLenum shaderType,
OpenSubdiv::OsdDrawShaderSource const & common,
OpenSubdiv::OsdDrawShaderSource const & source)
{
const char *sources[4];
std::stringstream definitions;
for (size_t i = 0; i < common.defines.size(); ++i) {
definitions << "#define "
<< common.defines[i].first << " "
<< common.defines[i].second << "\n";
}
for (size_t i = 0; i < source.defines.size(); ++i) {
definitions << "#define "
<< source.defines[i].first << " "
<< source.defines[i].second << "\n";
}
std::string defString = definitions.str();
sources[0] = defString.c_str();
sources[1] = source.version.c_str();
sources[2] = common.source.c_str();
sources[3] = source.source.c_str();
GLuint shader = glCreateShader(shaderType);
glShaderSource(shader, 4, sources, NULL);
glCompileShader(shader);
GLint status;
glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
if (status == GL_FALSE) {
GLchar emsg[1024];
glGetShaderInfoLog(shader, sizeof(emsg), 0, emsg);
fprintf(stderr, "Error compiling GLSL shader: %s\n", emsg);
exit(0);
}
return shader;
} |
_CreateDrawInstance | EffectDrawRegistry::InstanceType *
EffectDrawRegistry::_CreateDrawInstance(DescType const & desc)
{
ConfigType * config = GetDrawConfig(desc);
assert(config);
GLuint vertexShader =
config->vertexShader.source.empty() ? 0 :
compileShader(GL_VERTEX_SHADER,
config->commonShader, config->vertexShader);
GLuint tessControlShader =
config->tessControlShader.source.empty() ? 0 :
compileShader(GL_TESS_CONTROL_SHADER,
config->commonShader, config->tessControlShader);
GLuint tessEvalShader =
config->tessEvalShader.source.empty() ? 0 :
compileShader(GL_TESS_EVALUATION_SHADER,
config->commonShader, config->tessEvalShader);
GLuint geometryShader =
config->geometryShader.source.empty() ? 0 :
compileShader(GL_GEOMETRY_SHADER,
config->commonShader, config->geometryShader);
GLuint fragmentShader =
config->fragmentShader.source.empty() ? 0 :
compileShader(GL_FRAGMENT_SHADER,
config->commonShader, config->fragmentShader);
InstanceType * instance = new InstanceType(); |
Create GL program object and attach all of our shaders | GLuint program = glCreateProgram();
if (vertexShader) glAttachShader(program, vertexShader);
if (tessControlShader) glAttachShader(program, tessControlShader);
if (tessEvalShader) glAttachShader(program, tessEvalShader);
if (geometryShader) glAttachShader(program, geometryShader);
if (fragmentShader) glAttachShader(program, fragmentShader); |
Create shader executable | glLinkProgram(program);
CHECK_GL_ERROR("CreateDrawInstance link program\n"); |
Flag shaders for deletion when program is finished | glDeleteShader(vertexShader);
glDeleteShader(tessControlShader);
glDeleteShader(tessEvalShader);
glDeleteShader(geometryShader);
glDeleteShader(fragmentShader); |
Check results of glLinkProgram | GLint status;
glGetProgramiv(program, GL_LINK_STATUS, &status);
if (status == GL_FALSE) {
GLchar emsg[1024];
glGetProgramInfoLog(program, sizeof(emsg), 0, emsg);
fprintf(stderr, "Error linking GLSL program : %s\n", emsg);
exit(0);
} |
Assign binding points to uniform blocks | /* struct Transform */
g_transformBinding = 0;
glUniformBlockBinding(program,
glGetUniformBlockIndex(program, "Transform"),
g_transformBinding);
/* struct Tessellation */
g_tessellationBinding = 1;
glUniformBlockBinding(program,
glGetUniformBlockIndex(program, "Tessellation"),
g_tessellationBinding);
/* struct Lighting */
g_lightingBinding = 2;
glUniformBlockBinding(program,
glGetUniformBlockIndex(program, "Lighting"),
g_lightingBinding); |
Specify texture buffer ID uniforms in shader | GLint loc;
if ((loc = glGetUniformLocation(program, "g_VertexBuffer")) != -1) {
glProgramUniform1i(program, loc, 0); // GL_TEXTURE0
}
if ((loc = glGetUniformLocation(program, "g_ValenceBuffer")) != -1) {
glProgramUniform1i(program, loc, 1); // GL_TEXTURE1
}
if ((loc = glGetUniformLocation(program, "g_QuadOffsetBuffer")) != -1) {
glProgramUniform1i(program, loc, 2); // GL_TEXTURE2
}
if ((loc = glGetUniformLocation(program, "g_patchLevelBuffer")) != -1) {
glProgramUniform1i(program, loc, 3); // GL_TEXTURE3
}
if ((loc = glGetUniformLocation(program, "g_uvFVarBuffer")) != -1) {
glProgramUniform1i(program, loc, 4); // GL_TEXTURE4
}
instance->program = program;
CHECK_GL_ERROR("CreateDrawInstance leave\n");
return instance;
} |
Shader construction and initialization | OpenSubdivShader::OpenSubdivShader() {...}
OpenSubdivShader::~OpenSubdivShader() {...}
void * OpenSubdivShader::creator() {...}
MStatus OpenSubdivShader::initialize() {...}
void OpenSubdivShader::postConstructor() {...}
MStatus
OpenSubdivShader::compute(const MPlug &, MDataBlock &)
{
return MS::kSuccess;
} |
updateAttributes | void
OpenSubdivShader::updateAttributes()
{
MObject object = thisMObject();
MFnDependencyNode depFn(object); |
Retrieve non-internal attributes | _diffuse = getColor(object, aDiffuse);
_ambient = getColor(object, aAmbient);
_specular = getColor(object, aSpecular);
getAttribute(object, aWireframe, &_wireframe);
getAttribute(object, aShininess, &_shininess); |
Pull on any plugs that might be connected | getAttribute(object, aDiffuseMapFile, &_diffuseMapFile);
} |
Main draw method | void
OpenSubdivShader::draw(const MHWRender::MDrawContext &mDrawContext,
OsdMeshData *data)
{
glPushAttrib(GL_POLYGON_BIT|GL_ENABLE_BIT);
if (_wireframe) {
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
} else {
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
}
CHECK_GL_ERROR("draw begin\n"); |
If shader source attribute has changed, update effectRegistry | updateRegistry(); |
Bind position vertex buffer | GLuint bPosition = data->bindPositionVBO();
OpenSubdiv::OsdGLDrawContext *osdDrawContext = data->getDrawContext();
glBindBuffer(GL_ARRAY_BUFFER, bPosition);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 3, 0);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, osdDrawContext->patchIndexBuffer); |
Get list of patches from OSD | OpenSubdiv::OsdPatchArrayVector const & patches =
osdDrawContext->patchArrays; |
Draw patches | for (size_t i = 0; i < patches.size(); ++i) {
OpenSubdiv::OsdPatchArray const & patch = patches[i];
GLint surfaceProgram = bindProgram(mDrawContext, osdDrawContext, patch);
if (patch.desc.type != OpenSubdiv::kPerVertex) {
glPatchParameteri(GL_PATCH_VERTICES, patch.patchSize);
glDrawElements(GL_PATCHES,
patch.numIndices, GL_UNSIGNED_INT,
reinterpret_cast<void *>(patch.firstIndex *
sizeof(unsigned int)));
} else {
glDrawElements(_scheme == OsdMeshData::kLoop ? GL_TRIANGLES : GL_LINES_ADJACENCY,
patch.numIndices, GL_UNSIGNED_INT,
reinterpret_cast<void *>(patch.firstIndex *
sizeof(unsigned int)));
}
CHECK_GL_ERROR("post draw\n");
} |
Clear state | glUseProgram(0);
glDisableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glPopAttrib();
CHECK_GL_ERROR("draw end\n");
} |
bindTexture | GLuint
OpenSubdivShader::bindTexture( const MString& filename, int textureUnit )
{
GLuint textureId = 0;
MHWRender::MTexture *mTexture = _theTextureManager->acquireTexture(filename);
if (mTexture) {
textureId = *(GLuint*)mTexture->resourceHandle();
glActiveTexture(GL_TEXTURE0+textureUnit);
glBindTexture(GL_TEXTURE_2D, textureId);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
} else {
fprintf(stderr,"Can't read texture file: \"%s\"\n", filename.asChar());
}
return textureId;
} |
updateRegistry | void
OpenSubdivShader::updateRegistry()
{
/* If adaptive flag has changed, update the effectRegistry accordingly */
if (_adaptiveDirty) {
_effectRegistry.setIsAdaptive(_adaptive);
_adaptiveDirty = false;
}
MHWRender::MRenderer *theRenderer = MHWRender::MRenderer::theRenderer();
_theTextureManager = theRenderer->getTextureManager();
/* If diffuse texture has changed, update the effectRegistry accordingly */
if (_diffuseMapDirty) {
GLuint diffMapId = bindTexture( _diffuseMapFile, DIFF_TEXTURE_UNIT );
_effectRegistry.setDiffuseId(diffMapId);
_diffuseMapDirty = false;
}
/* If shader source has changed, update the effectRegistry accordingly */
if (_shaderSourceDirty) {
if ( _shaderSource.empty() ) {
if ( _effectRegistry.getShaderSource() != defaultShaderSource ) {
_effectRegistry.setShaderSource(defaultShaderSource);
}
} else {
if ( _effectRegistry.getShaderSource() != _shaderSource ) {
_effectRegistry.setShaderSource(_shaderSource);
}
}
_shaderSourceDirty = false;
}
} |
bindProgram | GLuint
OpenSubdivShader::bindProgram(const MHWRender::MDrawContext & mDrawContext,
OpenSubdiv::OsdGLDrawContext *osdDrawContext,
const OpenSubdiv::OsdPatchArray & patch)
{
CHECK_GL_ERROR("bindProgram begin\n"); |
Primitives are triangles for Loop subdivision, quads otherwise | Effect effect = (_scheme == OsdMeshData::kLoop) ? kTriFill : kQuadFill;
EffectDesc effectDesc( patch.desc, effect ); |
Build shader | EffectDrawRegistry::InstanceType * instance = _effectRegistry.GetDrawInstance(effectDesc); |
Install shader | GLuint program = instance->program;
glUseProgram(program); |
Update and bind transform state | struct Transform {
float ModelViewMatrix[16];
float ProjectionMatrix[16];
float ModelViewProjectionMatrix[16];
} transformData;
setMatrix(mDrawContext.getMatrix(MHWRender::MDrawContext::kWorldViewMtx),
transformData.ModelViewMatrix);
setMatrix(mDrawContext.getMatrix(MHWRender::MDrawContext::kProjectionMtx),
transformData.ProjectionMatrix);
setMatrix(mDrawContext.getMatrix(MHWRender::MDrawContext::kWorldViewProjMtx),
transformData.ModelViewProjectionMatrix);
if (!g_transformUB) {
glGenBuffers(1, &g_transformUB);
glBindBuffer(GL_UNIFORM_BUFFER, g_transformUB);
glBufferData(GL_UNIFORM_BUFFER,
sizeof(transformData), NULL, GL_STATIC_DRAW);
};
glBindBuffer(GL_UNIFORM_BUFFER, g_transformUB);
glBufferSubData(GL_UNIFORM_BUFFER,
0, sizeof(transformData), &transformData);
glBindBuffer(GL_UNIFORM_BUFFER, 0);
glBindBufferBase(GL_UNIFORM_BUFFER, g_transformBinding, g_transformUB);
#define MAX_LEVEL 16 |
Update and bind tessellation state | struct Tessellation {
/* XXX: std140 layout requires vec4 aligns for every entry */
float TessLevelInner[MAX_LEVEL][4];
float TessLevelOuter[MAX_LEVEL][4];
int GregoryQuadOffsetBase;
int LevelBase;
} tessellationData;
int tessFactor = 2 << _tessFactor;
for (int i = 0; i < MAX_LEVEL; ++i) {
tessellationData.TessLevelInner[i][0] = float(std::max(tessFactor >> i, 1));
tessellationData.TessLevelOuter[i][0] = float(std::max(tessFactor >> i, 1));
}
tessellationData.GregoryQuadOffsetBase = patch.gregoryQuadOffsetBase;
tessellationData.LevelBase = patch.levelBase;
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); |
Update and bind lighting state | int numLights = mDrawContext.numberOfActiveLights();
struct Lighting {
struct Light {
float position[4];
float diffuse[4];
float ambient[4];
float specular[4];
} lightSource[2];
} lightingData;
memset(&lightingData, 0, sizeof(lightingData));
for (int i = 0; i < numLights && i < 2; ++i) {
MFloatPointArray positions;
MFloatVector direction;
float intensity;
MColor color;
bool hasDirection, hasPosition;
mDrawContext.getLightInformation(i, positions, direction, intensity,
color, hasDirection, hasPosition);
MMatrix modelView = mDrawContext.getMatrix(MHWRender::MDrawContext::kWorldViewMtx);
direction = MVector(direction) * modelView;
Lighting::Light &light = lightingData.lightSource[i];
if (hasDirection) {
light.position[0] = -direction[0];
light.position[1] = -direction[1];
light.position[2] = -direction[2];
for (int j = 0; j < 4; ++j) {
light.diffuse[j] = color[j] * intensity;
light.ambient[j] = color[j] * intensity;
light.specular[j] = color[j] * intensity;
}
}
}
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);
glBindBufferBase(GL_UNIFORM_BUFFER, g_lightingBinding, g_lightingUB); |
Update other uniforms | float color[4] = { 0, 0, 0, 1 };
_diffuse.get(color);
glProgramUniform4fv(program,
glGetUniformLocation(program, "diffuseColor"),
1, color);
_ambient.get(color);
glProgramUniform4fv(program,
glGetUniformLocation(program, "ambientColor"),
1, color);
_specular.get(color);
glProgramUniform4fv(program,
glGetUniformLocation(program, "specularColor"),
1, color);
glProgramUniform1f(program,
glGetUniformLocation(program, "shininess"),
_shininess); |
Bind diffuse map | if (_effectRegistry.getDiffuseId()!=0) {
GLint difmap = glGetUniformLocation(program, "diffuseMap");
glProgramUniform1i(program, difmap, DIFF_TEXTURE_UNIT);
} |
Bind all texture buffers | if (osdDrawContext->vertexTextureBuffer) {
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_BUFFER,
osdDrawContext->vertexTextureBuffer);
}
if (osdDrawContext->vertexValenceTextureBuffer) {
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_BUFFER,
osdDrawContext->vertexValenceTextureBuffer);
}
if (osdDrawContext->quadOffsetTextureBuffer) {
glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_BUFFER,
osdDrawContext->quadOffsetTextureBuffer);
}
if (osdDrawContext->patchLevelTextureBuffer) {
glActiveTexture(GL_TEXTURE3);
glBindTexture(GL_TEXTURE_BUFFER,
osdDrawContext->patchLevelTextureBuffer);
}
if (osdDrawContext->fvarDataTextureBuffer) {
glActiveTexture( GL_TEXTURE4 );
glBindTexture( GL_TEXTURE_BUFFER,
osdDrawContext->fvarDataTextureBuffer );
}
glActiveTexture(GL_TEXTURE0);
CHECK_GL_ERROR("bindProgram leave\n");
return program;
} |
--------------------------------------------------------------------------------------OpenSubdivShaderOverride.h | class OpenSubdivShaderOverride : public MHWRender::MPxShaderOverride
{
public:
/* ... Standard MPxShaderOverride methods and definitions ... */
private: |
Pointer to associated shader | OpenSubdivShader *_shader;
}; |
--------------------------------------------------------------------------------------OpenSubdivShaderOverride.cpp | /* Include GLEW before Maya and OSD includes */
#include <GL/glew.h>
/* ... System includes ... */
/* ... Maya includes ... */ |
Set up compute controllers for each available compute kernel.
| #include <osd/cpuComputeController.h>
OpenSubdiv::OsdCpuComputeController *g_cpuComputeController = 0;
#ifdef OPENSUBDIV_HAS_OPENMP
#include <osd/ompComputeController.h>
OpenSubdiv::OsdOmpComputeController *g_ompComputeController = 0;
#endif
#ifdef OPENSUBDIV_HAS_OPENCL
#include <osd/clComputeController.h>
cl_context g_clContext;
cl_command_queue g_clQueue;
#include "../common/clInit.h"
OpenSubdiv::OsdCLComputeController *g_clComputeController = 0;
#endif
#ifdef OPENSUBDIV_HAS_CUDA
#include <osd/cudaComputeController.h>
extern void cudaInit();
OpenSubdiv::OsdCudaComputeController *g_cudaComputeController = 0;
#endif
OpenSubdivShaderOverride::OpenSubdivShaderOverride(const MObject &obj) {...}
OpenSubdivShaderOverride::~OpenSubdivShaderOverride() {...}
MHWRender::MPxShaderOverride* OpenSubdivShaderOverride::creator(const MObject &obj) {...} |
attrChangedCB | /*static*/
void
OpenSubdivShaderOverride::attrChangedCB(MNodeMessage::AttributeMessage msg, MPlug & plug,
MPlug & otherPlug, void* clientData)
{
/* We only care if the plug is outMesh and the action is "evaluate" */
if ( msg & MNodeMessage::kAttributeEval ) {
OsdMeshData *meshData = (OsdMeshData*)clientData;
MFnDependencyNode depNodeFn(meshData->getDagPath().node());
if ( plug == depNodeFn.attribute("outMesh")) {
meshData->setMeshTopoDirty();
}
}
} |
addTopologyChangedCallbacks | void
OpenSubdivShaderOverride::addTopologyChangedCallbacks( const MDagPath& dagPath, OsdMeshData *data )
{
MStatus status = MS::kSuccess; |
Extract shape node and add callback to let us know when an attribute changes | MDagPath meshDagPath = dagPath;
meshDagPath.extendToShape();
MObject shapeNode = meshDagPath.node();
MCallbackId id = MNodeMessage::addAttributeChangedCallback(shapeNode,
attrChangedCB, data, &status ); |
Keep track so we can delete callbacks in destructor | if ( status ) {
_callbackIds.append( id );
} else {
cerr << "MNodeMessage.addCallback failed" << endl;
}
} |
initialize | MString
OpenSubdivShaderOverride::initialize(const MInitContext &initContext,
MInitFeedback &initFeedback)
{
MString empty; |
Roundabout way of getting positions pulled into our OsdBufferGenerator where we can manage the VBO memory size. Needs to be re-visited, re-factored, optimized, etc.
| {
MHWRender::MVertexBufferDescriptor positionDesc(
empty,
MHWRender::MGeometry::kPosition,
MHWRender::MGeometry::kFloat,
3);
addGeometryRequirement(positionDesc);
}
{
MHWRender::MVertexBufferDescriptor positionDesc(
"osdPosition",
MHWRender::MGeometry::kTangent,
MHWRender::MGeometry::kFloat,
3);
positionDesc.setSemanticName("osdPosition");
addGeometryRequirement(positionDesc);
} |
Build data object for managing OSD mesh, pass in as custom data | if (initFeedback.customData == NULL) {
OsdMeshData *data = new OsdMeshData(initContext.dagPath);
initFeedback.customData = data;
} |
Add a Maya callback so we can rebuild HBR mesh if topology changes | addTopologyChangedCallbacks( initContext.dagPath, (OsdMeshData*)initFeedback.customData );
return MString("OpenSubdivShaderOverride");
} |
updateDG | void
OpenSubdivShaderOverride::updateDG(MObject object)
{
if (object == MObject::kNullObj)
return; |
Save pointer to shader for access from draw() | _shader = static_cast<OpenSubdivShader*>(
MPxHwShaderNode::getHwShaderNodePtr(object)); |
Get updated attributes from shader | if (_shader) {
_shader->updateAttributes();
}
}
void
OpenSubdivShaderOverride::updateDevice()
{
}
void
OpenSubdivShaderOverride::endUpdate()
{
} |
Override draw method.Setup draw state and call osdMeshData methods to setup and refine geometry. Call to shader to do actual drawing. | bool
OpenSubdivShaderOverride::draw(
MHWRender::MDrawContext &context,
const MHWRender::MRenderItemList &renderItemList) const
{
{
MHWRender::MStateManager *stateMgr = context.getStateManager();
static const MDepthStencilState * depthState = NULL;
if (!depthState) {
MDepthStencilStateDesc desc;
depthState = stateMgr->acquireDepthStencilState(desc);
}
static const MBlendState *blendState = NULL;
if (!blendState) {
MBlendStateDesc desc;
int ntargets = desc.independentBlendEnable ?
MHWRender::MBlendState::kMaxTargets : 1;
for (int i = 0; i < ntargets; ++i) {
desc.targetBlends[i].blendEnable = false;
}
blendState = stateMgr->acquireBlendState(desc);
}
stateMgr->setDepthStencilState(depthState);
stateMgr->setBlendState(blendState);
}
for (int i = 0; i < renderItemList.length(); i++)
{
const MHWRender::MRenderItem *renderItem = renderItemList.itemAt(i);
OsdMeshData *data =
static_cast<OsdMeshData*>(renderItem->customData());
if (data == NULL) {
return false;
} |
If attributes or topology have changed which affect the HBR mesh it will be regenerated here. | data->rebuildHbrMeshIfNeeded(_shader);
const MHWRender::MVertexBuffer *position = NULL;
{
const MHWRender::MGeometry *geometry = renderItem->geometry();
for (int i = 0; i < geometry->vertexBufferCount(); i++) {
const MHWRender::MVertexBuffer *vb = geometry->vertexBuffer(i);
const MHWRender::MVertexBufferDescriptor &vdesc = vb->descriptor();
if (vdesc.name() == "osdPosition")
position = vb;
}
} |
If HBR mesh was regenerated, rebuild FAR mesh factory and recreate OSD draw context | data->prepare(); |
Refine geometry | data->updateGeometry(position); |
Draw patches | _shader->draw(context, data);
}
return true;
} |
OsdBufferGeneratorVertex buffer generator for OpenSubdiv geometry | |
| class OsdBufferGenerator : public MHWRender::MPxVertexBufferGenerator
{
public:
OsdBufferGenerator() {}
virtual ~OsdBufferGenerator() {}
virtual bool getSourceIndexing(
const MDagPath &dagPath,
MHWRender::MComponentDataIndexing &sourceIndexing) const
{
MFnMesh mesh(dagPath.node());
MIntArray vertexCount, vertexList;
mesh.getVertices(vertexCount, vertexList);
MUintArray &vertices = sourceIndexing.indices();
for (unsigned int i = 0; i < vertexList.length(); ++i)
vertices.append((unsigned int)vertexList[i]);
sourceIndexing.setComponentType(MComponentDataIndexing::kFaceVertex);
return true;
}
virtual bool getSourceStreams(const MDagPath &dagPath,
MStringArray &) const
{
return false;
}
#if MAYA_API_VERSION >= 201350
virtual void createVertexStream(
const MDagPath &dagPath,
MVertexBuffer &vertexBuffer,
const MComponentDataIndexing &targetIndexing,
const MComponentDataIndexing &,
const MVertexBufferArray &) const
{
#else
virtual void createVertexStream(
const MDagPath &dagPath, MVertexBuffer &vertexBuffer,
const MComponentDataIndexing &targetIndexing) const
{
#endif
const MVertexBufferDescriptor &desc = vertexBuffer.descriptor();
MFnMesh meshFn(dagPath);
int nVertices = meshFn.numVertices();
MFloatPointArray points;
meshFn.getPoints(points);
#if MAYA_API_VERSION >= 201350
float *buffer = static_cast<float*>(vertexBuffer.acquire(nVertices, true));
#else
float *buffer = static_cast<float*>(vertexBuffer.acquire(nVertices));
#endif
float *dst = buffer;
for (int i = 0; i < nVertices; ++i) {
*dst++ = points[i].x;
*dst++ = points[i].y;
*dst++ = points[i].z;
}
vertexBuffer.commit(buffer);
}
static MPxVertexBufferGenerator *positionBufferCreator()
{
return new OsdBufferGenerator();
}
private:
}; |
--------------------------------------------------------------------------------------osdMeshData.hClass definition for OpenSubdiv MUserData used as custom data in OpenSubdivShaderOverride | |
| #include <far/meshFactory.h>
#include <osd/glDrawContext.h>
#include <osd/cpuGLVertexBuffer.h>
#include <osd/cpuComputeContext.h>
#ifdef OPENSUBDIV_HAS_OPENCL
#include <osd/clGLVertexBuffer.h>
#include <osd/clComputeContext.h>
#endif
#ifdef OPENSUBDIV_HAS_CUDA
#include <osd/cudaGLVertexBuffer.h>
#include <osd/cudaComputeContext.h>
#endif
/* ... Maya includes ... */
class OsdMeshData : public MUserData
{
...
}; |
--------------------------------------------------------------------------------------osdMeshData.cpp | /* ... Maya includes ... */ |
Set up compute controllers for each available compute kernel.
| #include <osd/cpuDispatcher.h>
#include <osd/cpuComputeController.h>
extern OpenSubdiv::OsdCpuComputeController *g_cpuComputeController;
#ifdef OPENSUBDIV_HAS_OPENMP
#include <osd/ompDispatcher.h>
#include <osd/ompComputeController.h>
extern OpenSubdiv::OsdOmpComputeController *g_ompComputeController;
#endif
#ifdef OPENSUBDIV_HAS_OPENCL
#include <osd/clDispatcher.h>
#include <osd/clComputeController.h>
extern cl_context g_clContext;
extern cl_command_queue g_clQueue;
extern OpenSubdiv::OsdCLComputeController *g_clComputeController;
#endif
#ifdef OPENSUBDIV_HAS_CUDA
#include <osd/cudaDispatcher.h>
#include <osd/cudaComputeController.h>
extern OpenSubdiv::OsdCudaComputeController *g_cudaComputeController;
#endif
#include <osd/glDrawContext.h> |
Constructor | OsdMeshData::OsdMeshData(const MDagPath& meshDagPath) {...}
: MUserData(false),
{
_cpuComputeContext = NULL;
_cpuPositionBuffer = NULL;
#ifdef OPENSUBDIV_HAS_OPENCL
_clComputeContext = NULL;
_clPositionBuffer = NULL;
#endif
#ifdef OPENSUBDIV_HAS_CUDA
_cudaComputeContext = NULL;
_cudaPositionBuffer = NULL;
#endif
} |
Destructor | OsdMeshData::~OsdMeshData()
{
delete _hbrmesh;
delete _farmesh;
delete _drawContext;
delete _fvarDesc;
clearComputeContextAndVertexBuffer();
}
void
OsdMeshData::clearComputeContextAndVertexBuffer()
{
delete _cpuComputeContext;
_cpuComputeContext = NULL;
delete _cpuPositionBuffer;
_cpuPositionBuffer = NULL;
#ifdef OPENSUBDIV_HAS_CUDA
delete _cudaComputeContext;
_cudaComputeContext = NULL;
delete _cudaPositionBuffer;
_cudaPositionBuffer = NULL;
#endif
#ifdef OPENSUBDIV_HAS_OPENCL
delete _clComputeContext;
_clComputeContext = NULL;
delete _clPositionBuffer;
_clPositionBuffer = NULL;
#endif
} |
buildUVListFace-varying data expects a list of per-face per-vertex floats. This method reads the UVs from the mesh and concatenates them into such a list. | MStatus
OsdMeshData::buildUVList( MFnMesh& meshFn, std::vector<float>& uvList )
{
MStatus status = MS::kSuccess;
MItMeshPolygon polyIt( _meshDagPath );
MFloatArray uArray;
MFloatArray vArray; |
If user hasn't given us a UV set, use the current one | MString *uvSetPtr=NULL;
if ( _uvSet.numChars() > 0 ) {
if (uvSetNameIsValid(meshFn, _uvSet)) {
uvSetPtr = &_uvSet;
}
else {
MGlobal::displayWarning(MString("OpenSubdivShader: uvSet \""+_uvSet+"\" does not exist."));
}
} else {
uvSetPtr = NULL;
} |
Pull UVs from Maya mesh | status = meshFn.getUVs( uArray, vArray, uvSetPtr );
MCHECK_RETURN(status, "OpenSubdivShader: Error reading UVs");
if ( uArray.length() == 0 || vArray.length() == 0 )
{
MGlobal::displayWarning("OpenSubdivShader: Mesh has no UVs");
return MS::kFailure;
} |
Initalize list of UV values | uvList.clear();
uvList.resize( meshFn.numFaceVertices()*2 );
int uvListIdx = 0; |
For each face-vertex copy UVs into list, adjusting for renderman orientation | for ( polyIt.reset(); !polyIt.isDone(); polyIt.next() )
{
int faceIdx = polyIt.index();
unsigned int numPolyVerts = polyIt.polygonVertexCount();
for ( unsigned int faceVertIdx = 0;
faceVertIdx < numPolyVerts;
faceVertIdx++ )
{
int uvIdx;
polyIt.getUVIndex( faceVertIdx, uvIdx, uvSetPtr ); |
convert maya UV orientation to renderman orientation | uvList[ uvListIdx++ ] = uArray[ uvIdx ];
uvList[ uvListIdx++ ] = 1.0f - vArray[ uvIdx ];
}
}
return status;
} |
rebuildHbrMeshIfNeeded | void
OsdMeshData::rebuildHbrMeshIfNeeded(OpenSubdivShader *shader)
{
MStatus status = MS::kSuccess;
if (!_meshTopoDirty && !shader->getHbrMeshDirty())
return;
MFnMesh meshFn(_meshDagPath); |
Cache attribute values | _level = shader->getLevel();
_kernel = shader->getKernel();
_adaptive = shader->isAdaptive();
_uvSet = shader->getUVSet();
int level = (_level < 1) ? 1 : _level; |
Get Maya vertex topology and crease data | MIntArray vertexCount;
MIntArray vertexList;
meshFn.getVertices(vertexCount, vertexList);
MUintArray edgeIds;
MDoubleArray edgeCreaseData;
meshFn.getCreaseEdges(edgeIds, edgeCreaseData);
MUintArray vtxIds;
MDoubleArray vtxCreaseData;
meshFn.getCreaseVertices(vtxIds, vtxCreaseData);
if (vertexCount.length() == 0) return; |
Copy Maya vectors into std::vectors | std::vector<int> numIndices(&vertexCount[0], &vertexCount[vertexCount.length()]);
std::vector<int> faceIndices(&vertexList[0], &vertexList[vertexList.length()]);
std::vector<int> vtxCreaseIndices(&vtxIds[0], &vtxIds[vtxIds.length()]);
std::vector<double> vtxCreases(&vtxCreaseData[0], &vtxCreaseData[vtxCreaseData.length()]);
std::vector<double> edgeCreases(&edgeCreaseData[0], &edgeCreaseData[edgeCreaseData.length()]); |
Edge crease index is stored as pairs of vertex ids | int nEdgeIds = edgeIds.length();
std::vector<int> edgeCreaseIndices;
edgeCreaseIndices.resize(nEdgeIds*2);
for (int i = 0; i < nEdgeIds; ++i) {
int2 vertices;
status = meshFn.getEdgeVertices(edgeIds[i], vertices);
if (status.error()) {
MERROR(status, "OpenSubdivShader: Can't get edge vertices");
continue;
}
edgeCreaseIndices[i*2] = vertices[0];
edgeCreaseIndices[i*2+1] = vertices[1];
} |
Convert attribute enums to HBR enums (this is why the enums need to match) | HbrMeshUtil::SchemeType hbrScheme = (HbrMeshUtil::SchemeType) shader->getScheme();
OsdHbrMesh::InterpolateBoundaryMethod hbrInterpBoundary =
(OsdHbrMesh::InterpolateBoundaryMethod) shader->getInterpolateBoundary();
OsdHbrMesh::InterpolateBoundaryMethod hbrInterpUVBoundary =
(OsdHbrMesh::InterpolateBoundaryMethod) shader->getInterpolateUVBoundary(); |
Clear any existing face-varying descriptor | if (_fvarDesc) {
delete _fvarDesc;
_fvarDesc = NULL;
} |
Read UV data from maya and build per-face per-vert list of UVs for HBR face-varying data | std::vector< float > uvList;
status = buildUVList( meshFn, uvList );
if (! status.error()) { |
Create face-varying data descriptor. The memory required for indices and widths needs to stay alive as the HBR library only takes in the pointers and assumes the client will maintain the memory so keep _fvarDesc around as long as _hbrmesh is around. | int fvarIndices[] = { 0, 1 };
int fvarWidths[] = { 1, 1 };
_fvarDesc = new FVarDataDesc( 2, fvarIndices, fvarWidths, 2, hbrInterpUVBoundary );
}
if (_fvarDesc && hbrScheme != HbrMeshUtil::kCatmark) {
MGlobal::displayWarning("Face-varying not yet supported for Loop/Bilinear, using Catmull-Clark");
hbrScheme = HbrMeshUtil::kCatmark;
} |
Convert Maya mesh to internal HBR representation | _hbrmesh = ConvertToHBR(meshFn.numVertices(), numIndices, faceIndices,
vtxCreaseIndices, vtxCreases,
std::vector<int>(), std::vector<float>(),
edgeCreaseIndices, edgeCreases,
hbrInterpBoundary,
hbrScheme,
false, // no ptex
_fvarDesc,
_fvarDesc?&uvList:NULL); // yes fvar (if have UVs)
/* note: GL function can't be used in prepareForDraw API. */ |
Set dirty flag for FAR mesh factory to regenerate | _needsInitializeMesh = true; |
Mesh topology data is up to date | _meshTopoDirty = false;
shader->setHbrMeshDirty(false);
} |
initializeMesh
| void
OsdMeshData::initializeMesh()
{
if (!_hbrmesh)
return; |
Create FAR mesh | OpenSubdiv::FarMeshFactory<OpenSubdiv::OsdVertex>
meshFactory(_hbrmesh, _level, _adaptive);
_farmesh = meshFactory.Create(false, /* ptex coords */
true); /* fvar data */
delete _hbrmesh;
_hbrmesh = NULL;
int numTotalVertices = _farmesh->GetNumVertices(); |
Create context and vertex buffer | clearComputeContextAndVertexBuffer();
if (_kernel == kCPU) {
_cpuComputeContext = OpenSubdiv::OsdCpuComputeContext::Create(_farmesh);
_cpuPositionBuffer = OpenSubdiv::OsdCpuGLVertexBuffer::Create(3, numTotalVertices);
#ifdef OPENSUBDIV_HAS_OPENMP
} else if (_kernel == kOPENMP) {
_cpuComputeContext = OpenSubdiv::OsdCpuComputeContext::Create(_farmesh);
_cpuPositionBuffer = OpenSubdiv::OsdCpuGLVertexBuffer::Create(3, numTotalVertices);
#endif
#ifdef OPENSUBDIV_HAS_CUDA
} else if (_kernel == kCUDA) {
_cudaComputeContext = OpenSubdiv::OsdCudaComputeContext::Create(_farmesh);
_cudaPositionBuffer = OpenSubdiv::OsdCudaGLVertexBuffer::Create(3, numTotalVertices);
#endif
#ifdef OPENSUBDIV_HAS_OPENCL
} else if (_kernel == kCL) {
_clComputeContext = OpenSubdiv::OsdCLComputeContext::Create(_farmesh, g_clContext);
_clPositionBuffer = OpenSubdiv::OsdCLGLVertexBuffer::Create(3, numTotalVertices,
g_clContext);
#endif
}
_needsInitializeMesh = false; |
Get geometry from maya mesh | MFnMesh meshFn(_meshDagPath);
meshFn.getPoints(_pointArray);
_needsUpdate = true;
} |
createKernelDrawContext
| void
OsdMeshData::createKernelDrawContext()
{
delete _drawContext;
if (_kernel == kCPU) {
_drawContext = OpenSubdiv::OsdGLDrawContext::Create(_farmesh,
_cpuPositionBuffer,
false, // ptex coords?
true); // fvar data?
#ifdef OPENSUBDIV_HAS_OPENMP
} else if (_kernel == kOPENMP) {
_drawContext = OpenSubdiv::OsdGLDrawContext::Create(_farmesh,
_cpuPositionBuffer,
false, true);
#endif
#ifdef OPENSUBDIV_HAS_CUDA
} else if (_kernel == kCUDA) {
_drawContext = OpenSubdiv::OsdGLDrawContext::Create(_farmesh,
_cudaPositionBuffer,
false, true);
#endif
#ifdef OPENSUBDIV_HAS_OPENCL
} else if (_kernel == kCL) {
_drawContext = OpenSubdiv::OsdGLDrawContext::Create(_farmesh,
_clPositionBuffer,
false, true);
#endif
} else {
assert(false);
}
} |
prepareIf HBR mesh has been rebuilt the mesh factory and draw context also need to be rebuilt.
| void
OsdMeshData::prepare()
{
if (_needsInitializeMesh) {
initializeMesh();
createKernelDrawContext();
}
} |
updateGeometryRefine the geometry.
| void
OsdMeshData::updateGeometry(const MHWRender::MVertexBuffer *points)
{
int nCoarsePoints = _pointArray.length();
GLuint mayaPositionVBO = *static_cast<GLuint*>(points->resourceHandle());
int size = nCoarsePoints * 3 * sizeof(float);
if (_kernel == kCPU || _kernel == kOPENMP) {
float *d_pos = _cpuPositionBuffer->BindCpuBuffer();
glBindBuffer(GL_ARRAY_BUFFER, mayaPositionVBO);
glGetBufferSubData(GL_ARRAY_BUFFER, 0, size, d_pos);
g_cpuComputeController->Refine(_cpuComputeContext, _cpuPositionBuffer);
glBindBuffer(GL_ARRAY_BUFFER, 0);
#ifdef OPENSUBDIV_HAS_CUDA
} else if (_kernel == kCUDA) {
glBindBuffer(GL_COPY_READ_BUFFER, mayaPositionVBO);
glBindBuffer(GL_COPY_WRITE_BUFFER, _cudaPositionBuffer->BindVBO());
glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER,
0, 0, size);
g_cudaComputeController->Refine(_cudaComputeContext, _cudaPositionBuffer);
glBindBuffer(GL_COPY_READ_BUFFER, 0);
glBindBuffer(GL_COPY_WRITE_BUFFER, 0);
#endif
#ifdef OPENSUBDIV_HAS_OPENCL
} else if (_kernel == kCL) {
glBindBuffer(GL_COPY_READ_BUFFER, mayaPositionVBO);
glBindBuffer(GL_COPY_WRITE_BUFFER, _clPositionBuffer->BindVBO());
glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER,
0, 0, size);
g_clComputeController->Refine(_clComputeContext, _clPositionBuffer);
glBindBuffer(GL_COPY_READ_BUFFER, 0);
glBindBuffer(GL_COPY_WRITE_BUFFER, 0);
#endif
}
_needsUpdate = false;
} |
bindPositionVBOWrapper to bind position the correct vertex buffer according to the current compute kernel | GLuint
OsdMeshData::bindPositionVBO()
{
if (_kernel == kCPU) {
return _cpuPositionBuffer->BindVBO();
#ifdef OPENSUBDIV_HAS_OPENMP
} else if (_kernel == kOPENMP) {
return _cpuPositionBuffer->BindVBO();
#endif
#ifdef OPENSUBDIV_HAS_CUDA
} else if (_kernel == kCUDA) {
return _cudaPositionBuffer->BindVBO();
#endif
#ifdef OPENSUBDIV_HAS_OPENCL
} else if (_kernel == kCL) {
return _clPositionBuffer->BindVBO();
#endif
}
return 0;
} |
--------------------------------------------------------------------------------------hbrUtil.h | |
Face-varying data descriptor | class FVarDataDesc
{
public: |
Must be instantiated with descriptor information | FVarDataDesc( int count,
const int *indices, // start index for each face-varying variable
const int *widths, // width for each face-varying variable
int width,
OsdHbrMesh::InterpolateBoundaryMethod
boundary=OsdHbrMesh::k_InterpolateBoundaryNone
)
{
_fvarCount = count;
_totalfvarWidth = width;
_fvarIndices.assign( indices, indices+count );
_fvarWidths.assign( widths, widths+count );
_interpBoundary = boundary;
}
~FVarDataDesc() {} |
Accessors | int getCount() const { return _fvarCount; }
const int *getIndices() const { return &_fvarIndices.front(); }
const int *getWidths() const { return &_fvarWidths.front(); }
int getTotalWidth() const { return _totalfvarWidth; }
OsdHbrMesh::InterpolateBoundaryMethod
getInterpBoundary() const { return _interpBoundary; }
private: |
Face-varying data parameters | int _fvarCount; // Number of facevarying datums
std::vector<int> _fvarIndices; // Start indices of the facevarying data
std::vector<int> _fvarWidths; // Individual widths of facevarying data
int _totalfvarWidth; // Total widths of the facevarying data |
Boundary interpolation | OsdHbrMesh::InterpolateBoundaryMethod _interpBoundary;
};
extern "C" OsdHbrMesh * ConvertToHBR( ... ); |
--------------------------------------------------------------------------------------hbrUtil.cpp | |
ConvertToHBR | OsdHbrMesh *
ConvertToHBR( int nVertices,
std::vector<int> const & faceVertCounts,
std::vector<int> const & faceIndices,
std::vector<int> const & vtxCreaseIndices,
std::vector<double> const & vtxCreases,
std::vector<int> const & edgeCrease1Indices, // face index, local edge index
std::vector<float> const & edgeCreases1,
std::vector<int> const & edgeCrease2Indices, // 2 vertex indices (Maya friendly)
std::vector<double> const & edgeCreases2,
OsdHbrMesh::InterpolateBoundaryMethod interpBoundary,
HbrMeshUtil::SchemeType scheme,
bool usingPtex, // defaults to false
FVarDataDesc const * fvarDesc, // defaults to NULL
std::vector<float> const * fvarData // defaults to NULL
)
{
static OpenSubdiv::HbrBilinearSubdivision<OpenSubdiv::OsdVertex> _bilinear;
static OpenSubdiv::HbrLoopSubdivision<OpenSubdiv::OsdVertex> _loop;
static OpenSubdiv::HbrCatmarkSubdivision<OpenSubdiv::OsdVertex> _catmark; |
Build HBR mesh with/without face varying data, according to input data. If a face-varying descriptor is passed in its memory needs to stay alive as long as this hbrMesh is alive (for indices and widths arrays). | OsdHbrMesh *hbrMesh;
if ( fvarDesc )
{
if (scheme == HbrMeshUtil::kCatmark)
hbrMesh = new OsdHbrMesh(&_catmark, fvarDesc->getCount(),
fvarDesc->getIndices(),
fvarDesc->getWidths(),
fvarDesc->getTotalWidth());
else if (scheme == HbrMeshUtil::kLoop)
hbrMesh = new OsdHbrMesh(&_loop, fvarDesc->getCount(),
fvarDesc->getIndices(),
fvarDesc->getWidths(),
fvarDesc->getTotalWidth());
else
hbrMesh = new OsdHbrMesh(&_bilinear, fvarDesc->getCount(),
fvarDesc->getIndices(),
fvarDesc->getWidths(),
fvarDesc->getTotalWidth());
}
else
{
if (scheme == HbrMeshUtil::kCatmark)
hbrMesh = new OsdHbrMesh(&_catmark);
else if (scheme == HbrMeshUtil::kLoop)
hbrMesh = new OsdHbrMesh(&_loop);
else
hbrMesh = new OsdHbrMesh(&_bilinear);
} |
Create empty verts: actual vertices initialized in UpdatePoints(); | OpenSubdiv::OsdVertex v;
for (int i = 0; i < nVertices; ++i) {
hbrMesh->NewVertex(i, v);
}
std::vector<int> vIndex;
int nFaces = (int)faceVertCounts.size();
int fvcOffset = 0; // face-vertex count offset
int ptxIdx = 0; |
Collect vertex indices for each face and create HBR face | for (int fi = 0; fi < nFaces; ++fi)
{
int nFaceVerts = faceVertCounts[fi];
vIndex.resize(nFaceVerts);
bool valid = true;
for (int fvi = 0; fvi < nFaceVerts; ++fvi)
{
vIndex[fvi] = faceIndices[fvi + fvcOffset];
int vNextIndex = faceIndices[(fvi+1) % nFaceVerts + fvcOffset]; |
Check for non-manifold face | OsdHbrVertex * origin = hbrMesh->GetVertex(vIndex[fvi]);
OsdHbrVertex * destination = hbrMesh->GetVertex(vNextIndex);
if (!origin || !destination) {
OSD_ERROR("ERROR : An edge was specified that connected a nonexistent vertex");
valid = false;
}
if (origin == destination) {
OSD_ERROR("ERROR : An edge was specified that connected a vertex to itself");
valid = false;
}
OsdHbrHalfedge * opposite = destination->GetEdge(origin);
if (opposite && opposite->GetOpposite()) {
OSD_ERROR("ERROR : A non-manifold edge incident to more than 2 faces was found");
valid = false;
}
if (origin->GetEdge(destination)) {
OSD_ERROR("ERROR : An edge connecting two vertices was specified more than once. "
"It's likely that an incident face was flipped");
valid = false;
}
}
if ( valid )
{
if (scheme == HbrMeshUtil::kLoop) { |
For Loop subdivision, triangulate from vertex indices | int triangle[3];
triangle[0] = vIndex[0];
for (int fvi = 2; fvi < nFaceVerts; ++fvi) {
triangle[1] = vIndex[fvi-1];
triangle[2] = vIndex[fvi];
hbrMesh->NewFace(3, triangle, 0);
}
/* ptex not fully implemented for loop, yet */
/* fvar not fully implemented for loop, yet */
} else { |
For Catmull-Clark subdivision, create a quad face from vertices | /* bilinear subdivision not fully implemented */
OsdHbrFace *face = hbrMesh->NewFace(nFaceVerts, &(vIndex[0]), 0);
if (usingPtex) { |
Ptex textures will be used, set up ptex coordinates | face->SetPtexIndex(ptxIdx);
ptxIdx += (nFaceVerts == 4) ? 1 : nFaceVerts;
}
if (fvarData) { |
Face-varying data has been passed in, get pointer to data | int fvarWidth = hbrMesh->GetTotalFVarWidth();
const float *faceData = &(*fvarData)[ fvcOffset*fvarWidth ]; |
For each face vertex copy fvar data into hbr mesh | for(int fvi=0; fvi<nFaceVerts; ++fvi)
{
OsdHbrVertex *v = hbrMesh->GetVertex( vIndex[fvi] );
OsdHbrFVarData& fvarData = v->GetFVarData(face);
if ( ! fvarData.IsInitialized() )
{
fvarData.SetAllData( fvarWidth, faceData );
}
else if (!fvarData.CompareAll(fvarWidth, faceData))
{ |
If data exists for this face vertex, but is different (e.g. we're on a UV seam) create another fvar datum | OsdHbrFVarData& fvarData = v->NewFVarData(face);
fvarData.SetAllData( fvarWidth, faceData );
} |
Advance pointer to next set of face-varying data | faceData += fvarWidth;
}
}
}
} else {
OSD_ERROR("Face %d will be ignored\n", fi);
}
fvcOffset += nFaceVerts;
} |
Assign boundary interpolation methods | hbrMesh->SetInterpolateBoundaryMethod(interpBoundary);
if ( fvarDesc )
hbrMesh->SetFVarInterpolateBoundaryMethod(fvarDesc->getInterpBoundary()); |
Set edge crease in two different indexing way | size_t nEdgeCreases = edgeCreases1.size();
for (size_t i = 0; i < nEdgeCreases; ++i) {
if (edgeCreases1[i] <= 0.0)
continue;
OsdHbrHalfedge * e = hbrMesh->
GetFace(edgeCrease1Indices[i*2])->
GetEdge(edgeCrease1Indices[i*2+1]);
if (!e) {
OSD_ERROR("Can't find edge (face %d edge %d)\n",
edgeCrease1Indices[i*2], edgeCrease1Indices[i*2+1]);
continue;
}
e->SetSharpness(static_cast<float>(edgeCreases1[i]));
}
nEdgeCreases = edgeCreases2.size();
for (size_t i = 0; i < nEdgeCreases; ++i) {
if (edgeCreases2[i] <= 0.0)
continue;
OsdHbrVertex * v0 = hbrMesh->GetVertex(edgeCrease2Indices[i*2]);
OsdHbrVertex * v1 = hbrMesh->GetVertex(edgeCrease2Indices[i*2+1]);
OsdHbrHalfedge * e = NULL;
if (v0 && v1)
if (!(e = v0->GetEdge(v1)))
e = v1->GetEdge(v0);
if (!e) {
OSD_ERROR("ERROR can't find edge");
continue;
}
e->SetSharpness(static_cast<float>(edgeCreases2[i]));
} |
Set corner | {
size_t nVertexCreases = vtxCreases.size();
for (size_t i = 0; i < nVertexCreases; ++i) {
if (vtxCreases[i] <= 0.0)
continue;
OsdHbrVertex * v = hbrMesh->GetVertex(vtxCreaseIndices[i]);
if (!v) {
OSD_ERROR("Can't find vertex %d\n", vtxCreaseIndices[i]);
continue;
}
v->SetSharpness(static_cast<float>(vtxCreases[i]));
}
} |
Call finish to complete build of HBR mesh | hbrMesh->Finish();
return hbrMesh;
} |
| |