- Main Page
- Related Pages
- Modules
- Classes
- Examples

# MTexture.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.
    //
    // ==========================================================================
    //+

    // MTexture.cpp

    // DESCRIPTION: Texture object, that can be mipmapped. Eventually,
    //              this class will likely end up in the Maya API.
    //
    // AUTHOR: Christian Laforte
    //

    #include "MTexture.h"
    #include <maya/MStatus.h>
    #include <maya/MGlobal.h>
    #include "MNormalMapConverter.h"

    MTexture::MTexture()
    {
        // Initialize everything
        m_levels = NULL;
        m_numLevels = 0;
    }

    #define MIN(x, y) (((x) < (y)) ? (x) : (y) )
    #define MAX(x, y) (((x) > (y)) ? (x) : (y) )

    bool MTexture::load(MString filename,
                        MTexture::Type type,
                        bool mipmapped /* = true */,
                        GLenum target /* = GL_TEXTURE_2D */)
    {
        MImage image;
        MStatus stat = image.readFromFile(filename);
        if (!stat)
        {
            MGlobal::displayWarning("In MTexture::load(), file not found: \"" + filename + "\".");
            return false;
        }

        return set( image, type, mipmapped, target );
    }

    bool MTexture::set(MImage &image, Type type,
                       bool mipmapped /* = true */,
                       GLenum target /* = GL_TEXTURE_2D) */)
    {
        unsigned int i; // used as a temporary index.

        // Store the type of texture, and derive other parameters.
        // (Depth is assumed to be 4 bytes per pixel RGBA.
        // MImage always returns that pixel format anyway.)
        m_type = type;
        if ( (m_type == RGBA) || (m_type == NMAP) )
        {
            m_internalFormat = GL_RGBA8;
            m_format = GL_RGBA;
            m_componentFormat = GL_UNSIGNED_BYTE;
        }
        else if (m_type == HILO)
        {
    #if NVIDIA_SPECIFIC
            m_internalFormat = GL_SIGNED_HILO_NV;
            m_format = GL_HILO_NV;
            m_componentFormat = GL_SHORT;
    #endif
        }
        else assert(0);

        // Get the dimension of the texture.
        MStatus stat = image.getSize(m_width, m_height);
        assert(stat);
        m_mipmapped = mipmapped;

        unsigned int maxWidthLevels  = highestPowerOf2(m_width);
        unsigned int maxHeightLevels = highestPowerOf2(m_height);

        // Standard OpenGL doesn't accept width or height that are not power of 2.
        // If that's the case we resize the picture to the closest larger valid rectangle.
        bool widthIsExponent = (m_width == (unsigned int) (1 << maxWidthLevels));
        bool heightIsExponent = (m_height == (unsigned int) (1 << maxHeightLevels));

        if (!widthIsExponent || !heightIsExponent)
        {
            // Calculate the new width/height.
            if (!widthIsExponent)
                maxWidthLevels++;
            if (!heightIsExponent)
                maxHeightLevels++;

            // Resize the image, without bothering to preserve the aspect ratio.
            m_width = 1 << maxWidthLevels;
            m_height = 1 << maxHeightLevels;
            image.resize(m_width, m_height, false);
        }

        // Deallocate any existing levels
        if (m_levels != NULL)
        {
            for (i=0; i < m_numLevels; i++)
            {
                if (m_levels[i])
                {
                    delete [] m_levels[i];
                    m_levels[i] = NULL;
                }
            }
            delete [] m_levels;
        }

        // The number of mipmap levels cannot be greater than the exponent of width or height.
        // The number of mipmap levels is 1 for a non-mipmapped texture.
        // For mipmapped textures, m_numLevels = max level + 1.
        m_numLevels = mipmapped ? MAX(maxWidthLevels, maxHeightLevels) + 1 : 1;

        // Allocate the proper amount of memory, for the base level and the mipmaps.
        m_levels = new unsigned char* [m_numLevels];
        for (i=0; i < m_numLevels; i++)
        {
            m_levels[i] = new unsigned char [width(i) * height(i) * 4];
        }

        // Copy the base level. (the actual file texture)
        memcpy(m_levels[0], image.pixels(), m_width * m_height * 4);

        // Create the mipmapped levels.
        // NOTE REGARDING THE width_ratio and height_ratio:
        //     The smallest mipmap levels of non-square textures must be handled
        // carefully. Say we have a 8x2 texture. Mipmap levels will be
        // 4x1, 2x1, 1x1. We cannot simply multiply the current st coordinate by
        // 2 like we do for square textures to find the source st coordinates,
        // or we'll end up fetching outside of the source level. Instead, we
        // multiply the target s, t coordinates by the width and height ratio respectively.
        for (unsigned int current_level = 1; current_level < m_numLevels; current_level++)
        {
            unsigned int width_ratio = width(i-1) / width(i);
            unsigned int height_ratio = height(i-1) / height(i-1);
            unsigned int previous_level = current_level - 1;

            for (unsigned int target_t = 0; target_t < height(current_level); target_t++)
            {
                for (unsigned int target_s = 0; target_s < width(current_level); target_s++)
                {
                    // The st coordinates from the source level.
                    unsigned int source_s = target_s * width_ratio;
                    unsigned int source_t = target_t * height_ratio;
                    unsigned int source_s2 = source_s + ((width_ratio == 2) ? 1 : 0);
                    unsigned int source_t2 = source_t + ((height_ratio == 2) ? 1 : 0);

                    unsigned char *destination  = internalFetch(target_s,   target_t,   current_level);
                    unsigned char *source1      = internalFetch(source_s,   source_t,   previous_level);
                    unsigned char *source2      = internalFetch(source_s2,  source_t,   previous_level);
                    unsigned char *source3      = internalFetch(source_s,   source_t2,  previous_level);
                    unsigned char *source4      = internalFetch(source_s2,  source_t2,  previous_level);

                    // Average byte per byte.
                    unsigned int average1 = (*source1++ + *source2++ + *source3++ + *source4++) / 4;
                    *destination++ = average1;

                    unsigned int average2 = (*source1++ + *source2++ + *source3++ + *source4++) / 4;
                    *destination++ = average2;

                    unsigned int average3 = (*source1++ + *source2++ + *source3++ + *source4++) / 4;
                    *destination++ = average3;

                    unsigned int average4 = (*source1++ + *source2++ + *source3++ + *source4++) / 4;
                    *destination++ = average4;
                }
            }
        }

        if( type == NMAP )
        {
            // Convert each level to the NORMAL map format
            //
            MNormalMapConverter mapConverter;

            for (unsigned int i = 0; i < m_numLevels; i++)
            {
                mapConverter.convertToNormalMap( m_levels[i], width(i), height(i), MNormalMapConverter::RGBA, 2.0f );
            }
        }

        specify(target);

        return true;
    }

    bool MTexture::specify(GLenum target /* = GL_TEXTURE_2D */)
    {
        assert(glGetError() == GL_NO_ERROR);

        m_texObj.bind();

        assert(glGetError() == GL_NO_ERROR);

        for (unsigned int i=0; i < m_numLevels; i++)
        {
            glTexImage2D(target, i, m_internalFormat, width(i), height(i), 0,
                         m_format, m_componentFormat, m_levels[i]);

            assert(glGetError() == GL_NO_ERROR);
        }

        if (mipmapped())
        {
            // Mipmapping enabled
            m_texObj.parameter(GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
            assert(glGetError() == GL_NO_ERROR);

            m_texObj.parameter(GL_TEXTURE_MAG_FILTER, GL_LINEAR);
            assert(glGetError() == GL_NO_ERROR);
        }
        else
        {
            m_texObj.parameter(GL_TEXTURE_MIN_FILTER, GL_LINEAR);
            m_texObj.parameter(GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        }

        m_texObj.parameter(GL_TEXTURE_WRAP_S, GL_CLAMP);
        m_texObj.parameter(GL_TEXTURE_WRAP_T, GL_CLAMP);

        return true;
    }

    bool MTexture::bind()
    {
        m_texObj.bind();
        //specify(GL_TEXTURE_2D);

        return true;
    }

    int highestPowerOf2(int num)
    {
        int power = 0;

        while (num > 1)
        {
            power++;
            num = num >> 1;
        }

        return power;
    }