From 4fd3d49fc16685eb111470863a43e200e95bf693 Mon Sep 17 00:00:00 2001 From: Frederic Bouvier Date: Sun, 10 Oct 2010 16:51:37 +0200 Subject: [PATCH] Add mipmap control in the effect file --- projects/VC90/SimGear.vcproj | 8 + simgear/scene/material/TextureBuilder.cxx | 18 +- simgear/scene/material/mipmap.cxx | 356 ++++++++++++++++++++++ simgear/scene/material/mipmap.hxx | 50 +++ 4 files changed, 427 insertions(+), 5 deletions(-) create mode 100644 simgear/scene/material/mipmap.cxx create mode 100644 simgear/scene/material/mipmap.hxx diff --git a/projects/VC90/SimGear.vcproj b/projects/VC90/SimGear.vcproj index cdfeb222..fca9e993 100644 --- a/projects/VC90/SimGear.vcproj +++ b/projects/VC90/SimGear.vcproj @@ -1261,6 +1261,14 @@ RelativePath="..\..\simgear\scene\material\matmodel.hxx" > + + + + diff --git a/simgear/scene/material/TextureBuilder.cxx b/simgear/scene/material/TextureBuilder.cxx index 3773754b..61b62dfe 100644 --- a/simgear/scene/material/TextureBuilder.cxx +++ b/simgear/scene/material/TextureBuilder.cxx @@ -19,6 +19,7 @@ #endif #include "TextureBuilder.hxx" +#include "mipmap.hxx" #include "Pass.hxx" @@ -67,7 +68,7 @@ osg::Texture* TextureBuilder::buildFromType(Effect* effect, const string& type, typedef boost::tuple TexTuple; + string, MipMapTuple> TexTuple; EffectNameValue texEnvModesInit[] = { @@ -134,7 +135,7 @@ void TextureUnitBuilder::buildAttribute(Effect* effect, Pass* pass, catch (BuilderException& ) { SG_LOG(SG_INPUT, SG_ALERT, "No image file, " << "maybe the reader did not set the filename attribute, " - << "using white for type '" << type << "' on '" << pass->getName() << "', in " << prop->getPath() ); + << "using white on " << pass->getName()); texture = StateAttributeFactory::instance()->getWhiteTexture(); } pass->setTextureAttributeAndModes(unit, texture); @@ -181,7 +182,6 @@ EffectNameValue wrapModesInit[] = }; EffectPropertyMap wrapModes(wrapModesInit); - TexTuple makeTexTuple(Effect* effect, const SGPropertyNode* props, const SGReaderWriterXMLOptions* options, const string& texType) @@ -214,8 +214,15 @@ TexTuple makeTexTuple(Effect* effect, const SGPropertyNode* props, if (pImage) imageName = pImage->getStringValue(); string absFileName = osgDB::findDataFile(imageName, options); + + const SGPropertyNode* pMipmapControl + = getEffectPropertyChild(effect, props, "mipmap-control"); + MipMapTuple mipmapFunctions( AUTOMATIC, AUTOMATIC, AUTOMATIC, AUTOMATIC ); + if ( pMipmapControl ) + mipmapFunctions = makeMipMapTuple(effect, pMipmapControl, options); + return TexTuple(absFileName, minFilter, magFilter, sWrap, tWrap, rWrap, - texType); + texType, mipmapFunctions); } void setAttrs(const TexTuple& attrs, Texture* tex, @@ -228,7 +235,8 @@ void setAttrs(const TexTuple& attrs, Texture* tex, osgDB::ReaderWriter::ReadResult result = osgDB::Registry::instance()->readImage(imageName, options); if (result.success()) { - osg::Image* image = result.getImage(); + osg::ref_ptr image = result.getImage(); + image = computeMipmap( image, attrs.get<7>() ); tex->setImage(GL_FRONT_AND_BACK, image); int s = image->s(); int t = image->t(); diff --git a/simgear/scene/material/mipmap.cxx b/simgear/scene/material/mipmap.cxx new file mode 100644 index 00000000..b7ba9a7c --- /dev/null +++ b/simgear/scene/material/mipmap.cxx @@ -0,0 +1,356 @@ +// Copyright (C) 2010 Frederic Bouvier +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Library General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "mipmap.hxx" +#include "EffectBuilder.hxx" + +#include + +#include +#include + +#include +#include + +namespace simgear { namespace effect { + +EffectNameValue mipmapFunctionsInit[] = +{ + {"auto", AUTOMATIC}, + {"average", AVERAGE}, + {"sum", SUM}, + {"product", PRODUCT}, + {"min", MIN}, + {"max", MAX} +}; +EffectPropertyMap mipmapFunctions(mipmapFunctionsInit); + +MipMapTuple makeMipMapTuple(Effect* effect, const SGPropertyNode* props, + const SGReaderWriterXMLOptions* options) +{ + const SGPropertyNode* pMipmapR + = getEffectPropertyChild(effect, props, "function-r"); + MipMapFunction mipmapR = AUTOMATIC; + if (pMipmapR) + findAttr(mipmapFunctions, pMipmapR, mipmapR); + const SGPropertyNode* pMipmapG + = getEffectPropertyChild(effect, props, "function-g"); + MipMapFunction mipmapG = AUTOMATIC; + if (pMipmapG) + findAttr(mipmapFunctions, pMipmapG, mipmapG); + const SGPropertyNode* pMipmapB + = getEffectPropertyChild(effect, props, "function-b"); + MipMapFunction mipmapB = AUTOMATIC; + if (pMipmapB) + findAttr(mipmapFunctions, pMipmapB, mipmapB); + const SGPropertyNode* pMipmapA + = getEffectPropertyChild(effect, props, "function-a"); + MipMapFunction mipmapA = AUTOMATIC; + if (pMipmapA) + findAttr(mipmapFunctions, pMipmapA, mipmapA); + return MipMapTuple( mipmapR, mipmapG, mipmapB, mipmapA ); +} + + +unsigned char* imageData(unsigned char* ptr, GLenum pixelFormat, GLenum dataType, int width, int height, int packing, int column, int row=0, int image=0) +{ + if (!ptr) return NULL; + return ptr + + ( column * osg::Image::computePixelSizeInBits( pixelFormat, dataType ) ) / 8 + + row * osg::Image::computeRowWidthInBytes( width, pixelFormat, dataType, packing ) + + image * height * osg::Image::computeRowWidthInBytes( width, pixelFormat, dataType, packing ); +} + +template +void _writeColor(GLenum pixelFormat, T* data, float scale, osg::Vec4 value) +{ + switch(pixelFormat) + { + case(GL_DEPTH_COMPONENT): //intentionally fall through and execute the code for GL_LUMINANCE + case(GL_LUMINANCE): { *data++ = value.r()*scale; break; } + case(GL_ALPHA): { *data++ = value.a()*scale; break; } + case(GL_LUMINANCE_ALPHA): { *data++ = value.r()*scale; *data++ = value.a()*scale; break; } + case(GL_RGB): { *data++ = value.r()*scale; *data++ = value.g()*scale; *data++ = value.b()*scale; break; } + case(GL_RGBA): { *data++ = value.r()*scale; *data++ = value.g()*scale; *data++ = value.b()*scale; *data++ = value.a()*scale; break; } + case(GL_BGR): { *data++ = value.b()*scale; *data++ = value.g()*scale; *data++ = value.r()*scale; break; } + case(GL_BGRA): { *data++ = value.b()*scale; *data++ = value.g()*scale; *data++ = value.r()*scale; *data++ = value.a()*scale; break; } + } +} + +void setColor(unsigned char *ptr, GLenum pixelFormat, GLenum dataType, osg::Vec4 color) +{ + switch(dataType) + { + case(GL_BYTE): return _writeColor(pixelFormat, (char*)ptr, 128.0f, color); + case(GL_UNSIGNED_BYTE): return _writeColor(pixelFormat, (unsigned char*)ptr, 255.0f, color); + case(GL_SHORT): return _writeColor(pixelFormat, (short*)ptr, 32768.0f, color); + case(GL_UNSIGNED_SHORT): return _writeColor(pixelFormat, (unsigned short*)ptr, 65535.0f, color); + case(GL_INT): return _writeColor(pixelFormat, (int*)ptr, 2147483648.0f, color); + case(GL_UNSIGNED_INT): return _writeColor(pixelFormat, (unsigned int*)ptr, 4294967295.0f, color); + case(GL_FLOAT): return _writeColor(pixelFormat, (float*)ptr, 1.0f, color); + } +} + +template +osg::Vec4 _readColor(GLenum pixelFormat, T* data,float scale) +{ + switch(pixelFormat) + { + case(GL_DEPTH_COMPONENT): //intentionally fall through and execute the code for GL_LUMINANCE + case(GL_LUMINANCE): { float l = float(*data++)*scale; return osg::Vec4(l, l, l, 1.0f); } + case(GL_ALPHA): { float a = float(*data++)*scale; return osg::Vec4(1.0f, 1.0f, 1.0f, a); } + case(GL_LUMINANCE_ALPHA): { float l = float(*data++)*scale; float a = float(*data++)*scale; return osg::Vec4(l,l,l,a); } + case(GL_RGB): { float r = float(*data++)*scale; float g = float(*data++)*scale; float b = float(*data++)*scale; return osg::Vec4(r,g,b,1.0f); } + case(GL_RGBA): { float r = float(*data++)*scale; float g = float(*data++)*scale; float b = float(*data++)*scale; float a = float(*data++)*scale; return osg::Vec4(r,g,b,a); } + case(GL_BGR): { float b = float(*data++)*scale; float g = float(*data++)*scale; float r = float(*data++)*scale; return osg::Vec4(r,g,b,1.0f); } + case(GL_BGRA): { float b = float(*data++)*scale; float g = float(*data++)*scale; float r = float(*data++)*scale; float a = float(*data++)*scale; return osg::Vec4(r,g,b,a); } + } + return osg::Vec4(1.0f,1.0f,1.0f,1.0f); +} + +osg::Vec4 getColor(const unsigned char* ptr, GLenum pixelFormat, GLenum dataType) +{ + switch(dataType) + { + case(GL_BYTE): return _readColor(pixelFormat, (char*)ptr, 1.0f/128.0f); + case(GL_UNSIGNED_BYTE): return _readColor(pixelFormat, (unsigned char*)ptr, 1.0f/255.0f); + case(GL_SHORT): return _readColor(pixelFormat, (short*)ptr, 1.0f/32768.0f); + case(GL_UNSIGNED_SHORT): return _readColor(pixelFormat, (unsigned short*)ptr, 1.0f/65535.0f); + case(GL_INT): return _readColor(pixelFormat, (int*)ptr, 1.0f/2147483648.0f); + case(GL_UNSIGNED_INT): return _readColor(pixelFormat, (unsigned int*)ptr, 1.0f/4294967295.0f); + case(GL_FLOAT): return _readColor(pixelFormat, (float*)ptr, 1.0f); + } + return osg::Vec4(1.0f,1.0f,1.0f,1.0f); +} + +osg::Vec4::value_type computeAverage( int c, osg::Vec4 colors[2][2][2], bool colorValid[2][2][2] ) +{ + osg::Vec4::value_type r = 0; + osg::Vec4::value_type nb = 0; + for ( int k = 0; k < 2; ++k ) for ( int j = 0; j < 2; ++j ) for ( int i = 0; i < 2; ++i ) + { + if ( colorValid[i][j][k] ) + { + r += colors[i][j][k][c]; + nb += 1; + } + } + return r / nb; +} + +osg::Vec4::value_type computeSum( int c, osg::Vec4 colors[2][2][2], bool colorValid[2][2][2] ) +{ + osg::Vec4::value_type r = 0; + for ( int k = 0; k < 2; ++k ) for ( int j = 0; j < 2; ++j ) for ( int i = 0; i < 2; ++i ) + if ( colorValid[i][j][k] ) + { + r += colors[i][j][k][c]; + } + return r; +} + +osg::Vec4::value_type computeProduct( int c, osg::Vec4 colors[2][2][2], bool colorValid[2][2][2] ) +{ + osg::Vec4::value_type r = 1; + for ( int k = 0; k < 2; ++k ) for ( int j = 0; j < 2; ++j ) for ( int i = 0; i < 2; ++i ) + if ( colorValid[i][j][k] ) + { + r *= colors[i][j][k][c]; + } + return r; +} + +osg::Vec4::value_type computeMin( int c, osg::Vec4 colors[2][2][2], bool colorValid[2][2][2] ) +{ + osg::Vec4::value_type r = std::numeric_limits::max(); + for ( int k = 0; k < 2; ++k ) for ( int j = 0; j < 2; ++j ) for ( int i = 0; i < 2; ++i ) + if ( colorValid[i][j][k] ) + { + r = std::min( r, colors[i][j][k][c] ); + } + return r; +} + +osg::Vec4::value_type computeMax( int c, osg::Vec4 colors[2][2][2], bool colorValid[2][2][2] ) +{ + osg::Vec4::value_type r = std::numeric_limits::min(); + for ( int k = 0; k < 2; ++k ) for ( int j = 0; j < 2; ++j ) for ( int i = 0; i < 2; ++i ) + if ( colorValid[i][j][k] ) + { + r = std::max( r, colors[i][j][k][c] ); + } + return r; +} + +osg::Vec4::value_type computeComponent( int c, osg::Vec4 colors[2][2][2], bool colorValid[2][2][2], MipMapFunction f ) +{ + switch ( f ) + { + case AVERAGE: return computeAverage( c, colors, colorValid ); + case SUM: return computeSum( c, colors, colorValid ); + case PRODUCT: return computeProduct( c, colors, colorValid ); + case MIN: return computeMin( c, colors, colorValid ); + case MAX: return computeMax( c, colors, colorValid ); + } + return 0; +} + +osg::Vec4 computeColor( osg::Vec4 colors[2][2][2], bool colorValid[2][2][2], MipMapTuple attrs ) +{ + osg::Vec4 result; + result[0] = computeComponent( 0, colors, colorValid, attrs.get<0>() ); + result[1] = computeComponent( 1, colors, colorValid, attrs.get<1>() ); + result[2] = computeComponent( 2, colors, colorValid, attrs.get<2>() ); + result[3] = computeComponent( 3, colors, colorValid, attrs.get<3>() ); + return result; +} + +osg::Image* computeMipmap( osg::Image* image, MipMapTuple attrs ) +{ + bool computeMipmap = false; + if ( attrs.get<0>() != AUTOMATIC && + attrs.get<1>() != AUTOMATIC && + attrs.get<2>() != AUTOMATIC && + attrs.get<3>() != AUTOMATIC ) + { + computeMipmap = true; + } + else if ( attrs.get<0>() != AUTOMATIC || + attrs.get<1>() != AUTOMATIC || + attrs.get<2>() != AUTOMATIC || + attrs.get<3>() != AUTOMATIC ) + { + throw BuilderException("invalid mipmap control function combination"); + } + + if ( computeMipmap ) + { + osg::ref_ptr mipmaps = new osg::Image(); + int s = image->s(), + t = image->t(), + r = image->r(); + int nb = osg::Image::computeNumberOfMipmapLevels(s, t, r); + osg::Image::MipmapDataType mipmapOffsets; + unsigned int offset = 0; + for ( int i = 0; i < nb; ++i ) + { + offset += t * r * osg::Image::computeRowWidthInBytes(s, image->getPixelFormat(), image->getDataType(), image->getPacking()); + mipmapOffsets.push_back( offset ); + s >>= 1; if ( s == 0 ) s = 1; + t >>= 1; if ( t == 0 ) t = 1; + r >>= 1; if ( r == 0 ) r = 1; + } + mipmapOffsets.pop_back(); + unsigned char *data = new unsigned char[offset]; + memcpy( data, image->data(), mipmapOffsets.front() ); + s = image->s(); + t = image->t(); + r = image->r(); + for ( int m = 0; m < nb-1; ++m ) + { + unsigned char *src = data; + if ( m > 0 ) + src += mipmapOffsets[m-1]; + + unsigned char *dest = data + mipmapOffsets[m]; + + int ns = s >> 1; if ( ns == 0 ) ns = 1; + int nt = t >> 1; if ( nt == 0 ) nt = 1; + int nr = r >> 1; if ( nr == 0 ) nr = 1; + + for ( int k = 0; k < r; k += 2 ) + { + for ( int j = 0; j < t; j += 2 ) + { + for ( int i = 0; i < s; i += 2 ) + { + osg::Vec4 colors[2][2][2]; + bool colorValid[2][2][2]; + colorValid[0][0][0] = false; colorValid[0][0][1] = false; colorValid[0][1][0] = false; colorValid[0][1][1] = false; + colorValid[1][0][0] = false; colorValid[1][0][1] = false; colorValid[1][1][0] = false; colorValid[1][1][1] = false; + if ( true ) + { + unsigned char *ptr = imageData( src, image->getPixelFormat(), image->getDataType(), s, t, image->getPacking(), i, j, k ); + colors[0][0][0] = getColor( ptr, image->getPixelFormat(), image->getDataType() ); + colorValid[0][0][0] = true; + } + if ( i + 1 < s ) + { + unsigned char *ptr = imageData( src, image->getPixelFormat(), image->getDataType(), s, t, image->getPacking(), i + 1, j, k ); + colors[0][0][1] = getColor( ptr, image->getPixelFormat(), image->getDataType() ); + colorValid[0][0][1] = true; + } + if ( j + 1 < t ) + { + unsigned char *ptr = imageData( src, image->getPixelFormat(), image->getDataType(), s, t, image->getPacking(), i, j + 1, k ); + colors[0][1][0] = getColor( ptr, image->getPixelFormat(), image->getDataType() ); + colorValid[0][1][0] = true; + } + if ( i + 1 < s && j + 1 < t ) + { + unsigned char *ptr = imageData( src, image->getPixelFormat(), image->getDataType(), s, t, image->getPacking(), i + 1, j + 1, k ); + colors[0][1][1] = getColor( ptr, image->getPixelFormat(), image->getDataType() ); + colorValid[0][1][1] = true; + } + if ( k + 1 < r ) + { + unsigned char *ptr = imageData( src, image->getPixelFormat(), image->getDataType(), s, t, image->getPacking(), i, j, k + 1 ); + colors[1][0][0] = getColor( ptr, image->getPixelFormat(), image->getDataType() ); + colorValid[1][0][0] = true; + } + if ( i + 1 < s && k + 1 < r ) + { + unsigned char *ptr = imageData( src, image->getPixelFormat(), image->getDataType(), s, t, image->getPacking(), i + 1, j, k + 1 ); + colors[1][0][1] = getColor( ptr, image->getPixelFormat(), image->getDataType() ); + colorValid[1][0][1] = true; + } + if ( j + 1 < t && k + 1 < r ) + { + unsigned char *ptr = imageData( src, image->getPixelFormat(), image->getDataType(), s, t, image->getPacking(), i, j + 1, k + 1 ); + colors[1][1][0] = getColor( ptr, image->getPixelFormat(), image->getDataType() ); + colorValid[1][1][0] = true; + } + if ( i + 1 < s && j + 1 < t && k + 1 < r ) + { + unsigned char *ptr = imageData( src, image->getPixelFormat(), image->getDataType(), s, t, image->getPacking(), i + 1, j + 1, k + 1 ); + colors[1][1][1] = getColor( ptr, image->getPixelFormat(), image->getDataType() ); + colorValid[1][1][1] = true; + } + + unsigned char *ptr = imageData( dest, image->getPixelFormat(), image->getDataType(), ns, nt, image->getPacking(), i/2, j/2, k/2 ); + osg::Vec4 color = computeColor( colors, colorValid, attrs ); + setColor( ptr, image->getPixelFormat(), image->getDataType(), color ); + } + } + } + s = ns; + t = nt; + r = nr; + } + mipmaps->setImage( image->s(), image->t(), image->r(), + image->getInternalTextureFormat(), image->getPixelFormat(), + image->getDataType(), data, osg::Image::USE_NEW_DELETE, image->getPacking() ); + mipmaps->setMipmapLevels( mipmapOffsets ); + + return mipmaps.release(); + } + return image; +} +} } diff --git a/simgear/scene/material/mipmap.hxx b/simgear/scene/material/mipmap.hxx new file mode 100644 index 00000000..d0fdcbb5 --- /dev/null +++ b/simgear/scene/material/mipmap.hxx @@ -0,0 +1,50 @@ +// Copyright (C) 2010 Frederic Bouvier +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Library General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifndef SIMGEAR_MIPMAP_HXX +#define SIMGEAR_MIPMAP_HXX 1 + +#include + +class SGPropertyNode; + +namespace osg { + class Image; +} + +namespace simgear +{ +class Effect; +class SGReaderWriterXMLOptions; + +namespace effect { +enum MipMapFunction { + AUTOMATIC, + AVERAGE, + SUM, + PRODUCT, + MIN, + MAX +}; + +typedef boost::tuple MipMapTuple; + +MipMapTuple makeMipMapTuple(Effect* effect, const SGPropertyNode* props, + const SGReaderWriterXMLOptions* options); +osg::Image* computeMipmap( osg::Image* image, MipMapTuple attrs ); +} } + +#endif -- 2.39.5