// // 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 defined(__APPLE__) #include #else #include #if defined(WIN32) #include #endif #endif #include "../common/maya_util.h" #include "OpenSubdivShader.h" #include "osdMeshData.h" #include #include #include #include #include #include #include #include #include #include #include #include // Identifiers MTypeId OpenSubdivShader::id(0x88110); MString OpenSubdivShader::drawRegistrantId("OpenSubdivShaderPlugin"); // Subdivision Attributes MObject OpenSubdivShader::aLevel; // Subdivision level MObject OpenSubdivShader::aTessFactor; // GPU tesselation factor MObject OpenSubdivShader::aScheme; // Catmull-Clark, Loop, Bilinear MObject OpenSubdivShader::aKernel; // Computation (CPU, CUDA, etc) MObject OpenSubdivShader::aInterpolateBoundary; // Boundary interpolation method MObject OpenSubdivShader::aInterpolateUVBoundary; // Face-varying interpolation MObject OpenSubdivShader::aAdaptive; // Feature-adaptive toggle // Appearance attributes MObject OpenSubdivShader::aWireframe; // Wireframe display toggle MObject OpenSubdivShader::aDiffuse; // Material parameters MObject OpenSubdivShader::aAmbient; MObject OpenSubdivShader::aSpecular; MObject OpenSubdivShader::aShininess; // Texture attributes MObject OpenSubdivShader::aDiffuseMapFile; // Input texture filename MObject OpenSubdivShader::aUVSet; // Optional UV set // 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 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 { 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: // Compile and link the shader virtual ConfigType * _CreateDrawConfig(DescType const & desc, SourceConfigType const * sconfig); // Build shader configuration virtual SourceConfigType * _CreateDrawSourceConfig(DescType const & desc); private: // Clear the registry if a value has changed, triggering a rebuild template< typename T> void resetIfChanged( T newVal, T& curVal ) { if ( newVal != curVal ) { curVal = newVal; Reset(); } } // Parameters mirroring attributes that affect shader generation bool _isAdaptive; GLuint _diffuseId; std::string _shaderSource; }; // #### _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::kNonPatch) { // 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 //* XXX dyu can use layout(binding=) with GLSL 4.20 and beyond */ /* 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; } EffectDrawRegistry g_effectRegistry; // -------------------------------------------------------------------------------------- // #### Shader construction and initialization OpenSubdivShader::OpenSubdivShader() : _level(3), _tessFactor(2), _adaptive(true), _wireframe(false), _scheme(OsdMeshData::kCatmark), _kernel(OsdMeshData::kCPU), _interpolateBoundary(OsdMeshData::kInterpolateBoundaryNone), _interpolateUVBoundary(OsdMeshData::kInterpolateBoundaryNone), _shaderSource( defaultShaderSource ), _hbrMeshDirty(true), _adaptiveDirty(true), _diffuseMapDirty(true), _shaderSourceDirty(false) { } OpenSubdivShader::~OpenSubdivShader() { } void * OpenSubdivShader::creator() { return new OpenSubdivShader(); } MStatus OpenSubdivShader::initialize() { MFnTypedAttribute typedAttr; MFnNumericAttribute numAttr; MFnEnumAttribute enumAttr; // Subdivision level aLevel = numAttr.create("level", "lv", MFnNumericData::kLong, 3); numAttr.setInternal(true); numAttr.setMin(1); numAttr.setSoftMax(5); numAttr.setMax(10); addAttribute(aLevel); // GPU tesselation factor aTessFactor = numAttr.create("tessFactor", "tessf", MFnNumericData::kLong, 2); numAttr.setInternal(true); numAttr.setMin(1); numAttr.setMax(10); addAttribute(aTessFactor); // Subdivision scheme aScheme = enumAttr.create("scheme", "sc", OsdMeshData::kCatmark); enumAttr.setInternal(true); enumAttr.addField("Catmull-Clark", OsdMeshData::kCatmark); enumAttr.addField("Loop", OsdMeshData::kLoop); enumAttr.addField("Bilinear", OsdMeshData::kBilinear); addAttribute(aScheme); // Computation kernel aKernel = enumAttr.create("kernel", "kn", OsdMeshData::kCPU); enumAttr.setInternal(true); enumAttr.addField("CPU", OsdMeshData::kCPU); #ifdef OPENSUBDIV_HAS_OPENMP enumAttr.addField("OpenMP", OsdMeshData::kOPENMP); #endif #ifdef OPENSUBDIV_HAS_OPENCL enumAttr.addField("CL", OsdMeshData::kCL); #endif #ifdef OPENSUBDIV_HAS_CUDA enumAttr.addField("CUDA", OsdMeshData::kCUDA); #endif addAttribute(aKernel); // Boundary interpolation flag aInterpolateBoundary = enumAttr.create("interpolateBoundary", "ib", OsdMeshData::kInterpolateBoundaryNone); enumAttr.addField("None", OsdMeshData::kInterpolateBoundaryNone); enumAttr.addField("Edge Only", OsdMeshData::kInterpolateBoundaryEdgeOnly); enumAttr.addField("Edge and Corner", OsdMeshData::kInterpolateBoundaryEdgeAndCorner); enumAttr.addField("Always Sharp", OsdMeshData::kInterpolateBoundaryAlwaysSharp); enumAttr.setInternal(true); addAttribute(aInterpolateBoundary); // Feature-adaptive toggle aAdaptive = numAttr.create("adaptive", "adp", MFnNumericData::kBoolean, true); numAttr.setInternal(true); addAttribute(aAdaptive); // Wireframe display toggle aWireframe = numAttr.create("wireframe", "wf", MFnNumericData::kBoolean, false); addAttribute(aWireframe); // Material attributes aDiffuse = numAttr.createColor("diffuse", "d"); numAttr.setDefault(0.6f, 0.6f, 0.7f); addAttribute(aDiffuse); aAmbient = numAttr.createColor("ambient", "a"); numAttr.setDefault(0.1f, 0.1f, 0.1f); addAttribute(aAmbient); aSpecular = numAttr.createColor("specular", "s"); numAttr.setDefault(0.3f, 0.3f, 0.3f); addAttribute(aSpecular); aShininess = numAttr.create("shininess", "shin", MFnNumericData::kFloat, 50.0f); numAttr.setMin(0); numAttr.setSoftMax(128.0f); addAttribute(aShininess); // Texture attributes aDiffuseMapFile = typedAttr.create("diffuseMap", "difmap", MFnData::kString); typedAttr.setInternal(true); /* don't let maya hold on to string when fileNode is disconnected */ typedAttr.setDisconnectBehavior(MFnAttribute::kReset); addAttribute(aDiffuseMapFile); // UV set (defaults to current UV set) aUVSet = typedAttr.create("uvSet", "uvs", MFnData::kString); typedAttr.setInternal(true); addAttribute(aUVSet); // Boundary interpolation flag for face-varying data (UVs) aInterpolateUVBoundary = enumAttr.create("interpolateUVBoundary", "iuvb", OsdMeshData::kInterpolateBoundaryNone); enumAttr.addField("None", OsdMeshData::kInterpolateBoundaryNone); enumAttr.addField("Edge Only", OsdMeshData::kInterpolateBoundaryEdgeOnly); enumAttr.addField("Edge and Corner", OsdMeshData::kInterpolateBoundaryEdgeAndCorner); enumAttr.addField("Always Sharp", OsdMeshData::kInterpolateBoundaryAlwaysSharp); enumAttr.setInternal(true); addAttribute(aInterpolateUVBoundary); // Optional shader source filename aShaderSource = typedAttr.create("shaderSource", "ssrc", MFnData::kString); typedAttr.setInternal(true); addAttribute(aShaderSource); return MS::kSuccess; } void OpenSubdivShader::postConstructor() { setMPSafe(false); } MStatus OpenSubdivShader::compute(const MPlug &, MDataBlock &) { return MS::kSuccess; } bool OpenSubdivShader::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 == aDiffuseMapFile) { handle.setString( _diffuseMapFile ); } else if (plug == aUVSet) { handle.setString( _uvSet ); } else if (plug == aInterpolateUVBoundary) { handle.setShort(_interpolateUVBoundary); } else if (plug == aShaderSource) { handle.setString( _shaderSourceFilename ); } return false; } bool OpenSubdivShader::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 = (OsdMeshData::SchemeType)handle.asShort(); } else if (plug == aKernel) { _hbrMeshDirty = true; _kernel = (OsdMeshData::KernelType)handle.asShort(); } else if (plug == aInterpolateBoundary) { _hbrMeshDirty = true; _interpolateBoundary = (OsdMeshData::InterpolateBoundaryType)handle.asShort(); } else if (plug == aAdaptive) { _hbrMeshDirty = true; _adaptiveDirty = true; _adaptive = handle.asBool(); } else if (plug == aDiffuseMapFile) { _diffuseMapDirty = true; _diffuseMapFile = handle.asString(); } else if (plug == aUVSet) { _hbrMeshDirty = true; _uvSet = handle.asString(); } else if (plug == aInterpolateUVBoundary) { _hbrMeshDirty = true; _interpolateUVBoundary = (OsdMeshData::InterpolateBoundaryType)handle.asShort(); } 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; } return false; } MStatus OpenSubdivShader::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; } // #### 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]; bindProgram(mDrawContext, osdDrawContext, patch); if (patch.desc.type != OpenSubdiv::kNonPatch) { glPatchParameteri(GL_PATCH_VERTICES, patch.patchSize); glDrawElements(GL_PATCHES, patch.numIndices, GL_UNSIGNED_INT, reinterpret_cast(patch.firstIndex * sizeof(unsigned int))); } else { glDrawElements(_scheme == OsdMeshData::kLoop ? GL_TRIANGLES : GL_LINES_ADJACENCY, patch.numIndices, GL_UNSIGNED_INT, reinterpret_cast(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) { g_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 ); g_effectRegistry.setDiffuseId(diffMapId); _diffuseMapDirty = false; } /* If shader source has changed, update the effectRegistry accordingly */ if (_shaderSourceDirty) { if ( _shaderSource.empty() ) { if ( g_effectRegistry.getShaderSource() != defaultShaderSource ) { g_effectRegistry.setShaderSource(defaultShaderSource); } } else { if ( g_effectRegistry.getShaderSource() != _shaderSource ) { g_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 = g_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(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); // 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 (g_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; }