mirror of
https://github.com/PixarAnimationStudios/OpenSubdiv
synced 2024-12-04 17:00:06 +00:00
3ae50d1c50
New text: 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.
1894 lines
62 KiB
C++
1894 lines
62 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.
|
|
//
|
|
|
|
|
|
// Consolidated documentation for the source code collectively known
|
|
// as `mayaViewer`:
|
|
//
|
|
// OpenSubdivShader.h/cpp
|
|
// OpenSubdivShaderOverride.h/cpp
|
|
// osdMeshData.h/cpp
|
|
// hbrUtil.h/cpp
|
|
//
|
|
|
|
|
|
// --------------------------------------------------------------------------------------
|
|
// --------------------------------------------------------------------------------------
|
|
// ### OpenSubdivShader.h
|
|
//
|
|
// Class definition of custom MPxHwShaderNode for drawing OpenSubdiv patches.
|
|
class OpenSubdivShader : public MPxHwShaderNode
|
|
{
|
|
// Supports Viewport 2.0 rendering only.
|
|
// geometry/glGeometry, bind/glBind and unbind/glUnbind
|
|
// are not implemented at this time.
|
|
|
|
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.cpp
|
|
//
|
|
// Custom MPxHwShaderNode for drawing OpenSubdiv patches
|
|
|
|
/* GLEW needs to be included before OSD and Maya headers */
|
|
#include <GL/glew.h>
|
|
|
|
/* ... System includes ... */
|
|
/* ... Maya includes ... */
|
|
|
|
// `dyu - difference between DrawContext and DrawRegistry?`
|
|
//
|
|
// `I could take a stab but you can explain this in your sleep right?`
|
|
#include <osd/glDrawContext.h>
|
|
#include <osd/glDrawRegistry.h>
|
|
|
|
|
|
// ##### Attribute declarations
|
|
/* MObject OpenSubdivShader::... */
|
|
|
|
// ##### shaderSource attribute
|
|
//
|
|
// A default shader is compiled into the plug-in.
|
|
// The shaderSource attribute offers the ability to modify or
|
|
// replace the shader without having to recompile the plug-in.
|
|
MObject OpenSubdivShader::aShaderSource; // Optional shader file
|
|
|
|
static const char *defaultShaderSource =
|
|
#include "shader.inc"
|
|
;
|
|
|
|
// --------------------------------------------------------------------------------------
|
|
// ### EffectDrawRegistry
|
|
// An OpenSubdiv application builds its own draw registry in
|
|
// order to define parameters needed for its shader. In our
|
|
// case we use the attributes from the OpenSubdivShader to set up
|
|
// parameters and #define directives to pass through subdivision
|
|
// options and to control draw style. Client/application must
|
|
// specialize OsdGLDrawRegistry in order to provide an appearance
|
|
// for objects. Built-in shaders to not contain lighting code.
|
|
|
|
// 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::OsdGLDrawRegistry
|
|
//
|
|
// At the very least this class needs to define _CreateDrawSourceConfig
|
|
// and _CreateDrawConfig in order to define shader content.
|
|
//
|
|
class EffectDrawRegistry : public OpenSubdiv::OsdGLDrawRegistry<EffectDesc>
|
|
{
|
|
|
|
public:
|
|
EffectDrawRegistry() : _isAdaptive(true),
|
|
_diffuseId(0),
|
|
_shaderSource( defaultShaderSource )
|
|
{}
|
|
|
|
// ##### Accessors
|
|
//
|
|
// When setInternalValueInContext() gets triggered for certain
|
|
// attributes it will set dirty flags causing the shaders to
|
|
// be rebuilt.
|
|
//
|
|
/* 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 SourceConfigType * _CreateDrawSourceConfig(DescType const & desc);
|
|
|
|
// ##### Compile and link the shader
|
|
virtual ConfigType * _CreateDrawConfig(DescType const & desc, SourceConfigType const * sconfig);
|
|
|
|
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;
|
|
};
|
|
|
|
|
|
//
|
|
// #### _CreateDrawSourceConfig
|
|
//
|
|
// Called by base registry when a draw configuration is requested.
|
|
// Returns a shader source configuration which consists of a
|
|
// set of shader source code and compile-time configuration
|
|
// defines. These are cached by the effect registry for
|
|
// efficient re-use when rebuilding shaders.
|
|
//
|
|
EffectDrawRegistry::SourceConfigType *
|
|
EffectDrawRegistry::_CreateDrawSourceConfig(DescType const & desc)
|
|
{
|
|
Effect effect = desc.second;
|
|
|
|
// Create base draw configuration
|
|
SourceConfigType * sconfig =
|
|
BaseRegistry::_CreateDrawSourceConfig(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;
|
|
sconfig->geometryShader.AddDefine("SMOOTH_NORMALS");
|
|
|
|
} else {
|
|
// Configuration for adaptive refinement
|
|
sconfig->vertexShader.version = "#version 410\n";
|
|
sconfig->vertexShader.source = _shaderSource;
|
|
sconfig->vertexShader.AddDefine("VERTEX_SHADER");
|
|
}
|
|
assert(sconfig);
|
|
|
|
// Enable feature-adaptive face-varying UV generation
|
|
if (_isAdaptive) {
|
|
sconfig->geometryShader.AddDefine("FVAR_ADAPTIVE");
|
|
}
|
|
|
|
// Enable diffuse texture display if there is a valid texture map
|
|
if (_diffuseId != 0) {
|
|
sconfig->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
|
|
sconfig->vertexShader.AddDefine("NUM_ELEMENTS", "3");
|
|
|
|
// Initialize geometry shader
|
|
sconfig->geometryShader.version = "#version 410\n";
|
|
sconfig->geometryShader.source = _shaderSource;
|
|
sconfig->geometryShader.AddDefine("GEOMETRY_SHADER");
|
|
|
|
// Initialize fragment shader
|
|
sconfig->fragmentShader.version = "#version 410\n";
|
|
sconfig->fragmentShader.source = _shaderSource;
|
|
sconfig->fragmentShader.AddDefine("FRAGMENT_SHADER");
|
|
|
|
// Set up directives according to draw style
|
|
switch (effect) {
|
|
case kQuadFill:
|
|
sconfig->geometryShader.AddDefine("PRIM_QUAD");
|
|
sconfig->geometryShader.AddDefine("GEOMETRY_OUT_FILL");
|
|
sconfig->fragmentShader.AddDefine("PRIM_QUAD");
|
|
sconfig->fragmentShader.AddDefine("GEOMETRY_OUT_FILL");
|
|
break;
|
|
case kQuadLine:
|
|
sconfig->geometryShader.AddDefine("PRIM_QUAD");
|
|
sconfig->geometryShader.AddDefine("GEOMETRY_OUT_LINE");
|
|
sconfig->fragmentShader.AddDefine("PRIM_QUAD");
|
|
sconfig->fragmentShader.AddDefine("GEOMETRY_OUT_LINE");
|
|
break;
|
|
case kTriFill:
|
|
sconfig->geometryShader.AddDefine("PRIM_TRI");
|
|
sconfig->geometryShader.AddDefine("GEOMETRY_OUT_FILL");
|
|
sconfig->fragmentShader.AddDefine("PRIM_TRI");
|
|
sconfig->fragmentShader.AddDefine("GEOMETRY_OUT_FILL");
|
|
break;
|
|
case kTriLine:
|
|
sconfig->geometryShader.AddDefine("PRIM_TRI");
|
|
sconfig->geometryShader.AddDefine("GEOMETRY_OUT_LINE");
|
|
sconfig->fragmentShader.AddDefine("PRIM_TRI");
|
|
sconfig->fragmentShader.AddDefine("GEOMETRY_OUT_LINE");
|
|
break;
|
|
case kPoint:
|
|
sconfig->geometryShader.AddDefine("PRIM_POINT");
|
|
sconfig->fragmentShader.AddDefine("PRIM_POINT");
|
|
break;
|
|
}
|
|
|
|
return sconfig;
|
|
}
|
|
|
|
|
|
// 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;
|
|
|
|
//
|
|
// #### _CreateDrawConfig
|
|
//
|
|
// Called by base registry when a draw config is requested.
|
|
// Returns a compiled and linked shader program corresponding to
|
|
// a previously created DrawSourceConfig. The effect registry also
|
|
// caches these for efficient re-use.
|
|
//
|
|
EffectDrawRegistry::ConfigType *
|
|
EffectDrawRegistry::_CreateDrawConfig(
|
|
DescType const & desc)
|
|
SourceConfigType const * sconfig)
|
|
{
|
|
ConfigType * config = BaseRegistry::_CreateDrawConfig(desc.first, sconfig);
|
|
assert(config);
|
|
|
|
// Assign binding points to uniform blocks
|
|
/* struct Transform */
|
|
g_transformBinding = 0;
|
|
glUniformBlockBinding(config->program,
|
|
glGetUniformBlockIndex(config->program, "Transform"),
|
|
g_transformBinding);
|
|
|
|
/* struct Tessellation */
|
|
g_tessellationBinding = 1;
|
|
glUniformBlockBinding(config->program,
|
|
glGetUniformBlockIndex(config->program, "Tessellation"),
|
|
g_tessellationBinding);
|
|
|
|
/* struct Lighting */
|
|
g_lightingBinding = 2;
|
|
glUniformBlockBinding(config->program,
|
|
glGetUniformBlockIndex(config->program, "Lighting"),
|
|
g_lightingBinding);
|
|
|
|
// Specify texture buffer ID uniforms in shader
|
|
GLint loc;
|
|
if ((loc = glGetUniformLocation(config->program, "g_VertexBuffer")) != -1) {
|
|
glProgramUniform1i(config->program, loc, 0); // GL_TEXTURE0
|
|
}
|
|
if ((loc = glGetUniformLocation(config->program, "g_ValenceBuffer")) != -1) {
|
|
glProgramUniform1i(config->program, loc, 1); // GL_TEXTURE1
|
|
}
|
|
if ((loc = glGetUniformLocation(config->program, "g_QuadOffsetBuffer")) != -1) {
|
|
glProgramUniform1i(config->program, loc, 2); // GL_TEXTURE2
|
|
}
|
|
if ((loc = glGetUniformLocation(config->program, "g_patchLevelBuffer")) != -1) {
|
|
glProgramUniform1i(config->program, loc, 3); // GL_TEXTURE3
|
|
}
|
|
if ((loc = glGetUniformLocation(config->program, "g_uvFVarBuffer")) != -1) {
|
|
glProgramUniform1i(config->program, loc, 4); // GL_TEXTURE4
|
|
}
|
|
|
|
CHECK_GL_ERROR("CreateDrawConfig leave\n");
|
|
|
|
return config;
|
|
}
|
|
|
|
|
|
// --------------------------------------------------------------------------------------
|
|
// ### 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
|
|
//
|
|
// Called by openSubdivShaderOverride in updateDG.
|
|
// Pulls values for all non-internal attributes.
|
|
//
|
|
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
|
|
//
|
|
// Called by OpenSubdivShaderOverride for each renderItem.
|
|
// Binds the vertex buffer and calls glDrawElements for
|
|
// each patch.
|
|
//
|
|
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
|
|
//
|
|
// Utility routine which binds a single texture map
|
|
//
|
|
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
|
|
//
|
|
// Evaluates dirty flags and updates the effect registry if any
|
|
// attributes have changed that require the shader to be rebuilt
|
|
//
|
|
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
|
|
//
|
|
// Do all the work to build and install shader including
|
|
// set up buffer blocks for uniform variables, set up
|
|
// default lighting parameters, pass material uniforms
|
|
// and bind texture buffers used by texture maps and by
|
|
// OpenSubdiv's built-in shading code.
|
|
//
|
|
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::ConfigType *
|
|
config = _effectRegistry.GetDrawConfig(effectDesc);
|
|
|
|
// Install shader
|
|
GLuint program = config->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 PrimitiveIdBase;
|
|
} 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.GetQuadOffsetIndex();
|
|
tessellationData.PrimitiveIdBase = patch.GetPatchIndex();
|
|
|
|
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
|
|
//
|
|
// OpenSubdiv's geometric shading code depends on additional
|
|
// GL texture buffers. These are managed by the DrawContext
|
|
// and must be bound for use by the program in addition to
|
|
// any buffers used by the client/application shading code.
|
|
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
|
|
//
|
|
// Viewport 2.0 override for OpenSubdivShader
|
|
//
|
|
|
|
class OpenSubdivShaderOverride : public MHWRender::MPxShaderOverride
|
|
{
|
|
public:
|
|
/* ... Standard MPxShaderOverride methods and definitions ... */
|
|
|
|
private:
|
|
// ##### Pointer to associated shader
|
|
OpenSubdivShader *_shader;
|
|
|
|
};
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------------------
|
|
// --------------------------------------------------------------------------------------
|
|
// ### OpenSubdivShaderOverride.cpp
|
|
//
|
|
// Viewport 2.0 override for OpenSubdivShader, implementing
|
|
// custom shading for OpenSubdiv patches.
|
|
//
|
|
|
|
/* Include GLEW before Maya and OSD includes */
|
|
#include <GL/glew.h>
|
|
|
|
/* ... System includes ... */
|
|
/* ... Maya includes ... */
|
|
|
|
// Set up compute controllers for each available compute kernel.
|
|
//
|
|
// `dyu - what exactly is a compute controller?`
|
|
//
|
|
#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
|
|
//
|
|
// Informs us whenever an attribute on the shape node changes.
|
|
// Overkill since we really only want to know if the topology
|
|
// changes (e.g. an edge crease is added or changed) but Maya
|
|
// doesn't give us access to a callback that fine-grained.
|
|
// MMessage::PolyTopologyChangedCallback sounds promising
|
|
// but only calls back on a single change for any edit
|
|
// (i.e. not while dragging).
|
|
//
|
|
/*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
|
|
//
|
|
// Add a callback to inform us when topology might be changing
|
|
// so we can update the HBR mesh accordingly.
|
|
//
|
|
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
|
|
//
|
|
// Set up vertex buffer descriptors and geometry requirements
|
|
//
|
|
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.
|
|
//
|
|
// `dyu - can we add any more explanation about why this is done this way?`
|
|
{
|
|
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
|
|
//
|
|
// Save pointer to shader so we have access down the line.
|
|
// Call shader to update any attributes it needs to.
|
|
//
|
|
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;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------
|
|
// #### OsdBufferGenerator
|
|
//
|
|
// Vertex buffer generator for OpenSubdiv geometry
|
|
//
|
|
|
|
// `dyu - might need some explanation from takahito here`
|
|
|
|
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.h
|
|
//
|
|
// Class definition for OpenSubdiv MUserData used as custom data
|
|
// in OpenSubdivShaderOverride
|
|
//
|
|
|
|
// `dyu - hi again, need intelligent descriptions here too!`
|
|
// ` although maybe not if these are described elsewhere`
|
|
// ` getting a bit blurry now`
|
|
//
|
|
|
|
#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.
|
|
//
|
|
// `dyu - what exactly is a compute controller?`
|
|
// ` same decription from above...`
|
|
//
|
|
#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
|
|
//
|
|
// Initialize all context and buffers to NULL
|
|
//
|
|
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
|
|
//
|
|
// Delete meshes, clear contexts and buffers
|
|
//
|
|
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
|
|
}
|
|
|
|
|
|
//
|
|
// #### buildUVList
|
|
//
|
|
// Face-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
|
|
//
|
|
// If the topology of the mesh changes, or any attributes that affect
|
|
// how the mesh is computed the original HBR needs to be rebuilt
|
|
// which will trigger a rebuild of the FAR mesh and subsequent buffers.
|
|
//
|
|
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
|
|
//
|
|
// `dyu - some juicy details about mesh factory and what a compute context is`
|
|
//
|
|
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
|
|
//
|
|
// `dyu - more juicy details`
|
|
//
|
|
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);
|
|
}
|
|
}
|
|
|
|
//
|
|
// #### prepare
|
|
//
|
|
// If HBR mesh has been rebuilt the mesh factory and draw context
|
|
// also need to be rebuilt.
|
|
//
|
|
// `dyu - it seems like this could be merged with rebuildHBR`
|
|
//
|
|
void
|
|
OsdMeshData::prepare()
|
|
{
|
|
if (_needsInitializeMesh) {
|
|
initializeMesh();
|
|
createKernelDrawContext();
|
|
}
|
|
}
|
|
|
|
//
|
|
// #### updateGeometry
|
|
//
|
|
// Refine the geometry.
|
|
//
|
|
// `dyu - probably need more detail... do they need to know about the bind/subdata copy?`
|
|
//
|
|
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;
|
|
}
|
|
|
|
|
|
//
|
|
// #### bindPositionVBO
|
|
//
|
|
// Wrapper 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
|
|
//
|
|
// Wrapper for basic information needed to request
|
|
// face-varying data allocation from HBR
|
|
//
|
|
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
|
|
//
|
|
// Take Maya mesh topology input and build an HBR mesh. Optional
|
|
// input parameters direct the method to create Ptex texture
|
|
// coordinates or to copy given face-varying data to the
|
|
// HBR faces as they are created.
|
|
//
|
|
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;
|
|
}
|
|
|
|
|
|
// `dyu: do we need to have one of these dudes for shader.glsl?`
|