//- // ========================================================================== // 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 #include #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; }