/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2003 Robert Osfield * * This library is open source and may be redistributed and/or modified under * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or * (at your option) any later version. The full license is in LICENSE file * included with this distribution, and on the openscenegraph.org website. * * 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 * OpenSceneGraph Public License for more details. */ #ifndef OSG_TEXTURE #define OSG_TEXTURE 1 #include #include #include #include #include #include #include #include // if not defined by gl.h use the definition found in: // http://oss.sgi.com/projects/ogl-sample/registry/EXT/texture_filter_anisotropic.txt #ifndef GL_TEXTURE_MAX_ANISOTROPY_EXT #define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE #endif #ifndef GL_ARB_texture_compression #define GL_COMPRESSED_ALPHA_ARB 0x84E9 #define GL_COMPRESSED_LUMINANCE_ARB 0x84EA #define GL_COMPRESSED_LUMINANCE_ALPHA_ARB 0x84EB #define GL_COMPRESSED_INTENSITY_ARB 0x84EC #define GL_COMPRESSED_RGB_ARB 0x84ED #define GL_COMPRESSED_RGBA_ARB 0x84EE #define GL_TEXTURE_COMPRESSION_HINT_ARB 0x84EF #define GL_TEXTURE_COMPRESSED_ARB 0x86A1 #define GL_NUM_COMPRESSED_TEXTURE_FORMATS_ARB 0x86A2 #define GL_COMPRESSED_TEXTURE_FORMATS_ARB 0x86A3 #endif #ifndef GL_TEXTURE_COMPRESSED_IMAGE_SIZE_ARB #define GL_TEXTURE_COMPRESSED_IMAGE_SIZE_ARB 0x86A0 #endif #ifndef GL_EXT_texture_compression_s3tc #define GL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0 #define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1 #define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2 #define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3 #endif #ifndef GL_MIRRORED_REPEAT_IBM #define GL_MIRRORED_REPEAT_IBM 0x8370 #endif #ifndef GL_CLAMP_TO_EDGE #define GL_CLAMP_TO_EDGE 0x812F #endif #ifndef GL_CLAMP_TO_BORDER_ARB #define GL_CLAMP_TO_BORDER_ARB 0x812D #endif #ifndef GL_GENERATE_MIPMAP_SGIS #define GL_GENERATE_MIPMAP_SGIS 0x8191 #define GL_GENERATE_MIPMAP_HINT_SGIS 0x8192 #endif #ifndef GL_TEXTURE_3D #define GL_TEXTURE_3D 0x806F #endif #ifndef GL_TEXTURE_COMPARE_MODE_ARB #define GL_DEPTH_TEXTURE_MODE_ARB 0x884B #define GL_TEXTURE_COMPARE_MODE_ARB 0x884C #define GL_TEXTURE_COMPARE_FUNC_ARB 0x884D #define GL_COMPARE_R_TO_TEXTURE_ARB 0x884E #endif #ifndef TEXTURE_COMPARE_FAIL_VALUE_ARB #define TEXTURE_COMPARE_FAIL_VALUE_ARB 0x80BF #endif namespace osg { /** Texture base class which encapsulates OpenGl texture functionality which common betweent the various types of OpenGL textures.*/ class SG_EXPORT Texture : public osg::StateAttribute { public : Texture(); /** Copy constructor using CopyOp to manage deep vs shallow copy.*/ Texture(const Texture& text,const CopyOp& copyop=CopyOp::SHALLOW_COPY); virtual osg::Object* cloneType() const = 0; virtual osg::Object* clone(const CopyOp& copyop) const = 0; virtual bool isSameKindAs(const osg::Object* obj) const { return dynamic_cast(obj)!=NULL; } virtual const char* libraryName() const { return "osg"; } virtual const char* className() const { return "Texture"; } virtual Type getType() const { return TEXTURE; } virtual bool isTextureAttribute() const { return true; } enum WrapParameter { WRAP_S, WRAP_T, WRAP_R }; enum WrapMode { CLAMP = GL_CLAMP, CLAMP_TO_EDGE = GL_CLAMP_TO_EDGE, CLAMP_TO_BORDER = GL_CLAMP_TO_BORDER_ARB, REPEAT = GL_REPEAT, MIRROR = GL_MIRRORED_REPEAT_IBM }; /** Set the texture wrap mode.*/ void setWrap(WrapParameter which, WrapMode wrap); /** Get the texture wrap mode.*/ WrapMode getWrap(WrapParameter which) const; /** Set the border color for this texture. Makes difference only if * wrap mode is CLAMP_TO_BORDER */ void setBorderColor(const Vec4& color) { _borderColor = color; dirtyTextureParameters(); } /** Get the border color for this texture.*/ const Vec4& getBorderColor() const { return _borderColor; } /** Set the border width.*/ void setBorderWidth(GLint width) { _borderWidth = width; dirtyTextureParameters(); } GLint getBorderWidth() const { return _borderWidth; } enum FilterParameter { MIN_FILTER, MAG_FILTER }; enum FilterMode { LINEAR = GL_LINEAR, LINEAR_MIPMAP_LINEAR = GL_LINEAR_MIPMAP_LINEAR, LINEAR_MIPMAP_NEAREST = GL_LINEAR_MIPMAP_NEAREST, NEAREST = GL_NEAREST, NEAREST_MIPMAP_LINEAR = GL_NEAREST_MIPMAP_LINEAR, NEAREST_MIPMAP_NEAREST = GL_NEAREST_MIPMAP_NEAREST }; /** Set the texture filter mode.*/ void setFilter(FilterParameter which, FilterMode filter); /** Get the texture filter mode.*/ FilterMode getFilter(FilterParameter which) const; /** Set the maximum anisotropy value, default value is 1.0 for * no anisotropic filtering. If hardware does not support anisotropic * filtering then normal filtering is used, equivilant to a max anisotropy value of 1.0. * valid range is 1.0f upwards. The maximum value depends on the graphics * system being used.*/ void setMaxAnisotropy(float anis); /** Get the maximum anisotropy value.*/ inline float getMaxAnisotropy() const { return _maxAnisotropy; } /** Set the hint of whether to use hardware mip map generation where available.*/ inline void setUseHardwareMipMapGeneration(bool useHardwareMipMapGeneration) { _useHardwareMipMapGeneration = useHardwareMipMapGeneration; } /** Get the hint of whether to use hardware mip map generation where available.*/ inline bool getUseHardwareMipMapGeneration() const { return _useHardwareMipMapGeneration; } /** Set the automatic unreference of image data after the texture has been set up in apply, on (true) or off (false). * If the image data is only referened by this Texture then the image data will be autoamtically deleted.*/ inline void setUnRefImageDataAfterApply(bool flag) { _unrefImageDataAfterApply = flag; } /** Get the automatic unreference of image data after the texture has been set up in apply.*/ inline bool getUnRefImageDataAfterApply() const { return _unrefImageDataAfterApply; } /** Set whether to use client storage of the texture where supported by OpenGL drivers. * Note, if UseClientStorageHint is set, and the OpenGL drivers support it, the osg::Image(s) associated with * this texture cannot be deleted, so the UnRefImageDataAfterApply flag is then ignored.*/ inline void setClientStorageHint(bool flag) { _clientStorageHint = flag; } /** Get whether to use client storage of the texture where supported by OpenGL drivers.*/ inline bool getClientStorageHint() const { return _clientStorageHint; } enum InternalFormatMode { USE_IMAGE_DATA_FORMAT, USE_USER_DEFINED_FORMAT, USE_ARB_COMPRESSION, USE_S3TC_DXT1_COMPRESSION, USE_S3TC_DXT3_COMPRESSION, USE_S3TC_DXT5_COMPRESSION }; /** Set the internal format mode. * Note, If the mode is set USE_IMAGE_DATA_FORMAT, USE_ARB_COMPRESSION, * USE_S3TC_COMPRESSION the internalFormat is automatically selected, * and will overwrite the previous _internalFormat. */ inline void setInternalFormatMode(InternalFormatMode mode) { _internalFormatMode = mode; } /** Get the internal format mode.*/ inline InternalFormatMode getInternalFormatMode() const { return _internalFormatMode; } /** Set the internal format to use when creating OpenGL textures. * Also sets the internalFormatMode to USE_USER_DEFINED_FORMAT. */ inline void setInternalFormat(GLint internalFormat) { _internalFormatMode = USE_USER_DEFINED_FORMAT; _internalFormat = internalFormat; } /** Get the internal format to use when creating OpenGL textures.*/ inline GLint getInternalFormat() const { if (_internalFormat==0) computeInternalFormat(); return _internalFormat; } bool isCompressedInternalFormat() const; class TextureObject; /** Get the handle to the texture object for the current context.*/ inline TextureObject* getTextureObject(unsigned int contextID) const { return _textureObjectBuffer[contextID].get(); } /** Force a recompile on next apply() of associated OpenGL texture objects.*/ void dirtyTextureObject(); /** return true if the texture objects for all the required graphics contexts are loaded.*/ bool areAllTextureObjectsLoaded() const; /** get the dirty flag for the current contextID.*/ inline unsigned int& getTextureParameterDirty(unsigned int contextID) const { return _texParametersDirtyList[contextID]; } /** Force a resetting on next apply() of associated OpenGL texture parameters.*/ void dirtyTextureParameters(); // set mode of GL_TEXTURE_COMPARE_MODE_ARB to GL_COMPARE_R_TO_TEXTURE_ARB // See http://oss.sgi.com/projects/ogl-sample/registry/ARB/shadow.txt void setShadowComparison(bool flag) { _use_shadow_comparison = flag; } enum ShadowCompareFunc { LEQUAL = GL_LEQUAL, GEQUAL = GL_GEQUAL }; // set shadow texture comparison function void setShadowCompareFunc(ShadowCompareFunc func) { _shadow_compare_func = func; } ShadowCompareFunc getShadowCompareFunc() { return _shadow_compare_func; } enum ShadowTextureMode { LUMINANCE = GL_LUMINANCE, INTENSITY = GL_INTENSITY, ALPHA = GL_ALPHA }; // set shadow texture mode after comparison void setShadowTextureMode(ShadowTextureMode mode) { _shadow_texture_mode = mode; } ShadowTextureMode getShadowTextureMode() { return _shadow_texture_mode; } // set value of TEXTURE_COMPARE_FAIL_VALUE_ARB texture parameter // http://oss.sgi.com/projects/ogl-sample/registry/ARB/shadow_ambient.txt void setShadowAmbient(float shadow_ambient) { _shadow_ambient = shadow_ambient; } float getShadowAmbient() { return _shadow_ambient; } /** Texture is pure virtual base class, apply must be overriden. */ virtual void apply(State& state) const = 0; /** Calls apply(state) to compile the texture. */ virtual void compile(State& state) const; /** Extensions class which encapsulates the querring of extensions and * associated function pointers, and provide convinience wrappers to * check for the extensions or use the associated functions.*/ class SG_EXPORT Extensions : public osg::Referenced { public: Extensions(); Extensions(const Extensions& rhs); void lowestCommonDenominator(const Extensions& rhs); void setupGLExtenions(); void setMultiTexturingSupported(bool flag) { _isMultiTexturingSupported=flag; } bool isMultiTexturingSupported() const { return _isMultiTexturingSupported; } void setTextureFilterAnisotropicSupported(bool flag) { _isTextureFilterAnisotropicSupported=flag; } bool isTextureFilterAnisotropicSupported() const { return _isTextureFilterAnisotropicSupported; } void setTextureCompressionARBSupported(bool flag) { _isTextureCompressionARBSupported=flag; } bool isTextureCompressionARBSupported() const { return _isTextureCompressionARBSupported; } void setTextureCompressionS3TCSupported(bool flag) { _isTextureCompressionS3TCSupported=flag; } bool isTextureCompressionS3TCSupported() const { return _isTextureCompressionS3TCSupported; } void setTextureMirroredRepeatSupported(bool flag) { _isTextureMirroredRepeatSupported=flag; } bool isTextureMirroredRepeatSupported() const { return _isTextureMirroredRepeatSupported; } void setTextureEdgeClampSupported(bool flag) { _isTextureEdgeClampSupported=flag; } bool isTextureEdgeClampSupported() const { return _isTextureEdgeClampSupported; } void setTextureBorderClampSupported(bool flag) { _isTextureBorderClampSupported=flag; } bool isTextureBorderClampSupported() const { return _isTextureBorderClampSupported; } void setGenerateMipMapSupported(bool flag) { _isGenerateMipMapSupported=flag; } bool isGenerateMipMapSupported() const { return _isGenerateMipMapSupported; } void setShadowSupported(bool flag) { _isShadowSupported = flag; } bool isShadowSupported() const { return _isShadowSupported; } void setShadowAmbientSupported(bool flag) { _isShadowAmbientSupported = flag; } bool isShadowAmbientSupported() const { return _isShadowAmbientSupported; } void setMaxTextureSize(GLint maxsize) { _maxTextureSize=maxsize; } GLint maxTextureSize() const { return _maxTextureSize; } bool isCompressedTexImage2DSupported() const { return _glCompressedTexImage2D!=0; } void setCompressedTexImage2DProc(void* ptr) { _glCompressedTexImage2D = ptr; } void glCompressedTexImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const GLvoid *data) const; void setCompressedTexSubImage2DProc(void* ptr) { _glCompressedTexSubImage2D = ptr; } void glCompressedTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei type, const GLvoid *data) const; void setGetCompressedTexImageProc(void* ptr) { _glGetCompressedTexImage = ptr; } void glGetCompressedTexImage(GLenum target, GLint level, GLvoid *data) const; bool isClientStorageSupported() const { return _isClientStorageSupported; } protected: ~Extensions() {} bool _isMultiTexturingSupported; bool _isTextureFilterAnisotropicSupported; bool _isTextureCompressionARBSupported; bool _isTextureCompressionS3TCSupported; bool _isTextureMirroredRepeatSupported; bool _isTextureEdgeClampSupported; bool _isTextureBorderClampSupported; bool _isGenerateMipMapSupported; bool _isShadowSupported; bool _isShadowAmbientSupported; bool _isClientStorageSupported; GLint _maxTextureSize; void* _glCompressedTexImage2D; void* _glCompressedTexSubImage2D; void* _glGetCompressedTexImage; }; /** Function to call to get the extension of a specified context. * If the Exentsion object for that context has not yet been created then * and the 'createIfNotInitalized' flag been set to false then returns NULL. * If 'createIfNotInitalized' is true then the Extensions object is * automatically created. However, in this case the extension object * only be created with the graphics context associated with ContextID..*/ static Extensions* getExtensions(unsigned int contextID,bool createIfNotInitalized); /** setExtensions allows users to override the extensions across graphics contexts. * typically used when you have different extensions supported across graphics pipes * but need to ensure that they all use the same low common denominator extensions.*/ static void setExtensions(unsigned int contextID,Extensions* extensions); /** Helper method which does the creation of the texture itself, but does not set or use texture binding. * Note, do not call this method directly unless you are implementing your own Subload callback*/ void applyTexImage2D_load(State& state, GLenum target, const Image* image, GLsizei width, GLsizei height,GLsizei numMipmapLevels) const; /** Helper method which subloads images to the texture itself, but does not set or use texture binding. * Note, do not call this method directly unless you are implementing your own Subload callback*/ void applyTexImage2D_subload(State& state, GLenum target, const Image* image, GLsizei width, GLsizei height, GLint inInternalFormat, GLsizei numMipmapLevels) const; protected : virtual ~Texture(); virtual void computeInternalFormat() const = 0; void computeInternalFormatWithImage(const osg::Image& image) const; void computeRequiredTextureDimensions(State& state, const osg::Image& image,GLsizei& width, GLsizei& height,GLsizei& numMipmapLevels) const; bool isCompressedInternalFormat(GLint internalFormat) const; /** Helper method which does setting of texture paramters. */ void applyTexParameters(GLenum target, State& state) const; /** return -1 if *this < *rhs, 0 if *this==*rhs, 1 if *this>*rhs.*/ int compareTexture(const Texture& rhs) const; typedef buffered_value TexParameterDirtyList; mutable TexParameterDirtyList _texParametersDirtyList; WrapMode _wrap_s; WrapMode _wrap_t; WrapMode _wrap_r; FilterMode _min_filter; FilterMode _mag_filter; float _maxAnisotropy; bool _useHardwareMipMapGeneration; bool _unrefImageDataAfterApply; bool _clientStorageHint; Vec4 _borderColor; GLint _borderWidth; InternalFormatMode _internalFormatMode; mutable GLint _internalFormat; bool _use_shadow_comparison; ShadowCompareFunc _shadow_compare_func; ShadowTextureMode _shadow_texture_mode; float _shadow_ambient; public: class TextureObject : public osg::Referenced { public: inline TextureObject(GLuint id,GLenum target): _id(id), _target(target), _numMipmapLevels(0), _internalFormat(0), _width(0), _height(0), _depth(0), _border(0), _allocated(false), _timeStamp(0) {} inline TextureObject(GLuint id, GLenum target, GLint numMipmapLevels, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLint border): _id(id), _target(target), _numMipmapLevels(numMipmapLevels), _internalFormat(internalFormat), _width(width), _height(height), _depth(depth), _border(border), _allocated(false), _timeStamp(0) {} inline bool match(GLenum target, GLint numMipmapLevels, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLint border) { return isReusable() && (_target == target) && (_numMipmapLevels == numMipmapLevels) && (_internalFormat == internalFormat) && (_width == width) && (_height == height) && (_depth == depth) && (_border == border); } inline void bind() { glBindTexture( _target, _id); } inline void setAllocated(bool allocated=true) { _allocated = allocated; } inline void setAllocated(GLint numMipmapLevels, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLint border) { _allocated=true; _numMipmapLevels = numMipmapLevels; _internalFormat = internalFormat; _width = width; _height = height; _depth = depth; _border = border; } inline bool isAllocated() const { return _allocated; } inline bool isReusable() const { return _allocated && _width!=0; } GLuint _id; GLenum _target; GLint _numMipmapLevels; GLenum _internalFormat; GLsizei _width; GLsizei _height; GLsizei _depth; GLint _border; bool _allocated; double _timeStamp; }; typedef std::list< ref_ptr > TextureObjectList; typedef std::map TextureObjectListMap; /** take the active texture objects from the Texture and place them in the specified TextureObjectListMap.*/ void takeTextureObjects(TextureObjectListMap& toblm); typedef buffered_object< ref_ptr > TextureObjectBuffer; mutable TextureObjectBuffer _textureObjectBuffer; class SG_EXPORT TextureObjectManager : public osg::Referenced { public: TextureObjectManager(): _expiryDelay(0.0) {} virtual TextureObject* generateTextureObject(unsigned int contextID,GLenum target); virtual TextureObject* generateTextureObject(unsigned int contextID, GLenum target, GLint numMipmapLevels, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLint border); virtual TextureObject* reuseTextureObject(unsigned int contextID, GLenum target, GLint numMipmapLevels, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLint border); inline TextureObject* reuseOrGenerateTextureObject(unsigned int contextID, GLenum target, GLint numMipmapLevels, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLint border) { TextureObject* to = reuseTextureObject(contextID,target,numMipmapLevels,internalFormat,width,height,depth,border); if (to) return to; else return generateTextureObject(contextID,target,numMipmapLevels,internalFormat,width,height,depth,border); } virtual void addTextureObjects(TextureObjectListMap& toblm); virtual void addTextureObjectsFrom(Texture& texture); virtual void flushTextureObjects(unsigned int contextID,double currentTime, double& availableTime); void setExpiryDelay(double expiryDelay) { _expiryDelay = expiryDelay; } double getExpiryDelay() const { return _expiryDelay; } // how long to keep unsed texture objects around for before deleting. double _expiryDelay; TextureObjectListMap _textureObjectListMap; }; static void setTextureObjectManager(TextureObjectManager* tom); static TextureObjectManager* getTextureObjectManager(); static void flushTextureObjects(unsigned int contextID,double currentTime, double& availableTime); }; } #endif