MTextureCache.cpp

//-
// ==========================================================================
// Copyright (C) 1995 - 2006 Autodesk, Inc. and/or its licensors.  All 
// rights reserved.
//
// The coded instructions, statements, computer programs, and/or related 
// material (collectively the "Data") in these files contain unpublished 
// information proprietary to Autodesk, Inc. ("Autodesk") and/or its 
// licensors, which is protected by U.S. and Canadian federal copyright 
// law and by international treaties.
//
// The Data is provided for use exclusively by You. You have the right 
// to use, modify, and incorporate this Data into other products for 
// purposes authorized by the Autodesk software license agreement, 
// without fee.
//
// The copyright notices in the Software and this entire statement, 
// including the above license grant, this restriction and the 
// following disclaimer, must be included in all copies of the 
// Software, in whole or in part, and all derivative works of 
// the Software, unless such copies or derivative works are solely 
// in the form of machine-executable object code generated by a 
// source language processor.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND. 
// AUTODESK DOES NOT MAKE AND HEREBY DISCLAIMS ANY EXPRESS OR IMPLIED 
// WARRANTIES INCLUDING, BUT NOT LIMITED TO, THE WARRANTIES OF 
// NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR 
// PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE, OR 
// TRADE PRACTICE. IN NO EVENT WILL AUTODESK AND/OR ITS LICENSORS 
// BE LIABLE FOR ANY LOST REVENUES, DATA, OR PROFITS, OR SPECIAL, 
// DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES, EVEN IF AUTODESK 
// AND/OR ITS LICENSORS HAS BEEN ADVISED OF THE POSSIBILITY 
// OR PROBABILITY OF SUCH DAMAGES.
//
// ==========================================================================
//+

// MTextureCache.cpp

// DESCRIPTION: Texture cache, used to temporarily store textures.
//              Eventually, we'll likely expose the actual Maya
//              texture cache to plug-ins to improve memory utilization
//              and performance.
//
// PS: Thanks to sbrew@lucasarts.com for contributing several fixes 
// to this class! ;-)
//

#include <maya/MPlug.h>
#include "MTextureCache.h"
#include "NodeMonitor.h"

// Initialize the singleton instance, and the refcount is originally 0.
MTextureCache* MTextureCache::m_instance = NULL;
/*static*/ int MTextureCache::refcount = 0;

MTextureCacheElement::~MTextureCacheElement()
{ 
    if (m_texture) 
    {
        delete m_texture;
        m_texture = NULL;
    }
}


MTextureCache::~MTextureCache()
{
    if (m_textureTable.empty())
        return;

    // Delete all texture cache elements.
    //
    string_to_cacheElement_map::iterator p = m_textureTable.begin();
    for ( ; p != m_textureTable.end(); ++p)
    {
        delete p->second;
    }
}

// Return a reference to the texture. Need to dereference by calling "release".
MTexture* MTextureCache::texture(MObject textureObj, 
             MTexture::Type type /* = MTexture::RGBA */, 
             bool mipmapped /* = true */,
             GLenum target /* = GL_TEXTURE_2D */)
{
    // Get the name of the texture object.
    MString textureName = getNameFromObj(textureObj);
    
    // If this isn't a file texture node, or if it has no valid name, 
    // return NULL.
    if (!textureObj.hasFn(MFn::kFileTexture) ||
        textureName == "")
        return NULL;

    // Check if we already have a texCacheElement assigned to the given texture name.
    MTextureCacheElement *texCacheElement = 
        m_textureTable[textureName.asChar()];
    bool newTexture = !texCacheElement;
    bool textureDirty = texCacheElement && texCacheElement->fMonitor.dirty();

    if (textureDirty)
    {
        texCacheElement->fMonitor.stopWatching();
        delete texCacheElement->m_texture;
        texCacheElement->m_texture = NULL;
    }

    if (newTexture)
    {
        texCacheElement = new MTextureCacheElement;

        texCacheElement->fMonitor.setManager(this);
        
        // Add it to the map.
        m_textureTable[textureName.asChar()] = texCacheElement;
    }

    if (textureDirty || newTexture)
    {
        // Get the filename of the file texture node.
        MString textureFilename;
        MFnDependencyNode textureNode(textureObj);
        MPlug filenamePlug( textureObj, 
            textureNode.attribute(MString("fileTextureName")) );
        filenamePlug.getValue(textureFilename);
        
        // Create the MTexture
        texCacheElement->m_texture = new MTexture;

        // Monitor the given texture node for "dirty" or "rename" messages.
        texCacheElement->fMonitor.watch(textureObj);

        // Attempt to load the texture from disk and bind it in the OpenGL driver. 
        if (texCacheElement->m_texture->load(textureFilename, 
                                type, mipmapped, target) == false)
        {
            // An error occured. Most likely, it was impossible to 
            // open the given filename.
            // Clean up and return NULL.
            delete texCacheElement;
            texCacheElement = NULL;
            m_textureTable.erase(textureName.asChar());
            return NULL;
        }
    }

    // Update the last updated timestamp.
    texCacheElement->lastAccessedTimestamp = m_currentTimestamp;

    return texCacheElement->texture();
}

// Returns true if the texture was found and bound; 
// returns false otherwise.
bool MTextureCache::bind(MObject textureObj, 
                         MTexture::Type type /* = MTexture::RGBA */, 
                         bool mipmapped /* = true */,
                         GLenum target /* = GL_TEXTURE_2D */)
{
    // Get a reference to the texture, allocating it if necessary.
    MTexture* pTex = texture(textureObj, type, mipmapped, target);

    if (pTex)
    {
        // bind the texture.
        pTex->bind();
    
        return true;
    }
    
    return false;
}

void MTextureCache::onNodeRenamed(MObject& node, MString oldName, MString newName)
{
    // Remove the texture from the cache.
    MTextureCacheElement *texCacheElement = m_textureTable[oldName.asChar()];
    delete texCacheElement->m_texture;
    texCacheElement->m_texture = NULL;
}

void MTextureCache::incrementTimestamp(unsigned int increment /* =  1 */)
{
    m_currentTimestamp += increment;
    
    // Optionally, go through all textures and get rid of each of them that
    // is too old.
}