From c7a72c8435a879feeae6d160ec55afb96be095a3 Mon Sep 17 00:00:00 2001 From: Robert Osfield Date: Fri, 7 Sep 2007 11:21:02 +0000 Subject: [PATCH] From Art Tevs, "A new texture class Texture2DArray derived from Texture extends the osg to support the new EXT_texture_array extensions. Texture arrays provides a feature for people interesting in GPGPU programming. Faetures and changes: - Full support for layered 2D textures. - New uniform types were added (sampler2DArray) - FrameBufferObject implementation were changed to support attaching of 2D array textures to the framebuffer - StateSet was slightly changed to support texture arrays. NOTE: array textures can not be used in fixed function pipeline. Thus using the layered texture as a statemode for a Drawable produce invalid enumerant OpenGL errors. - Image class was extended to support handling of array textures Tests: I have used this class as a new feature of my application. It works for me without problems (Note: Texture arrays were introduced only for shading languages and not for fixed function pipelines!!!). RTT with Texture2DArray works, as I have tested them as texture targets for a camera with 6 layers/faces (i.e. replacement for cube maps). I am using the array textures in shader programming. Array textures can be attached to the FBO and used as input and as output." --- include/osg/FrameBufferObject | 9 +- include/osg/GL2Extensions | 12 + include/osg/Texture | 4 + include/osg/Texture2DArray | 239 ++++++++++++++ include/osg/Uniform | 6 + src/osg/CMakeLists.txt | 2 + src/osg/FrameBufferObject.cpp | 25 +- src/osg/Image.cpp | 27 +- src/osg/StateSet.cpp | 4 +- src/osg/Texture2DArray.cpp | 574 ++++++++++++++++++++++++++++++++++ src/osg/Uniform.cpp | 20 ++ 11 files changed, 915 insertions(+), 7 deletions(-) create mode 100644 include/osg/Texture2DArray create mode 100644 src/osg/Texture2DArray.cpp diff --git a/include/osg/FrameBufferObject b/include/osg/FrameBufferObject index 54f98a010..e68e1f411 100644 --- a/include/osg/FrameBufferObject +++ b/include/osg/FrameBufferObject @@ -44,6 +44,7 @@ #define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL_EXT 0x8CD2 #define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE_EXT 0x8CD3 #define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_3D_ZOFFSET_EXT 0x8CD4 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER_EXT 0x8CD4 #define GL_COLOR_ATTACHMENT0_EXT 0x8CE0 #define GL_COLOR_ATTACHMENT1_EXT 0x8CE1 #define GL_COLOR_ATTACHMENT2_EXT 0x8CE2 @@ -105,13 +106,16 @@ namespace osg typedef void APIENTRY TglFramebufferTexture1DEXT(GLenum, GLenum, GLenum, GLuint, GLint); typedef void APIENTRY TglFramebufferTexture2DEXT(GLenum, GLenum, GLenum, GLuint, GLint); typedef void APIENTRY TglFramebufferTexture3DEXT(GLenum, GLenum, GLenum, GLuint, GLint, GLint); + typedef void APIENTRY TglFramebufferTextureLayerEXT(GLenum, GLenum, GLuint, GLint, GLint); typedef void APIENTRY TglFramebufferRenderbufferEXT(GLenum, GLenum, GLenum, GLuint); typedef void APIENTRY TglGenerateMipmapEXT(GLenum); - + typedef void APIENTRY TglRenderbufferStorageMultisampleCoverageNV(GLenum, GLuint, GLuint, GLenum, GLuint, GLuint); + TglBindRenderbufferEXT* glBindRenderbufferEXT; TglGenRenderbuffersEXT* glGenRenderbuffersEXT; TglDeleteRenderbuffersEXT* glDeleteRenderbuffersEXT; TglRenderbufferStorageEXT* glRenderbufferStorageEXT; + TglRenderbufferStorageMultisampleCoverageNV* glRenderbufferStorageMultisampleCoverageNV; TglBindFramebufferEXT* glBindFramebufferEXT; TglDeleteFramebuffersEXT* glDeleteFramebuffersEXT; TglGenFramebuffersEXT* glGenFramebuffersEXT; @@ -119,6 +123,7 @@ namespace osg TglFramebufferTexture1DEXT* glFramebufferTexture1DEXT; TglFramebufferTexture2DEXT* glFramebufferTexture2DEXT; TglFramebufferTexture3DEXT* glFramebufferTexture3DEXT; + TglFramebufferTextureLayerEXT* glFramebufferTextureLayerEXT; TglFramebufferRenderbufferEXT* glFramebufferRenderbufferEXT; TglGenerateMipmapEXT* glGenerateMipmapEXT; @@ -246,6 +251,7 @@ namespace osg class Texture1D; class Texture2D; class Texture3D; + class Texture2DArray; class TextureCubeMap; class TextureRectangle; @@ -259,6 +265,7 @@ namespace osg explicit FrameBufferAttachment(Texture1D* target, int level = 0); explicit FrameBufferAttachment(Texture2D* target, int level = 0); explicit FrameBufferAttachment(Texture3D* target, int zoffset, int level = 0); + explicit FrameBufferAttachment(Texture2DArray* target, int layer, int level = 0); explicit FrameBufferAttachment(TextureCubeMap* target, int face, int level = 0); explicit FrameBufferAttachment(TextureRectangle* target); explicit FrameBufferAttachment(Camera::Attachment& attachment); diff --git a/include/osg/GL2Extensions b/include/osg/GL2Extensions index 535f3a608..73dd8aaf5 100644 --- a/include/osg/GL2Extensions +++ b/include/osg/GL2Extensions @@ -1,6 +1,7 @@ /* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield * Copyright (C) 2003-2005 3Dlabs Inc. Ltd. * Copyright (C) 2004-2005 Nathan Cournia + * Copyright (C) 2007 Art Tevs * * This application is open source and may be redistributed and/or modified * freely and without restriction, both in commericial and non commericial @@ -23,6 +24,17 @@ #include +#ifndef GL_VERSION_2_1 //[ +#define GL_VERSION_2_1 1 + +#define GL_SAMPLER_1D_ARRAY_EXT 0x8DC0 +#define GL_SAMPLER_2D_ARRAY_EXT 0x8DC1 +#define GL_SAMPLER_1D_ARRAY_SHADOW_EXT 0x8DC3 +#define GL_SAMPLER_2D_ARRAY_SHADOW_EXT 0x8DC4 + +#endif //] + + #ifndef GL_VERSION_2_0 //[ #define GL_VERSION_2_0 1 typedef char GLchar; diff --git a/include/osg/Texture b/include/osg/Texture index 107b692e5..a1fbce028 100644 --- a/include/osg/Texture +++ b/include/osg/Texture @@ -156,6 +156,10 @@ #define GL_TEXTURE_3D 0x806F #endif +#ifndef GL_TEXTURE_2D_ARRAY_EXT +#define GL_TEXTURE_2D_ARRAY_EXT 0x8C1A +#endif + #ifndef GL_TEXTURE_BINDING_3D #define GL_TEXTURE_BINDING_3D 0x806A #endif diff --git a/include/osg/Texture2DArray b/include/osg/Texture2DArray new file mode 100644 index 000000000..bb7133ad4 --- /dev/null +++ b/include/osg/Texture2DArray @@ -0,0 +1,239 @@ +/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 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_TEXTURE2DARRAY +#define OSG_TEXTURE2DARRAY 1 + +#include +#include + +#ifndef GL_TEXTURE_2D_ARRAY_EXT + #define GL_TEXTURE_2D_ARRAY_EXT 0x8C1A + #define GL_PROXY_TEXTURE_2D_ARRAY_EXT 0x8C1B + #define GL_TEXTURE_BINDING_2D_ARRAY_EXT 0x8C1D + #define GL_MAX_ARRAY_TEXTURE_LAYERS_EXT 0x88FF + #define GL_COMPARE_REF_DEPTH_TO_TEXTURE_EXT 0x884E + #define GL_SAMPLER_2D_ARRAY_EXT 0x8DC1 + #define GL_SAMPLER_2D_ARRAY_SHADOW_EXT 0x8DC4 + #define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER_EXT 0x8CD4 +#endif + +namespace osg { + +/** Texture2DArray state class which encapsulates OpenGL 2D array texture functionality. + * Texture arrays were introduced with Shader Model 4.0 hardware. + * + * A 2D texture array does contain textures sharing the same properties (e.g. size, bitdepth,...) + * in a layered structure. See http://www.opengl.org/registry/specs/EXT/texture_array.txt for more info. + */ +class OSG_EXPORT Texture2DArray : public Texture +{ + + public : + + Texture2DArray(); + + /** Copy constructor using CopyOp to manage deep vs shallow copy. */ + Texture2DArray(const Texture2DArray& cm,const CopyOp& copyop=CopyOp::SHALLOW_COPY); + + META_StateAttribute(osg, Texture2DArray, TEXTURE); + + /** Return -1 if *this < *rhs, 0 if *this==*rhs, 1 if *this>*rhs. */ + virtual int compare(const StateAttribute& rhs) const; + + virtual GLenum getTextureTarget() const { return GL_TEXTURE_2D_ARRAY_EXT; } + + /** Set the texture image for specified layer. */ + virtual void setImage(unsigned int layer, Image* image); + + /** Get the texture image for specified layer. */ + virtual Image* getImage(unsigned int layer); + + /** Get the const texture image for specified layer. */ + virtual const Image* getImage(unsigned int layer) const; + + /** Get the number of images that are assigned to the Texture. + * The number is equal to the texture depth. To get the maximum possible + * image/layer count, you have to use the extension subclass, since it provides + * graphic context dependent information. + */ + virtual unsigned int getNumImages() const { return getTextureDepth(); } + + /** Check how often was a certain layer in the given context modified */ + inline unsigned int& getModifiedCount(unsigned int layer, unsigned int contextID) const + { + // get the modified count for the current contextID. + return _modifiedCount[layer][contextID]; + } + + /** Set the texture width and height. If width or height are zero then + * the repsective size value is calculated from the source image sizes. + * Depth parameter specifies the number of layers to be used. + */ + void setTextureSize(int width, int height, int depth); + + void setTextureWidth(int width) { _textureWidth=width; } + void setTextureHeight(int height) { _textureHeight=height; } + void setTextureDepth(int depth); + + virtual int getTextureWidth() const { return _textureWidth; } + virtual int getTextureHeight() const { return _textureHeight; } + virtual int getTextureDepth() const { return _textureDepth; } + + class OSG_EXPORT SubloadCallback : public Referenced + { + public: + virtual void load(const Texture2DArray& texture,State& state) const = 0; + virtual void subload(const Texture2DArray& texture,State& state) const = 0; + }; + + + void setSubloadCallback(SubloadCallback* cb) { _subloadCallback = cb;; } + + SubloadCallback* getSubloadCallback() { return _subloadCallback.get(); } + + const SubloadCallback* getSubloadCallback() const { return _subloadCallback.get(); } + + + + /** Set the number of mip map levels the the texture has been created with. + * Should only be called within an osg::Texuture::apply() and custom OpenGL texture load. + */ + void setNumMipmapLevels(unsigned int num) const { _numMipmapLevels=num; } + + /** Get the number of mip map levels the the texture has been created with. */ + unsigned int getNumMipmapLevels() const { return _numMipmapLevels; } + + /** Copies a two-dimensional texture subimage, as per + * glCopyTexSubImage3D. Updates a portion of an existing OpenGL + * texture object from the current OpenGL background framebuffer + * contents at position \a x, \a y with width \a width and height + * \a height. Loads framebuffer data into the texture using offsets + * \a xoffset and \a yoffset. \a zoffset specifies the layer of the texture + * array to which the result is copied. + */ + void copyTexSubImage2DArray(State& state, int xoffset, int yoffset, int zoffset, int x, int y, int width, int height ); + + /** Bind the texture if already compiled. Otherwise recompile. + */ + virtual void apply(State& state) const; + + + /** Extensions class which encapsulates the querying of extensions and + * associated function pointers, and provides convinience wrappers to + * check for the extensions or use the associated functions. + */ + class OSG_EXPORT Extensions : public osg::Referenced + { + public: + Extensions(unsigned int contextID); + + Extensions(const Extensions& rhs); + + void lowestCommonDenominator(const Extensions& rhs); + + void setupGLExtensions(unsigned int contextID); + + void setTexture2DArraySupported(bool flag) { _isTexture2DArraySupported=flag; } + bool isTexture2DArraySupported() const { return _isTexture2DArraySupported; } + + void setTexture3DSupported(bool flag) { _isTexture3DSupported=flag; } + bool isTexture3DSupported() const { return _isTexture3DSupported; } + + void setMaxLayerCount(GLint count) { _maxLayerCount = count; } + GLint maxLayerCount() const { return _maxLayerCount; } + + void setMax2DSize(GLint size) { _max2DSize = size; } + GLint max2DSize() const { return _max2DSize; } + + void setTexImage3DProc(void* ptr) { _glTexImage3D = ptr; } + void glTexImage3D( GLenum target, GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid *pixels) const; + + void setTexSubImage3DProc(void* ptr) { _glTexSubImage3D = ptr; } + void glTexSubImage3D( GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const GLvoid *pixels) const; + + void setCopyTexSubImage3DProc(void* ptr) { _glCopyTexSubImage3D = ptr; } + void glCopyTexSubImage3D( GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height ) const; + + bool isCompressedTexImage3DSupported() const { return _glCompressedTexImage3D!=0; } + void setCompressedTexImage3DProc(void* ptr) { _glCompressedTexImage3D = ptr; } + void glCompressedTexImage3D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const GLvoid *data) const; + + bool isCompressedTexSubImage3DSupported() const { return _glCompressedTexSubImage3D!=0; } + void setCompressedTexSubImage3DProc(void* ptr) { _glCompressedTexSubImage3D = ptr; } + void glCompressedTexSubImage3D( GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const GLvoid *data ) const; + + protected: + + ~Extensions() {} + + bool _isTexture2DArraySupported; + bool _isTexture3DSupported; + + GLint _maxLayerCount; + GLint _max2DSize; + + void* _glTexImage3D; + void* _glTexSubImage3D; + void* _glCompressedTexImage3D; + void* _glCompressedTexSubImage3D; + void* _glCopyTexSubImage3D; + + }; + + /** Function to call to get the extension of a specified context. + * If the Exentsion object for that context has not yet been created + * 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 will + * only be created with the graphics context associated with ContextID. + */ + static Extensions* getExtensions(unsigned int contextID,bool createIfNotInitalized); + + /** The setExtensions method 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); + + + protected : + + virtual ~Texture2DArray(); + + bool imagesValid() const; + + virtual void computeInternalFormat() const; + + void applyTexImage2DArray_subload(State& state, Image* image, GLsizei& inwidth, GLsizei& inheight, GLsizei& indepth, GLsizei& numMipmapLevels) const; + + /** + * Use std::vector to encapsulate referenced pointers to images of different layers. + * Vectors gives us a random access iterator. The overhead of non-used elements is negligeable */ + std::vector > _images; + + // subloaded images can have different texture and image sizes. + mutable GLsizei _textureWidth, _textureHeight, _textureDepth; + + // number of mip map levels the the texture has been created with, + mutable GLsizei _numMipmapLevels; + + ref_ptr _subloadCallback; + + typedef buffered_value ImageModifiedCount; + mutable std::vector _modifiedCount; +}; + +} + +#endif diff --git a/include/osg/Uniform b/include/osg/Uniform index 46365238b..33e5ab55c 100644 --- a/include/osg/Uniform +++ b/include/osg/Uniform @@ -165,6 +165,12 @@ class OSG_EXPORT Uniform : public Object SAMPLER_CUBE = GL_SAMPLER_CUBE, SAMPLER_1D_SHADOW = GL_SAMPLER_1D_SHADOW, SAMPLER_2D_SHADOW = GL_SAMPLER_2D_SHADOW, + + SAMPLER_1D_ARRAY = GL_SAMPLER_1D_ARRAY_EXT, + SAMPLER_2D_ARRAY = GL_SAMPLER_2D_ARRAY_EXT, + SAMPLER_1D_ARRAY_SHADOW = GL_SAMPLER_1D_ARRAY_SHADOW_EXT, + SAMPLER_2D_ARRAY_SHADOW = GL_SAMPLER_2D_ARRAY_SHADOW_EXT, + UNDEFINED = 0x0 }; diff --git a/src/osg/CMakeLists.txt b/src/osg/CMakeLists.txt index 76c65a232..53bffe2c4 100644 --- a/src/osg/CMakeLists.txt +++ b/src/osg/CMakeLists.txt @@ -132,6 +132,7 @@ SET(LIB_PUBLIC_HEADERS ${HEADER_PATH}/Texture ${HEADER_PATH}/Texture1D ${HEADER_PATH}/Texture2D + ${HEADER_PATH}/Texture2DArray ${HEADER_PATH}/Texture3D ${HEADER_PATH}/TextureCubeMap ${HEADER_PATH}/TextureRectangle @@ -283,6 +284,7 @@ ADD_LIBRARY(${LIB_NAME} Texture.cpp Texture1D.cpp Texture2D.cpp + Texture2DArray.cpp Texture3D.cpp TextureCubeMap.cpp TextureRectangle.cpp diff --git a/src/osg/FrameBufferObject.cpp b/src/osg/FrameBufferObject.cpp index 70f86df9a..780480b9d 100644 --- a/src/osg/FrameBufferObject.cpp +++ b/src/osg/FrameBufferObject.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -56,6 +57,7 @@ FBOExtensions::FBOExtensions(unsigned int contextID) LOAD_FBO_EXT(glFramebufferTexture1DEXT); LOAD_FBO_EXT(glFramebufferTexture2DEXT); LOAD_FBO_EXT(glFramebufferTexture3DEXT); + LOAD_FBO_EXT(glFramebufferTextureLayerEXT); LOAD_FBO_EXT(glFramebufferRenderbufferEXT); LOAD_FBO_EXT(glGenerateMipmapEXT); @@ -205,7 +207,8 @@ struct FrameBufferAttachment::Pimpl TEXTURE2D, TEXTURE3D, TEXTURECUBE, - TEXTURERECT + TEXTURERECT, + TEXTURE2DARRAY }; TargetType targetType; @@ -269,6 +272,13 @@ FrameBufferAttachment::FrameBufferAttachment(Texture3D* target, int level, int z _ximpl->zoffset = zoffset; } +FrameBufferAttachment::FrameBufferAttachment(Texture2DArray* target, int layer, int level) +{ + _ximpl = new Pimpl(Pimpl::TEXTURE2DARRAY, level); + _ximpl->textureTarget = target; + _ximpl->zoffset = layer; +} + FrameBufferAttachment::FrameBufferAttachment(TextureCubeMap* target, int face, int level) { _ximpl = new Pimpl(Pimpl::TEXTURECUBE, level); @@ -312,6 +322,15 @@ FrameBufferAttachment::FrameBufferAttachment(Camera::Attachment& attachment) _ximpl->zoffset = attachment._face; return; } + + osg::Texture2DArray* texture2DArray = dynamic_cast(texture); + if (texture2DArray) + { + _ximpl = new Pimpl(Pimpl::TEXTURE2DARRAY, attachment._level); + _ximpl->textureTarget = texture2DArray; + _ximpl->zoffset = attachment._face; + return; + } osg::TextureCubeMap* textureCubeMap = dynamic_cast(texture); if (textureCubeMap) @@ -429,6 +448,9 @@ void FrameBufferAttachment::attach(State &state, GLenum attachment_point, const case Pimpl::TEXTURE3D: ext->glFramebufferTexture3DEXT(GL_FRAMEBUFFER_EXT, attachment_point, GL_TEXTURE_3D, tobj->_id, _ximpl->level, _ximpl->zoffset); break; + case Pimpl::TEXTURE2DARRAY: + ext->glFramebufferTextureLayerEXT(GL_FRAMEBUFFER_EXT, attachment_point, tobj->_id, _ximpl->level, _ximpl->zoffset); + break; case Pimpl::TEXTURERECT: ext->glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, attachment_point, GL_TEXTURE_RECTANGLE, tobj->_id, 0); break; @@ -588,6 +610,7 @@ void FrameBufferObject::apply(State &state) const } + ext->glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fboID); if (dirtyAttachmentList) diff --git a/src/osg/Image.cpp b/src/osg/Image.cpp index 4bdcd63bf..d6a54358b 100644 --- a/src/osg/Image.cpp +++ b/src/osg/Image.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include "dxtctool.h" @@ -522,14 +523,25 @@ void Image::readImageFromCurrentTexture(unsigned int contextID, bool copyMipMaps { const osg::Texture::Extensions* extensions = osg::Texture::getExtensions(contextID,true); const osg::Texture3D::Extensions* extensions3D = osg::Texture3D::getExtensions(contextID,true); + const osg::Texture2DArray::Extensions* extensions2DArray = osg::Texture2DArray::getExtensions(contextID,true); - GLboolean binding1D, binding2D, binding3D; + GLboolean binding1D, binding2D, binding3D, binding2DArray; glGetBooleanv(GL_TEXTURE_BINDING_1D, &binding1D); glGetBooleanv(GL_TEXTURE_BINDING_2D, &binding2D); glGetBooleanv(GL_TEXTURE_BINDING_3D, &binding3D); + + + if (extensions2DArray->isTexture2DArraySupported()) + { + glGetBooleanv(GL_TEXTURE_BINDING_2D_ARRAY_EXT, &binding2DArray); + } + else + { + binding2DArray - GL_FALSE; + } - GLenum textureMode = binding1D ? GL_TEXTURE_1D : binding2D ? GL_TEXTURE_2D : binding3D ? GL_TEXTURE_3D : 0; + GLenum textureMode = binding1D ? GL_TEXTURE_1D : binding2D ? GL_TEXTURE_2D : binding3D ? GL_TEXTURE_3D : binding2DArray ? GL_TEXTURE_2D_ARRAY_EXT : 0; if (textureMode==0) return; @@ -562,14 +574,21 @@ void Image::readImageFromCurrentTexture(unsigned int contextID, bool copyMipMaps { if (extensions->isCompressedTexImage2DSupported()) { - glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_COMPRESSED_ARB,&compressed); + glGetTexLevelParameteriv(textureMode, 0, GL_TEXTURE_COMPRESSED_ARB,&compressed); } } else if (textureMode==GL_TEXTURE_3D) { if (extensions3D->isCompressedTexImage3DSupported()) { - glGetTexLevelParameteriv(GL_TEXTURE_3D, 0, GL_TEXTURE_COMPRESSED_ARB,&compressed); + glGetTexLevelParameteriv(textureMode, 0, GL_TEXTURE_COMPRESSED_ARB,&compressed); + } + } + else if (textureMode==GL_TEXTURE_2D_ARRAY_EXT) + { + if (extensions2DArray->isCompressedTexImage3DSupported()) + { + glGetTexLevelParameteriv(textureMode, 0, GL_TEXTURE_COMPRESSED_ARB,&compressed); } } diff --git a/src/osg/StateSet.cpp b/src/osg/StateSet.cpp index faafedd01..50921227a 100644 --- a/src/osg/StateSet.cpp +++ b/src/osg/StateSet.cpp @@ -28,6 +28,7 @@ #include #include +#include #include #include @@ -48,7 +49,8 @@ class TextureGLModeSet _textureModeSet.insert(GL_TEXTURE_3D); _textureModeSet.insert(GL_TEXTURE_CUBE_MAP); - _textureModeSet.insert(GL_TEXTURE_RECTANGLE_NV); + _textureModeSet.insert(GL_TEXTURE_RECTANGLE_NV); + _textureModeSet.insert(GL_TEXTURE_2D_ARRAY_EXT); _textureModeSet.insert(GL_TEXTURE_GEN_Q); _textureModeSet.insert(GL_TEXTURE_GEN_R); diff --git a/src/osg/Texture2DArray.cpp b/src/osg/Texture2DArray.cpp new file mode 100644 index 000000000..8abe0d70d --- /dev/null +++ b/src/osg/Texture2DArray.cpp @@ -0,0 +1,574 @@ +/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 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. +*/ +#include +#include +#include +#include + +#include + + +using namespace osg; + +Texture2DArray::Texture2DArray(): + _textureWidth(0), + _textureHeight(0), + _textureDepth(0), + _numMipmapLevels(0) +{ +} + +Texture2DArray::Texture2DArray(const Texture2DArray& text,const CopyOp& copyop): + Texture(text,copyop), + _textureWidth(text._textureWidth), + _textureHeight(text._textureHeight), + _textureDepth(text._textureDepth), + _numMipmapLevels(text._numMipmapLevels), + _subloadCallback(text._subloadCallback) +{ + // copy all images by iterating through all of them + for (int i=0; i < text._textureDepth; i++) + { + _images.push_back(copyop(text._images[i].get())); + _modifiedCount.push_back(ImageModifiedCount()); + } +} + +Texture2DArray::~Texture2DArray() +{ +} + +int Texture2DArray::compare(const StateAttribute& sa) const +{ + // check the types are equal and then create the rhs variable + // used by the COMPARE_StateAttribute_Paramter macro's below. + COMPARE_StateAttribute_Types(Texture2DArray,sa) + + bool noImages = true; + for (int n=0; n < _textureDepth; n++) + { + if (noImages && _images[n].valid()) noImages = false; + if (noImages && rhs._images[n].valid()) noImages = false; + + if (_images[n]!=rhs._images[n]) // smart pointer comparison. + { + if (_images[n].valid()) + { + if (rhs._images[n].valid()) + { + int result = _images[n]->compare(*rhs._images[n]); + if (result!=0) return result; + } + else + { + return 1; // valid lhs._image is greater than null. + } + } + else if (rhs._images[n].valid()) + { + return -1; // valid rhs._image is greater than null. + } + } + } + + + if (noImages) + { + int result = compareTextureObjects(rhs); + if (result!=0) return result; + } + + int result = compareTexture(rhs); + if (result!=0) return result; + + // compare each paramter in turn against the rhs. + COMPARE_StateAttribute_Parameter(_textureWidth) + COMPARE_StateAttribute_Parameter(_textureHeight) + COMPARE_StateAttribute_Parameter(_textureDepth) + COMPARE_StateAttribute_Parameter(_subloadCallback) + + return 0; // passed all the above comparison macro's, must be equal. +} + +void Texture2DArray::setImage(unsigned int layer, Image* image) +{ + // check if the layer exceeds the texture depth + if (layer >= _textureDepth) + { + // print warning and do nothing + notify(WARN)<<"Warning: Texture2DArray::setImage(..) failed, the given layer number is bigger then the size of the texture array."< _textureDepth) + { + _images.resize(depth, ref_ptr(0)); + _modifiedCount.resize(depth, ImageModifiedCount()); + } + + // resize the texture array + _textureDepth = depth; +} + +Image* Texture2DArray::getImage(unsigned int layer) +{ + return _images[layer].get(); +} + +const Image* Texture2DArray::getImage(unsigned int layer) const +{ + return _images[layer].get(); +} + +bool Texture2DArray::imagesValid() const +{ + if (_textureDepth < 1) return false; + for (int n=0; n < _textureDepth; n++) + { + if (!_images[n].valid() || !_images[n]->data()) + return false; + } + return true; +} + +void Texture2DArray::computeInternalFormat() const +{ + if (imagesValid()) computeInternalFormatWithImage(*_images[0]); +} + + +void Texture2DArray::apply(State& state) const +{ + // get the contextID (user defined ID of 0 upwards) for the + // current OpenGL context. + const unsigned int contextID = state.getContextID(); + const Extensions* extensions = getExtensions(contextID,true); + + // if not supported, then return + if (!extensions->isTexture2DArraySupported() || !extensions->isTexture3DSupported()) + { + notify(WARN)<<"Warning: Texture2DArray::apply(..) failed, 2D texture arrays are not support by OpenGL driver."<bind(); + + // if texture parameters changed, then reset them + if (getTextureParameterDirty(state.getContextID())) applyTexParameters(GL_TEXTURE_2D_ARRAY_EXT,state); + + // if subload is specified, then use it to subload the images to GPU memory + if (_subloadCallback.valid()) + { + _subloadCallback->subload(*this,state); + } + else + { + // for each image of the texture array do + for (int n=0; n < _textureDepth; n++) + { + osg::Image* image = _images[n].get(); + + // if image content is modified, then upload it to the GPU memory + if (image && getModifiedCount(n,contextID) != image->getModifiedCount()) + { + applyTexImage2DArray_subload(state, image, _textureWidth, _textureHeight, n, _numMipmapLevels); + getModifiedCount(n,contextID) = image->getModifiedCount(); + } + } + } + + } + + // there is no texture object, but exists a subload callback, so use it to upload images + else if (_subloadCallback.valid()) + { + // generate texture (i.e. glGenTexture) and apply parameters + _textureObjectBuffer[contextID] = textureObject = generateTextureObject(contextID, GL_TEXTURE_2D_ARRAY_EXT); + textureObject->bind(); + applyTexParameters(GL_TEXTURE_2D_ARRAY_EXT, state); + _subloadCallback->load(*this,state); + } + + // nothing before, but we have valid images, so do manual upload and create texture object manually + else if (imagesValid()) + { + + // compute the internal texture format, this set the _internalFormat to an appropriate value. + computeInternalFormat(); + + // compute the dimensions of the texture. + computeRequiredTextureDimensions(state,*_images[0],_textureWidth, _textureHeight, _numMipmapLevels); + + // create texture object + _textureObjectBuffer[contextID] = textureObject = generateTextureObject( + contextID,GL_TEXTURE_2D_ARRAY_EXT,_numMipmapLevels,_internalFormat,_textureWidth,_textureHeight,_textureDepth,0); + + // bind texture + textureObject->bind(); + applyTexParameters(GL_TEXTURE_2D_ARRAY_EXT, state); + + // now for each layer do + for (int n=0; n<_textureDepth; n++) + { + // if image is valid then upload it to the texture memory + osg::Image* image = _images[n].get(); + if (image) + { + applyTexImage2DArray_subload(state, image, _textureWidth, _textureHeight, n, _numMipmapLevels); + getModifiedCount(n,contextID) = image->getModifiedCount(); + } + } + textureObject->setAllocated(_numMipmapLevels,_internalFormat,_textureWidth,_textureHeight,_textureDepth,0); + + // no idea what this for ;-) + if (_unrefImageDataAfterApply && areAllTextureObjectsLoaded()) + { + Texture2DArray* non_const_this = const_cast(this); + for (int n=0; n<_textureDepth; n++) + { + if (_images[n].valid() && _images[n]->getDataVariance()==STATIC) + { + non_const_this->_images[n] = 0; + } + } + } + + } + + // No images present, but dimensions are set. So create empty texture + else if ( (_textureWidth > 0) && (_textureHeight > 0) && (_textureDepth > 0) && (_internalFormat!=0) ) + { + // generate texture + _textureObjectBuffer[contextID] = textureObject = generateTextureObject( + contextID, GL_TEXTURE_2D_ARRAY_EXT,_numMipmapLevels,_internalFormat,_textureWidth,_textureHeight,_textureDepth,0); + + textureObject->bind(); + applyTexParameters(GL_TEXTURE_2D_ARRAY_EXT,state); + + extensions->glTexImage3D( GL_TEXTURE_2D_ARRAY_EXT, 0, _internalFormat, + _textureWidth, _textureHeight, _textureDepth, + _borderWidth, + _sourceFormat ? _sourceFormat : _internalFormat, + _sourceType ? _sourceType : GL_UNSIGNED_BYTE, + 0); + + } + + // nothing before, so just unbind the texture target + else + { + glBindTexture( GL_TEXTURE_2D_ARRAY_EXT, 0 ); + } +} + +void Texture2DArray::applyTexImage2DArray_subload(State& state, Image* image, GLsizei& inwidth, GLsizei& inheight, GLsizei& indepth, GLsizei& numMipmapLevels) const +{ + // if we don't have a valid image we can't create a texture! + if (!imagesValid()) + return; + + // get the contextID (user defined ID of 0 upwards) for the + // current OpenGL context. + const unsigned int contextID = state.getContextID(); + const Extensions* extensions = getExtensions(contextID,true); + const Texture::Extensions* texExtensions = Texture::getExtensions(contextID,true); + GLenum target = GL_TEXTURE_2D_ARRAY_EXT; + + // compute the internal texture format, this set the _internalFormat to an appropriate value. + computeInternalFormat(); + + // select the internalFormat required for the texture. + bool compressed = isCompressedInternalFormat(_internalFormat); + bool compressed_image = isCompressedInternalFormat((GLenum)image->getPixelFormat()); + + // if the required layer is exceeds the maximum allowed layer sizes + if (indepth > extensions->maxLayerCount()) + { + // we give a warning and do nothing + notify(WARN)<<"Warning: Texture2DArray::applyTexImage2DArray_subload(..) the given layer number exceeds the maximum number of supported layers."<isNonPowerOfTwoTextureSupported(_min_filter) + || inwidth > extensions->max2DSize() + || inheight > extensions->max2DSize()) + image->ensureValidSizeForTexturing(extensions->max2DSize()); + + + glPixelStorei(GL_UNPACK_ALIGNMENT,image->getPacking()); + + // if no special mipmapping is required, then + if( _min_filter == LINEAR || _min_filter == NEAREST ) + { + numMipmapLevels = 1; + + // upload non-compressed image + if (!compressed_image) + { + extensions->glTexImage3D( target, 0, _internalFormat, + inwidth, inheight, indepth, + _borderWidth, + (GLenum)image->getPixelFormat(), + (GLenum)image->getDataType(), + image->data() ); + } + + // if we support compression and image is compressed, then + else if (extensions->isCompressedTexImage3DSupported()) + { + // notify(WARN)<<"glCompressedTexImage3D "<glCompressedTexImage3D(target, 0, _internalFormat, + inwidth, inheight, indepth, + _borderWidth, + size, + image->data()); + } + + // we want to use mipmapping, so enable it + }else + { + // image does not provide mipmaps, so we have to create them + if(!image->isMipmap()) + { + notify(WARN)<<"Warning: Texture2DArray::applyTexImage2DArray_subload(..) automagic mipmap generation is currently not implemented."<getNumMipmapLevels(); + + int width = image->s(); + int height = image->t(); + int depth = image->r(); + + for( GLsizei k = 0 ; k < numMipmapLevels && (width || height || depth) ;k++) + { + + if (width == 0) + width = 1; + if (height == 0) + height = 1; + if (depth == 0) + depth = 1; + + extensions->glTexImage3D( target, k, _internalFormat, + width, height, depth, _borderWidth, + (GLenum)image->getPixelFormat(), + (GLenum)image->getDataType(), + image->getMipmapData(k)); + + width >>= 1; + height >>= 1; + depth >>= 1; + } + } + + } + + // set new sizes back + inwidth = image->s(); + inheight = image->t(); +} + + +void Texture2DArray::copyTexSubImage2DArray(State& state, int xoffset, int yoffset, int zoffset, int x, int y, int width, int height ) +{ + const unsigned int contextID = state.getContextID(); + const Extensions* extensions = getExtensions(contextID,true); + + // get the texture object for the current contextID. + TextureObject* textureObject = getTextureObject(contextID); + + // if texture object is valid + if (textureObject != 0) + { + textureObject->bind(); + + applyTexParameters(GL_TEXTURE_2D_ARRAY_EXT,state); + extensions->glCopyTexSubImage3D( GL_TEXTURE_2D_ARRAY_EXT, 0, xoffset,yoffset,zoffset, x, y, width, height); + + // inform state that this texture is the current one bound. + state.haveAppliedTextureAttribute(state.getActiveTextureUnit(), this); + + } + else + { + notify(WARN)<<"Warning: Texture2DArray::copyTexSubImage2DArray(..) failed, cannot not copy to a non existant texture."< > BufferedExtensions; +static BufferedExtensions s_extensions; + +Texture2DArray::Extensions* Texture2DArray::getExtensions(unsigned int contextID,bool createIfNotInitalized) +{ + if (!s_extensions[contextID] && createIfNotInitalized) s_extensions[contextID] = new Extensions(contextID); + return s_extensions[contextID].get(); +} + +void Texture2DArray::setExtensions(unsigned int contextID,Extensions* extensions) +{ + s_extensions[contextID] = extensions; +} + +Texture2DArray::Extensions::Extensions(unsigned int contextID) +{ + setupGLExtensions(contextID); +} + +Texture2DArray::Extensions::Extensions(const Extensions& rhs): + Referenced() +{ + _isTexture3DSupported = rhs._isTexture3DSupported; + _isTexture2DArraySupported = rhs._isTexture2DArraySupported; + + _max2DSize = rhs._max2DSize; + _maxLayerCount = rhs._maxLayerCount; + + _glTexImage3D = rhs._glTexImage3D; + _glTexSubImage3D = rhs._glTexSubImage3D; + _glCopyTexSubImage3D = rhs._glCopyTexSubImage3D; + _glCompressedTexImage3D = rhs._glCompressedTexImage3D; + _glCompressedTexSubImage3D = rhs._glCompressedTexSubImage3D;; +} + +void Texture2DArray::Extensions::lowestCommonDenominator(const Extensions& rhs) +{ + if (!rhs._isTexture3DSupported) _isTexture3DSupported = false; + if (!rhs._isTexture2DArraySupported) _isTexture2DArraySupported = false; + if (rhs._max2DSize<_max2DSize) _max2DSize = rhs._max2DSize; + if (rhs._maxLayerCount<_maxLayerCount) _maxLayerCount = rhs._maxLayerCount; + + if (!rhs._glTexImage3D) _glTexImage3D = 0; + if (!rhs._glTexSubImage3D) _glTexSubImage3D = 0; + if (!rhs._glCompressedTexImage3D) _glTexImage3D = 0; + if (!rhs._glCompressedTexSubImage3D) _glTexSubImage3D = 0; + if (!rhs._glCopyTexSubImage3D) _glCopyTexSubImage3D = 0; +} + +void Texture2DArray::Extensions::setupGLExtensions(unsigned int contextID) +{ + _isTexture3DSupported = isGLExtensionSupported(contextID,"GL_EXT_texture3D"); + _isTexture2DArraySupported = isGLExtensionSupported(contextID,"GL_EXT_texture_array"); + + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &_max2DSize); + glGetIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS_EXT, &_maxLayerCount); + + _glTexImage3D = getGLExtensionFuncPtr("glTexImage3D","glTexImage3DEXT"); + _glTexSubImage3D = getGLExtensionFuncPtr("glTexSubImage3D","glTexSubImage3DEXT"); + _glCompressedTexImage3D = getGLExtensionFuncPtr("glCompressedTexImage3D","glCompressedTexImage3DARB"); + _glCompressedTexSubImage3D = getGLExtensionFuncPtr("glCompressedTexSubImage3D","glCompressedTexSubImage3DARB"); + _glCopyTexSubImage3D = getGLExtensionFuncPtr("glCopyTexSubImage3D","glCopyTexSubImage3DEXT"); +} + +void Texture2DArray::Extensions::glTexImage3D( GLenum target, GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid *pixels) const +{ + if (_glTexImage3D) + { + typedef void (APIENTRY * GLTexImage3DProc) ( GLenum target, GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid *pixels); + ((GLTexImage3DProc)_glTexImage3D)( target, level, internalFormat, width, height, depth, border, format, type, pixels); + } + else + { + notify(WARN)<<"Error: glTexImage3D not supported by OpenGL driver"<