OpenSubdiv/examples/mayaPtexViewer/OpenSubdivPtexShader.cpp
manuelk e6e7c96a52 We need to leverage our per-patch ptex indexing scheme in the EvalLimit API.
- replace ptex indexing with the FarPtexCoord structure as a way to pass per-patch
  ptex data to the shaders.

  We are replacing a vector<int> arranged as :
  int[0] : ptex face index
  int[1] : (u,v) as 16 bits encoding the log2 coordinate of the top left corner

  Instead we are now using a struct arranged as :
  int[0] : ptex face index
  int[1] : is a bit-field containing u,v, rotation, depth and non-quad

  The u,v coordinates have been reduced to 10 bits instead of 16, which still
  gives us a lot of margin.

- Replace OsdVertexBufferDescriptor with something more adequate for general
  primvar representation (this name will probably eventually change...)

- Improve OsdPatchDescriptor
    - add a "loop" boolean (true if the patch is of loop type)
    - add a GetPatchSize() accessor

- OsdPatchArray :
    - remove some redundant elements (still more to do there)

- Fix all shader / examples / regressions & stuff to make this all work.

fixes #143
2013-03-22 18:20:50 -07:00

1187 lines
41 KiB
C++

//
// Copyright (C) Pixar. All rights reserved.
//
// This license governs use of the accompanying software. If you
// use the software, you accept this license. If you do not accept
// the license, do not use the software.
//
// 1. Definitions
// The terms "reproduce," "reproduction," "derivative works," and
// "distribution" have the same meaning here as under U.S.
// copyright law. A "contribution" is the original software, or
// any additions or changes to the software.
// A "contributor" is any person or entity that distributes its
// contribution under this license.
// "Licensed patents" are a contributor's patent claims that read
// directly on its contribution.
//
// 2. Grant of Rights
// (A) Copyright Grant- Subject to the terms of this license,
// including the license conditions and limitations in section 3,
// each contributor grants you a non-exclusive, worldwide,
// royalty-free copyright license to reproduce its contribution,
// prepare derivative works of its contribution, and distribute
// its contribution or any derivative works that you create.
// (B) Patent Grant- Subject to the terms of this license,
// including the license conditions and limitations in section 3,
// each contributor grants you a non-exclusive, worldwide,
// royalty-free license under its licensed patents to make, have
// made, use, sell, offer for sale, import, and/or otherwise
// dispose of its contribution in the software or derivative works
// of the contribution in the software.
//
// 3. Conditions and Limitations
// (A) No Trademark License- This license does not grant you
// rights to use any contributor's name, logo, or trademarks.
// (B) If you bring a patent claim against any contributor over
// patents that you claim are infringed by the software, your
// patent license from such contributor to the software ends
// automatically.
// (C) If you distribute any portion of the software, you must
// retain all copyright, patent, trademark, and attribution
// notices that are present in the software.
// (D) If you distribute any portion of the software in source
// code form, you may do so only under this license by including a
// complete copy of this license with your distribution. If you
// distribute any portion of the software in compiled or object
// code form, you may only do so under a license that complies
// with this license.
// (E) The software is licensed "as-is." You bear the risk of
// using it. The contributors give no express warranties,
// guarantees or conditions. You may have additional consumer
// rights under your local laws which this license cannot change.
// To the extent permitted under your local laws, the contributors
// exclude the implied warranties of merchantability, fitness for
// a particular purpose and non-infringement.
//
#if not defined(__APPLE__)
#include <GL/glew.h>
#if defined(WIN32)
#include <GL/wglew.h>
#endif
#endif
#include <maya/MFnTypedAttribute.h>
#include <maya/MFnNumericAttribute.h>
#include <maya/MFnEnumAttribute.h>
#include <maya/MFnDependencyNode.h>
#include <maya/MDrawContext.h>
#include <osd/glDrawContext.h>
#include <osd/glDrawRegistry.h>
#include <osd/glPtexTexture.h>
#include <Ptexture.h>
#include <PtexUtils.h>
#include <algorithm>
#include <fstream>
#include <sstream>
#include <string>
#include <utility>
#include "../common/maya_util.h"
#include "OpenSubdivPtexShader.h"
#include "osdPtexMeshData.h"
// Identifiers
MTypeId OpenSubdivPtexShader::id(0x88111);
MString OpenSubdivPtexShader::drawRegistrantId("OpenSubdivPtexShaderPlugin");
// Attributes
MObject OpenSubdivPtexShader::aLevel;
MObject OpenSubdivPtexShader::aTessFactor;
MObject OpenSubdivPtexShader::aScheme;
MObject OpenSubdivPtexShader::aKernel;
MObject OpenSubdivPtexShader::aInterpolateBoundary;
MObject OpenSubdivPtexShader::aAdaptive;
MObject OpenSubdivPtexShader::aWireframe;
MObject OpenSubdivPtexShader::aDiffuse;
MObject OpenSubdivPtexShader::aAmbient;
MObject OpenSubdivPtexShader::aSpecular;
// Ptex texture attributes
MObject OpenSubdivPtexShader::aDiffuseEnvironmentMapFile;
MObject OpenSubdivPtexShader::aSpecularEnvironmentMapFile;
MObject OpenSubdivPtexShader::aColorFile;
MObject OpenSubdivPtexShader::aDisplacementFile;
MObject OpenSubdivPtexShader::aOcclusionFile;
MObject OpenSubdivPtexShader::aEnableDisplacement;
MObject OpenSubdivPtexShader::aEnableColor;
MObject OpenSubdivPtexShader::aEnableOcclusion;
MObject OpenSubdivPtexShader::aEnableNormal;
MObject OpenSubdivPtexShader::aFresnelBias;
MObject OpenSubdivPtexShader::aFresnelScale;
MObject OpenSubdivPtexShader::aFresnelPower;
// Shader
MObject OpenSubdivPtexShader::aShaderSource;
static const char *defaultShaderSource =
#include "shader.inc"
;
// --------------------------------------------------------------------------------------
//
// Override of OpenSubdiv::OsdGLDrawRegistry
//
// --------------------------------------------------------------------------------------
struct Effect
{
bool color;
bool occlusion;
bool displacement;
bool normal;
bool operator < (const Effect &e) const
{
// no precedence, just need to know if the two are not equal
return (color == false && e.color != false) ||
(color == e.color && (occlusion == false && e.occlusion != false) ||
(occlusion == e.occlusion && (displacement == false && e.displacement != false) ||
(displacement == e.displacement && (normal == false && e.normal != false))));
}
};
typedef std::pair<OpenSubdiv::OsdPatchDescriptor, Effect> EffectDesc;
class EffectDrawRegistry : public OpenSubdiv::OsdGLDrawRegistry<EffectDesc>
{
public:
EffectDrawRegistry() : _isAdaptive(false),
_ptexColorValid(false),
_ptexDisplacementValid(false),
_ptexOcclusionValid(false),
_diffuseEnvironmentId(0),
_shaderSource( defaultShaderSource )
{}
// isAdaptive
void setIsAdaptive( bool ad ) { resetIfChanged(ad, _isAdaptive); }
bool getIsAdaptive() const { return _isAdaptive; }
// ptexColorValid
void setPtexColorValid( bool pcv ) { resetIfChanged(pcv, _ptexColorValid); }
bool getPtexColorValid() const { return _ptexColorValid; }
// ptexDisplacementValid
void setPtexDisplacementValid( bool pdv ) { resetIfChanged(pdv, _ptexDisplacementValid); }
bool getPtexDisplacementValid() const { return _ptexDisplacementValid; }
// ptexOcclusionValid
void setPtexOcclusionValid( bool pov ) { resetIfChanged(pov, _ptexOcclusionValid); }
bool getPtexOcclusionValid() const { return _ptexOcclusionValid; }
// diffuseEnvironmentId
void setDiffuseEnvironmentId( GLuint dId ) { resetIfChanged(dId, _diffuseEnvironmentId); }
GLuint getDiffuseEnvironmentId() const { return _diffuseEnvironmentId; }
// specularEnvironmentId
void setSpecularEnvironmentId( GLuint eId ) { resetIfChanged(eId, _specularEnvironmentId); }
GLuint getSpecularEnvironmentId() const { return _specularEnvironmentId; }
// shaderSource
void setShaderSource( std::string const & src ) { resetIfChanged(src, _shaderSource); }
std::string const & getShaderSource() const { return _shaderSource; }
protected:
virtual ConfigType *
_CreateDrawConfig(DescType const & desc, SourceConfigType const * sconfig);
virtual SourceConfigType *
_CreateDrawSourceConfig(DescType const & desc);
private:
// convenience method
template< typename T>
void resetIfChanged( T newVal, T& curVal )
{
if ( newVal != curVal ) {
curVal = newVal;
Reset();
}
}
// parameters
bool _isAdaptive;
bool _ptexColorValid;
bool _ptexDisplacementValid;
bool _ptexOcclusionValid;
GLuint _diffuseEnvironmentId;
GLuint _specularEnvironmentId;
std::string _shaderSource;
};
EffectDrawRegistry::SourceConfigType *
EffectDrawRegistry::_CreateDrawSourceConfig(DescType const & desc)
{
Effect effect = desc.second;
SourceConfigType * sconfig =
BaseRegistry::_CreateDrawSourceConfig(desc.first);
sconfig->commonShader.AddDefine("USE_PTEX_COORD"); // used by built-in OSD shaders
sconfig->commonShader.AddDefine("OSD_ENABLE_PATCH_CULL"); // used by built-in OSD shaders
sconfig->commonShader.AddDefine("OSD_ENABLE_SCREENSPACE_TESSELLATION");
bool quad = true;
if (desc.first.type != OpenSubdiv::kNonPatch) {
quad = false;
sconfig->tessEvalShader.source = _shaderSource +
sconfig->tessEvalShader.source;
sconfig->tessEvalShader.version = "#version 410\n";
if (_ptexDisplacementValid) {
if (effect.displacement) {
sconfig->tessEvalShader.AddDefine("USE_PTEX_DISPLACEMENT");
}
if (effect.normal) {
sconfig->fragmentShader.AddDefine("USE_PTEX_NORMAL");
} else if (effect.displacement) {
sconfig->geometryShader.AddDefine("FLAT_NORMALS");
}
}
} else {
sconfig->vertexShader.version = "#version 410\n";
sconfig->vertexShader.source = _shaderSource;
sconfig->vertexShader.AddDefine("VERTEX_SHADER");
if (effect.displacement && _ptexDisplacementValid) {
sconfig->geometryShader.AddDefine("USE_PTEX_DISPLACEMENT");
sconfig->geometryShader.AddDefine("FLAT_NORMALS");
}
}
assert(sconfig);
sconfig->vertexShader.AddDefine("NUM_ELEMENTS", "3");
sconfig->geometryShader.version = "#version 410\n";
sconfig->geometryShader.source = _shaderSource;
sconfig->geometryShader.AddDefine("GEOMETRY_SHADER");
sconfig->fragmentShader.version = "#version 410\n";
sconfig->fragmentShader.source = _shaderSource;
sconfig->fragmentShader.AddDefine("FRAGMENT_SHADER");
if (quad) {
sconfig->geometryShader.AddDefine("PRIM_QUAD");
sconfig->geometryShader.AddDefine("GEOMETRY_OUT_FILL");
sconfig->fragmentShader.AddDefine("PRIM_QUAD");
sconfig->fragmentShader.AddDefine("GEOMETRY_OUT_FILL");
} else {
sconfig->geometryShader.AddDefine("PRIM_TRI");
sconfig->geometryShader.AddDefine("GEOMETRY_OUT_FILL");
sconfig->fragmentShader.AddDefine("PRIM_TRI");
sconfig->fragmentShader.AddDefine("GEOMETRY_OUT_FILL");
}
if (effect.color && _ptexColorValid) {
sconfig->fragmentShader.AddDefine("USE_PTEX_COLOR");
}
if (effect.occlusion && _ptexOcclusionValid) {
sconfig->fragmentShader.AddDefine("USE_PTEX_OCCLUSION");
}
if (_diffuseEnvironmentId != 0) {
sconfig->fragmentShader.AddDefine("USE_DIFFUSE_ENV_MAP");
}
if (_specularEnvironmentId != 0) {
sconfig->fragmentShader.AddDefine("USE_SPECULAR_ENV_MAP");
}
return sconfig;
}
// XXX can/should these be in the registry instead of global?
GLuint g_transformUB = 0,
g_transformBinding = 0,
g_tessellationUB = 0,
g_tessellationBinding = 0,
g_lightingUB = 0,
g_lightingBinding = 0;
EffectDrawRegistry::ConfigType *
EffectDrawRegistry::_CreateDrawConfig(
DescType const & desc,
SourceConfigType const * sconfig)
{
ConfigType * config = BaseRegistry::_CreateDrawConfig(desc.first, sconfig);
assert(config);
// XXXdyu can use layout(binding=) with GLSL 4.20 and beyond
g_transformBinding = 0;
glUniformBlockBinding(config->program,
glGetUniformBlockIndex(config->program, "Transform"),
g_transformBinding);
g_tessellationBinding = 1;
glUniformBlockBinding(config->program,
glGetUniformBlockIndex(config->program, "Tessellation"),
g_tessellationBinding);
g_lightingBinding = 2;
glUniformBlockBinding(config->program,
glGetUniformBlockIndex(config->program, "Lighting"),
g_lightingBinding);
CHECK_GL_ERROR("CreateDrawConfig B \n");
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_ptexIndicesBuffer")) != -1) {
glProgramUniform1i(config->program, loc, 4); // GL_TEXTURE4
}
CHECK_GL_ERROR("CreateDrawConfig leave\n");
return config;
}
// XXX could be a singleton
EffectDrawRegistry effectRegistry;
// --------------------------------------------------------------------------------------
// --------------------------------------------------------------------------------------
OpenSubdivPtexShader::OpenSubdivPtexShader()
: _level(3),
_tessFactor(2),
_adaptive(true),
_wireframe(false),
_scheme(OsdPtexMeshData::kCatmark),
_kernel(OsdPtexMeshData::kCPU),
_interpolateBoundary(OsdPtexMeshData::kInterpolateBoundaryNone),
_enableColor(true),
_enableDisplacement(true),
_enableOcclusion(true),
_enableNormal(true),
_ptexColor(NULL),
_ptexDisplacement(NULL),
_ptexOcclusion(NULL),
_shaderSource( defaultShaderSource ),
_adaptiveDirty(false),
_diffEnvMapDirty(true),
_specEnvMapDirty(true),
_ptexColorDirty(true),
_ptexDisplacementDirty(true),
_ptexOcclusionDirty(true),
_shaderSourceDirty(false)
{
}
OpenSubdivPtexShader::~OpenSubdivPtexShader()
{
if (_ptexColor) delete _ptexColor;
if (_ptexDisplacement) delete _ptexDisplacement;
if (_ptexOcclusion) delete _ptexOcclusion;
}
void *
OpenSubdivPtexShader::creator()
{
return new OpenSubdivPtexShader();
}
MStatus
OpenSubdivPtexShader::initialize()
{
MFnTypedAttribute typedAttr;
MFnNumericAttribute numAttr;
MFnEnumAttribute enumAttr;
// level
aLevel = numAttr.create("level", "lv", MFnNumericData::kLong, 3);
numAttr.setInternal(true);
numAttr.setMin(1);
numAttr.setSoftMax(5);
numAttr.setMax(10);
// tessFactor
aTessFactor = numAttr.create("tessFactor", "tessf", MFnNumericData::kLong, 2);
numAttr.setInternal(true);
numAttr.setMin(1);
numAttr.setMax(10);
// scheme
aScheme = enumAttr.create("scheme", "sc", OsdPtexMeshData::kCatmark);
enumAttr.addField("Catmull-Clark", OsdPtexMeshData::kCatmark);
enumAttr.addField("Loop", OsdPtexMeshData::kLoop);
enumAttr.addField("Bilinear", OsdPtexMeshData::kBilinear);
enumAttr.setInternal(true);
// kernel
aKernel = enumAttr.create("kernel", "kn", OsdPtexMeshData::kCPU);
enumAttr.addField("CPU", OsdPtexMeshData::kCPU);
#ifdef OPENSUBDIV_HAS_OPENMP
enumAttr.addField("OpenMP", OsdPtexMeshData::kOPENMP);
#endif
#ifdef OPENSUBDIV_HAS_OPENCL
enumAttr.addField("CL", OsdPtexMeshData::kCL);
#endif
#ifdef OPENSUBDIV_HAS_CUDA
enumAttr.addField("CUDA", OsdPtexMeshData::kCUDA);
#endif
enumAttr.setInternal(true);
// interpolateBoundary
aInterpolateBoundary = enumAttr.create("interpolateBoundary", "ib",
OsdPtexMeshData::kInterpolateBoundaryNone);
enumAttr.addField("None", OsdPtexMeshData::kInterpolateBoundaryNone);
enumAttr.addField("Edge Only", OsdPtexMeshData::kInterpolateBoundaryEdgeOnly);
enumAttr.addField("Edge and Corner", OsdPtexMeshData::kInterpolateBoundaryEdgeAndCorner);
enumAttr.addField("Always Sharp", OsdPtexMeshData::kInterpolateBoundaryAlwaysSharp);
enumAttr.setInternal(true);
// adaptive
aAdaptive = numAttr.create("adaptive", "adp", MFnNumericData::kBoolean, true);
numAttr.setInternal(true);
// wireframe
aWireframe = numAttr.create("wireframe", "wf", MFnNumericData::kBoolean, false);
// material attributes
aDiffuse = numAttr.createColor("diffuse", "d");
numAttr.setDefault(0.6f, 0.6f, 0.7f);
aAmbient = numAttr.createColor("ambient", "a");
numAttr.setDefault(0.1f, 0.1f, 0.1f);
aSpecular = numAttr.createColor("specular", "s");
numAttr.setDefault(0.3f, 0.3f, 0.3f);
// Ptex Texture Attributes
//
// diffuseEnvironmentMapFile;
aDiffuseEnvironmentMapFile = typedAttr.create("diffuseEnvironmentMap", "difenv", MFnData::kString);
typedAttr.setInternal(true);
// don't let maya hold on to string when fileNode is disconnected
typedAttr.setDisconnectBehavior(MFnAttribute::kReset);
// specularEnvironmentMapFile;
aSpecularEnvironmentMapFile = typedAttr.create("specularEnvironmentMap", "specenv", MFnData::kString);
typedAttr.setInternal(true);
// don't let maya hold on to string when fileNode is disconnected
typedAttr.setDisconnectBehavior(MFnAttribute::kReset);
// colorFile;
aColorFile = typedAttr.create("colorFile", "cf", MFnData::kString);
typedAttr.setInternal(true);
// displacementFile;
aDisplacementFile = typedAttr.create("displacementFile", "df", MFnData::kString);
typedAttr.setInternal(true);
// occlusionFile;
aOcclusionFile = typedAttr.create("occlusionFile", "of", MFnData::kString);
typedAttr.setInternal(true);
// enableDisplacement;
aEnableDisplacement = numAttr.create("enableDisplacement", "end", MFnNumericData::kBoolean, 1);
numAttr.setInternal(true);
// enableColor;
aEnableColor = numAttr.create("enableColor", "enc", MFnNumericData::kBoolean, 1);
numAttr.setInternal(true);
// enableOcclusion;
aEnableOcclusion = numAttr.create("enableOcclusion", "eno", MFnNumericData::kBoolean, 1);
numAttr.setInternal(true);
// enableNormal;
aEnableNormal = numAttr.create("enableNormal", "enn", MFnNumericData::kBoolean, 1);
numAttr.setInternal(true);
// fresnelBias;
aFresnelBias = numAttr.create("fresnelBias", "fb", MFnNumericData::kFloat, 0.2f);
numAttr.setMin(0);
numAttr.setMax(1);
// fresnelScale;
aFresnelScale = numAttr.create("fresnelScale", "fs", MFnNumericData::kFloat, 1.0f);
numAttr.setMin(0);
numAttr.setSoftMax(1);
// fresnelPower;
aFresnelPower = numAttr.create("fresnelPower", "fp", MFnNumericData::kFloat, 5.0f);
numAttr.setMin(0);
numAttr.setSoftMax(10);
// shaderSource;
aShaderSource = typedAttr.create("shaderSource", "ssrc", MFnData::kString);
typedAttr.setInternal(true);
// add attributes
addAttribute(aLevel);
addAttribute(aTessFactor);
addAttribute(aScheme);
addAttribute(aKernel);
addAttribute(aInterpolateBoundary);
addAttribute(aAdaptive);
addAttribute(aWireframe);
addAttribute(aDiffuse);
addAttribute(aAmbient);
addAttribute(aSpecular);
addAttribute(aShaderSource);
addAttribute(aDiffuseEnvironmentMapFile);
addAttribute(aSpecularEnvironmentMapFile);
addAttribute(aColorFile);
addAttribute(aDisplacementFile);
addAttribute(aOcclusionFile);
addAttribute(aEnableDisplacement);
addAttribute(aEnableColor);
addAttribute(aEnableOcclusion);
addAttribute(aEnableNormal);
addAttribute(aFresnelBias);
addAttribute(aFresnelScale);
addAttribute(aFresnelPower);
return MS::kSuccess;
}
void
OpenSubdivPtexShader::postConstructor()
{
setMPSafe(false);
}
MStatus
OpenSubdivPtexShader::compute(const MPlug &plug, MDataBlock &data)
{
return MS::kSuccess;
}
bool
OpenSubdivPtexShader::getInternalValueInContext(const MPlug &plug, MDataHandle &handle, MDGContext &)
{
if (plug == aLevel) {
handle.setInt(_level);
} else if (plug == aTessFactor) {
handle.setInt(_tessFactor);
} else if (plug == aScheme) {
handle.setShort(_scheme);
} else if (plug == aKernel) {
handle.setShort(_kernel);
} else if (plug == aInterpolateBoundary) {
handle.setShort(_interpolateBoundary);
} else if (plug == aAdaptive) {
handle.setBool(_adaptive);
} else if (plug == aShaderSource) {
handle.setString( _shaderSourceFilename );
} else if (plug == aDiffuseEnvironmentMapFile) {
handle.setString(_diffEnvMapFile);
} else if (plug == aSpecularEnvironmentMapFile) {
handle.setString(_specEnvMapFile);
} else if (plug == aColorFile) {
handle.setString(_colorFile);
} else if (plug == aDisplacementFile) {
handle.setString(_displacementFile);
} else if (plug == aOcclusionFile) {
handle.setString(_occlusionFile);
} else if (plug == aEnableColor) {
handle.setBool(_enableColor);
} else if (plug == aEnableDisplacement) {
handle.setBool(_enableDisplacement);
} else if (plug == aEnableOcclusion) {
handle.setBool(_enableOcclusion);
} else if (plug == aEnableNormal) {
handle.setBool(_enableNormal);
}
return false;
}
bool
OpenSubdivPtexShader::setInternalValueInContext(const MPlug &plug, const MDataHandle &handle, MDGContext &)
{
if (plug == aLevel) {
_hbrMeshDirty = true;
_level = handle.asLong();
} else if (plug == aTessFactor) {
_tessFactor = handle.asLong();
} else if (plug == aScheme) {
_hbrMeshDirty = true;
_scheme = (OsdPtexMeshData::SchemeType)handle.asShort();
} else if (plug == aKernel) {
_hbrMeshDirty = true;
_kernel = (OsdPtexMeshData::KernelType)handle.asShort();
} else if (plug == aInterpolateBoundary) {
_hbrMeshDirty = true;
_interpolateBoundary = (OsdPtexMeshData::InterpolateBoundaryType)handle.asShort();
} else if (plug == aAdaptive) {
_hbrMeshDirty = true;
_adaptiveDirty = true;
_adaptive = handle.asBool();
} else if (plug == aShaderSource) {
_shaderSourceFilename = handle.asString();
std::ifstream ifs;
ifs.open(_shaderSourceFilename.asChar());
if (ifs.fail()) {
printf("Using default shader\n");
_shaderSource.clear();
_shaderSourceFilename.clear();
} else {
printf("Using %s shader\n", _shaderSourceFilename.asChar());
std::stringstream buffer;
buffer << ifs.rdbuf();
_shaderSource = buffer.str();
}
ifs.close();
_shaderSourceDirty = true;
} else if (plug == aDiffuseEnvironmentMapFile) {
_diffEnvMapDirty = true;
_diffEnvMapFile = handle.asString();
} else if (plug == aSpecularEnvironmentMapFile) {
_specEnvMapDirty = true;
_specEnvMapFile = handle.asString();
} else if (plug == aColorFile) {
_ptexColorDirty = true;
_colorFile = handle.asString();
} else if (plug == aDisplacementFile) {
_ptexDisplacementDirty = true;
_displacementFile = handle.asString();
} else if (plug == aOcclusionFile) {
_ptexOcclusionDirty = true;
_occlusionFile = handle.asString();
} else if (plug == aEnableColor) {
_enableColor = handle.asBool();
} else if (plug == aEnableDisplacement) {
_enableDisplacement = handle.asBool();
} else if (plug == aEnableOcclusion) {
_enableOcclusion = handle.asBool();
} else if (plug == aEnableNormal) {
_enableNormal = handle.asBool();
}
return false;
}
MStatus
OpenSubdivPtexShader::renderSwatchImage(MImage & image)
{
unsigned int width, height;
image.getSize(width, height);
unsigned char *p = image.pixels();
for (unsigned int i = 0; i < width*height; i++) {
*p++ = 0;
*p++ = 0;
*p++ = 0;
*p++ = 255;
}
return MS::kSuccess;
}
void
OpenSubdivPtexShader::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, aFresnelBias, &_fresnelBias);
getAttribute(object, aFresnelScale, &_fresnelScale);
getAttribute(object, aFresnelPower, &_fresnelPower);
// pull on any plugs that might be connected
}
void
OpenSubdivPtexShader::draw(const MHWRender::MDrawContext &mDrawContext,
OsdPtexMeshData *data)
{
MStatus status;
glPushAttrib(GL_POLYGON_BIT|GL_ENABLE_BIT);
// in ptexviewer, cull back face even in wireframe mode (for performance)
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
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();
GLuint bPosition = data->bindPositionVBO();
GLuint bNormal = data->bindNormalVBO();
OpenSubdiv::OsdGLDrawContext *osdDrawContext = data->getDrawContext();
glBindBuffer(GL_ARRAY_BUFFER, bPosition);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 3, 0);
glEnableVertexAttribArray(0);
if (not _adaptive) {
glBindBuffer(GL_ARRAY_BUFFER, bNormal);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 3, 0);
glEnableVertexAttribArray(1);
}
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, osdDrawContext->patchIndexBuffer);
OpenSubdiv::OsdPatchArrayVector const & patches =
osdDrawContext->patchArrays;
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::kNonPatch) {
glPatchParameteri(GL_PATCH_VERTICES, patch.desc.GetPatchSize());
if (osdDrawContext->vertexTextureBuffer) {
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_BUFFER,
osdDrawContext->vertexTextureBuffer);
glTexBuffer(GL_TEXTURE_BUFFER, GL_R32F, bPosition);
}
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->ptexCoordinateTextureBuffer) {
glActiveTexture(GL_TEXTURE4);
glBindTexture(GL_TEXTURE_BUFFER,
osdDrawContext->ptexCoordinateTextureBuffer);
}
glActiveTexture(GL_TEXTURE0);
glDrawElements(GL_PATCHES,
patch.numIndices, GL_UNSIGNED_INT,
reinterpret_cast<void *>(patch.firstIndex *
sizeof(unsigned int)));
} else {
GLint nonAdaptiveLevel = glGetUniformLocation(surfaceProgram,
"nonAdaptiveLevel");
if (nonAdaptiveLevel != -1) {
glProgramUniform1i(surfaceProgram, nonAdaptiveLevel, _level);
}
glDrawElements(_scheme == OsdPtexMeshData::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");
}
glUseProgram(0);
glDisableVertexAttribArray(0);
if (not _adaptive)
glDisableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glPopAttrib();
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
CHECK_GL_ERROR("draw end\n");
}
// ----------------------------------------------------------------------
// private methods
//
GLuint
OpenSubdivPtexShader::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);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
} else {
fprintf(stderr,"Can't read texture file: \"%s\"\n", filename.asChar());
}
return textureId;
}
//
// updateRegistry
//
// When attributes change which affect the shader compilation the
// effectRegistry needs to be updated with the new values
//
void
OpenSubdivPtexShader::updateRegistry()
{
// adaptive toggle
if (_adaptiveDirty) {
effectRegistry.setIsAdaptive(_adaptive);
_adaptiveDirty = false;
}
// ptex color file
if (_ptexColorDirty) {
bool ptexColorValid = bindPtexTexture(_colorFile, &_ptexColor, CLR_TEXTURE_UNIT);
effectRegistry.setPtexColorValid(ptexColorValid);
_ptexColorDirty = false;
}
// ptex displacement file
if (_ptexDisplacementDirty) {
bool ptexDisplacementValid = bindPtexTexture(_displacementFile, &_ptexDisplacement, DISP_TEXTURE_UNIT);
effectRegistry.setPtexDisplacementValid(ptexDisplacementValid);
_ptexDisplacementDirty = false;
}
// ptex occlusion file
if (_ptexOcclusionDirty) {
bool ptexOcclusionValid = bindPtexTexture(_occlusionFile, &_ptexOcclusion, OCC_TEXTURE_UNIT);
effectRegistry.setPtexOcclusionValid(ptexOcclusionValid);
_ptexOcclusionDirty = false;
}
MHWRender::MRenderer *theRenderer = MHWRender::MRenderer::theRenderer();
_theTextureManager = theRenderer->getTextureManager();
// diffuse environment map file
if (_diffEnvMapDirty) {
GLuint diffEnvMapId = bindTexture( _diffEnvMapFile, DIFF_TEXTURE_UNIT );
effectRegistry.setDiffuseEnvironmentId(diffEnvMapId);
_diffEnvMapDirty = false;
}
// specular environment map file
if (_specEnvMapDirty) {
GLuint specEnvMapId = bindTexture( _specEnvMapFile, ENV_TEXTURE_UNIT );
effectRegistry.setSpecularEnvironmentId(specEnvMapId);
_specEnvMapDirty = false;
}
// shader source
if (_shaderSourceDirty) {
if ( _shaderSource.empty() ) {
if ( effectRegistry.getShaderSource() != defaultShaderSource ) {
effectRegistry.setShaderSource(defaultShaderSource);
}
} else {
if ( effectRegistry.getShaderSource() != _shaderSource ) {
effectRegistry.setShaderSource(_shaderSource);
}
}
_shaderSourceDirty = false;
}
}
GLuint
OpenSubdivPtexShader::bindProgram(const MHWRender::MDrawContext & mDrawContext,
OpenSubdiv::OsdGLDrawContext *osdDrawContext,
const OpenSubdiv::OsdPatchArray & patch)
{
CHECK_GL_ERROR("bindProgram begin\n");
// Build shader
Effect effect;
effect.color = _enableColor;
effect.occlusion = _enableOcclusion;
effect.displacement = _enableDisplacement;
effect.normal = _enableNormal;
EffectDesc effectDesc( patch.desc, effect );
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);
// Update and bind tessellation state
struct Tessellation {
float TessLevel;
int GregoryQuadOffsetBase;
int LevelBase;
} tessellationData;
tessellationData.TessLevel = static_cast<float>(1 << _tessFactor);
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);
#ifdef USE_NON_IMAGE_BASED_LIGHTING
// 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 < 1; ++i) {
MFloatPointArray positions;
MFloatVector direction;
float intensity;
MColor color;
bool hasDirection, hasPosition;
mDrawContext.getLightInformation(i, positions, direction, intensity,
color, hasDirection, hasPosition);
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);
#endif
GLint eye = glGetUniformLocation(program, "eyePositionInWorld");
MPoint e = MPoint(0, 0, 0) *
mDrawContext.getMatrix(MHWRender::MDrawContext::kWorldViewInverseMtx);
glProgramUniform3f(program, eye,
static_cast<float>(e.x),
static_cast<float>(e.y),
static_cast<float>(e.z));
// 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, "fresnelBias"),
_fresnelBias);
glProgramUniform1f(program,
glGetUniformLocation(program, "fresnelScale"),
_fresnelScale);
glProgramUniform1f(program,
glGetUniformLocation(program, "fresnelPower"),
_fresnelPower);
// Ptex bindings
// color ptex
if (effectRegistry.getPtexColorValid()) {
GLint texData = glGetUniformLocation(program, "textureImage_Data");
glProgramUniform1i(program, texData, CLR_TEXTURE_UNIT + 0);
GLint texPacking = glGetUniformLocation(program, "textureImage_Packing");
glProgramUniform1i(program, texPacking, CLR_TEXTURE_UNIT + 1);
GLint texPages = glGetUniformLocation(program, "textureImage_Pages");
glProgramUniform1i(program, texPages, CLR_TEXTURE_UNIT + 2);
}
// displacement ptex
if (effectRegistry.getPtexDisplacementValid()) {
GLint texData = glGetUniformLocation(program, "textureDisplace_Data");
glProgramUniform1i(program, texData, DISP_TEXTURE_UNIT + 0);
GLint texPacking = glGetUniformLocation(program, "textureDisplace_Packing");
glProgramUniform1i(program, texPacking, DISP_TEXTURE_UNIT + 1);
GLint texPages = glGetUniformLocation(program, "textureDisplace_Pages");
glProgramUniform1i(program, texPages, DISP_TEXTURE_UNIT + 2);
}
// occlusion ptex
if (effectRegistry.getPtexOcclusionValid()) {
GLint texData = glGetUniformLocation(program, "textureOcclusion_Data");
glProgramUniform1i(program, texData, OCC_TEXTURE_UNIT + 0);
GLint texPacking = glGetUniformLocation(program, "textureOcclusion_Packing");
glProgramUniform1i(program, texPacking, OCC_TEXTURE_UNIT + 1);
GLint texPages = glGetUniformLocation(program, "textureOcclusion_Pages");
glProgramUniform1i(program, texPages, OCC_TEXTURE_UNIT + 2);
}
// diffuse environment map
if (effectRegistry.getDiffuseEnvironmentId() != 0) {
GLint difmap = glGetUniformLocation(program, "diffuseEnvironmentMap");
glProgramUniform1i(program, difmap, DIFF_TEXTURE_UNIT);
}
// specular environment map
if (effectRegistry.getSpecularEnvironmentId() != 0) {
GLint envmap = glGetUniformLocation(program, "specularEnvironmentMap");
glProgramUniform1i(program, envmap, ENV_TEXTURE_UNIT);
}
glActiveTexture(GL_TEXTURE0);
CHECK_GL_ERROR("bindProgram leave\n");
return program;
}
OpenSubdiv::OsdGLPtexTexture *
OpenSubdivPtexShader::loadPtex(const MString &filename)
{
if (filename.length()) {
printf("Load ptex %s\n", filename.asChar());
Ptex::String ptexError;
PtexTexture *ptex = PtexTexture::open(filename.asChar(),
ptexError, true);
if (ptex) {
// create osdptex
OpenSubdiv::OsdGLPtexTexture *osdptex =
OpenSubdiv::OsdGLPtexTexture::Create(ptex, 0,
/*gutterWidth=*/1,
/*pageMargin=*/8);
ptex->release();
return osdptex;
}
printf("Load ptex failed on file: \"%s\"\n", filename.asChar());
}
return NULL;
}
bool
OpenSubdivPtexShader::bindPtexTexture(const MString& ptexFilename,
OpenSubdiv::OsdGLPtexTexture **osdPtexPtr,
int samplerUnit)
{
// Reload ptex texture
if (*osdPtexPtr) delete *osdPtexPtr;
*osdPtexPtr = loadPtex(ptexFilename);
if (*osdPtexPtr == NULL)
return false;
// Rebind
glActiveTexture(GL_TEXTURE0 + samplerUnit + 0);
glBindTexture(GL_TEXTURE_2D_ARRAY, (*osdPtexPtr)->GetTexelsTexture());
glActiveTexture(GL_TEXTURE0 + samplerUnit + 1);
glBindTexture(GL_TEXTURE_BUFFER, (*osdPtexPtr)->GetLayoutTextureBuffer());
glActiveTexture(GL_TEXTURE0 + samplerUnit + 2);
glBindTexture(GL_TEXTURE_BUFFER, (*osdPtexPtr)->GetPagesTextureBuffer());
// Reset
glActiveTexture(GL_TEXTURE0);
return true;
}