root/OpenSceneGraph/trunk/src/osg/TextureCubeMap.cpp @ 10867

Revision 10867, 16.3 kB (checked in by robert, 4 years ago)

Added check for image compatibility with existing texture object, releasing the existing texture object in cases when the new image size/pixel format is not compatible.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
2 *
3 * This library is open source and may be redistributed and/or modified under 
4 * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
5 * (at your option) any later version.  The full license is in LICENSE file
6 * included with this distribution, and on the openscenegraph.org website.
7 *
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 * OpenSceneGraph Public License for more details.
12*/
13#include <osg/GLExtensions>
14#include <osg/ref_ptr>
15#include <osg/Image>
16#include <osg/State>
17#include <osg/TextureCubeMap>
18#include <osg/ImageSequence>
19#include <osg/Notify>
20
21#include <osg/GLU>
22
23
24using namespace osg;
25
26static GLenum faceTarget[6] =
27{
28    GL_TEXTURE_CUBE_MAP_POSITIVE_X,
29    GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
30    GL_TEXTURE_CUBE_MAP_POSITIVE_Y,
31    GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
32    GL_TEXTURE_CUBE_MAP_POSITIVE_Z,
33    GL_TEXTURE_CUBE_MAP_NEGATIVE_Z
34};
35
36
37TextureCubeMap::TextureCubeMap():
38            _textureWidth(0),
39            _textureHeight(0),
40            _numMipmapLevels(0)
41{
42    setUseHardwareMipMapGeneration(false);
43}
44
45TextureCubeMap::TextureCubeMap(const TextureCubeMap& text,const CopyOp& copyop):
46            Texture(text,copyop),
47            _textureWidth(text._textureWidth),
48            _textureHeight(text._textureHeight),
49            _numMipmapLevels(text._numMipmapLevels),
50            _subloadCallback(text._subloadCallback)
51{
52    _images[0] = copyop(text._images[0].get());
53    _images[1] = copyop(text._images[1].get());
54    _images[2] = copyop(text._images[2].get());
55    _images[3] = copyop(text._images[3].get());
56    _images[4] = copyop(text._images[4].get());
57    _images[5] = copyop(text._images[5].get());
58
59    _modifiedCount[0].setAllElementsTo(0);
60    _modifiedCount[1].setAllElementsTo(0);
61    _modifiedCount[2].setAllElementsTo(0);
62    _modifiedCount[3].setAllElementsTo(0);
63    _modifiedCount[4].setAllElementsTo(0);
64    _modifiedCount[5].setAllElementsTo(0);
65
66}   
67
68
69TextureCubeMap::~TextureCubeMap()
70{
71}
72
73
74int TextureCubeMap::compare(const StateAttribute& sa) const
75{
76    // check the types are equal and then create the rhs variable
77    // used by the COMPARE_StateAttribute_Paramter macro's below.
78    COMPARE_StateAttribute_Types(TextureCubeMap,sa)
79
80    bool noImages = true;
81    for (int n=0; n<6; n++)
82    {
83        if (noImages && _images[n].valid()) noImages = false;
84        if (noImages && rhs._images[n].valid()) noImages = false;
85   
86        if (_images[n]!=rhs._images[n]) // smart pointer comparison.
87        {
88            if (_images[n].valid())
89            {
90                if (rhs._images[n].valid())
91                {
92                    int result = _images[n]->compare(*rhs._images[n]);
93                    if (result!=0) return result;
94                }
95                else
96                {
97                    return 1; // valid lhs._image is greater than null.
98                }
99            }
100            else if (rhs._images[n].valid())
101            {
102                return -1; // valid rhs._image is greater than null.
103            }
104        }
105    }
106
107    if (noImages)
108    {
109        // no image attached to either Texture2D
110        // but could these textures already be downloaded?
111        // check the _textureObjectBuffer to see if they have been
112        // downloaded
113
114        int result = compareTextureObjects(rhs);
115        if (result!=0) return result;
116    }
117
118    int result = compareTexture(rhs);
119    if (result!=0) return result;
120
121    // compare each paramter in turn against the rhs.
122    COMPARE_StateAttribute_Parameter(_textureWidth)
123    COMPARE_StateAttribute_Parameter(_textureHeight)
124    COMPARE_StateAttribute_Parameter(_subloadCallback)
125
126    return 0; // passed all the above comparison macro's, must be equal.
127}
128
129
130void TextureCubeMap::setImage( unsigned int face, Image* image)
131{
132    if (_images[face] == image) return;
133
134    unsigned numImageSequencesBefore = 0;
135    for (unsigned int i=0; i<getNumImages(); ++i)
136    {
137        osg::ImageSequence* is = dynamic_cast<osg::ImageSequence*>(_images[i].get());
138        if (is) ++numImageSequencesBefore;
139    }
140
141    _images[face] = image;
142    _modifiedCount[face].setAllElementsTo(0);
143
144
145    // find out if we need to reset the update callback to handle the animation of ImageSequence
146    unsigned numImageSequencesAfter = 0;
147    for (unsigned int i=0; i<getNumImages(); ++i)
148    {
149        osg::ImageSequence* is = dynamic_cast<osg::ImageSequence*>(_images[i].get());
150        if (is) ++numImageSequencesAfter;
151    }
152
153    if (numImageSequencesBefore>0)
154    {
155        if (numImageSequencesAfter==0)
156        {
157            setUpdateCallback(0);
158            setDataVariance(osg::Object::STATIC);
159        }
160    }
161    else if (numImageSequencesAfter>0)
162    {
163        setUpdateCallback(new ImageSequence::UpdateCallback());
164        setDataVariance(osg::Object::DYNAMIC);
165    }
166}
167
168Image* TextureCubeMap::getImage(unsigned int face)
169{
170    return _images[face].get();
171}
172
173const Image* TextureCubeMap::getImage(unsigned int face) const
174{
175    return _images[face].get();
176}
177
178bool TextureCubeMap::imagesValid() const
179{
180    for (int n=0; n<6; n++)
181    {
182        if (!_images[n].valid() || !_images[n]->data())
183            return false;
184    }
185    return true;
186}
187
188void TextureCubeMap::computeInternalFormat() const
189{
190    if (imagesValid()) computeInternalFormatWithImage(*_images[0]);
191    else computeInternalFormatType();
192}
193
194void TextureCubeMap::apply(State& state) const
195{
196    // get the contextID (user defined ID of 0 upwards) for the
197    // current OpenGL context.
198    const unsigned int contextID = state.getContextID();
199
200    Texture::TextureObjectManager* tom = Texture::getTextureObjectManager(contextID).get();
201    ElapsedTime elapsedTime(&(tom->getApplyTime()));
202    tom->getNumberApplied()++;
203
204    const Extensions* extensions = getExtensions(contextID,true);
205
206    if (!extensions->isCubeMapSupported())
207        return;
208
209    // get the texture object for the current contextID.
210    TextureObject* textureObject = getTextureObject(contextID);
211
212    if (textureObject)
213    {
214        const osg::Image* image = _images[0].get();
215        if (image && getModifiedCount(0, contextID) != image->getModifiedCount())
216        {
217            // compute the internal texture format, this set the _internalFormat to an appropriate value.
218            computeInternalFormat();
219
220            GLsizei new_width, new_height, new_numMipmapLevels;
221
222            // compute the dimensions of the texture.
223            computeRequiredTextureDimensions(state, *image, new_width, new_height, new_numMipmapLevels);
224
225            if (!textureObject->match(GL_TEXTURE_CUBE_MAP, new_numMipmapLevels, _internalFormat, new_width, new_height, 1, _borderWidth))
226            {
227                Texture::releaseTextureObject(contextID, _textureObjectBuffer[contextID].get());
228                _textureObjectBuffer[contextID] = 0;
229                textureObject = 0;
230            }
231        }
232    }
233
234    if (textureObject)
235    {
236        textureObject->bind();
237
238        if (getTextureParameterDirty(state.getContextID())) applyTexParameters(GL_TEXTURE_CUBE_MAP,state);
239
240        if (_subloadCallback.valid())
241        {
242            _subloadCallback->subload(*this,state);
243        }
244        else
245        {
246            for (int n=0; n<6; n++)
247            {
248                const osg::Image* image = _images[n].get();
249                if (image && getModifiedCount((Face)n,contextID) != image->getModifiedCount())
250                {
251                    applyTexImage2D_subload( state, faceTarget[n], _images[n].get(), _textureWidth, _textureHeight, _internalFormat, _numMipmapLevels);
252                    getModifiedCount((Face)n,contextID) = image->getModifiedCount();
253                }
254            }
255        }
256
257    }
258    else if (_subloadCallback.valid())
259    {
260        _textureObjectBuffer[contextID] = textureObject = generateTextureObject(this, contextID,GL_TEXTURE_CUBE_MAP);
261
262        textureObject->bind();
263
264        applyTexParameters(GL_TEXTURE_CUBE_MAP,state);
265
266        _subloadCallback->load(*this,state);
267
268        // in theory the following line is redundent, but in practice
269        // have found that the first frame drawn doesn't apply the textures
270        // unless a second bind is called?!!
271        // perhaps it is the first glBind which is not required...
272        //glBindTexture( GL_TEXTURE_CUBE_MAP, handle );
273
274    }
275    else if (imagesValid())
276    {
277
278        // compute the internal texture format, this set the _internalFormat to an appropriate value.
279        computeInternalFormat();
280
281        // compute the dimensions of the texture.
282        computeRequiredTextureDimensions(state,*_images[0],_textureWidth, _textureHeight, _numMipmapLevels);
283
284        // cubemap textures must have square dimensions
285        if( _textureWidth != _textureHeight )
286        {
287            _textureWidth = _textureHeight = minimum( _textureWidth , _textureHeight );
288        }
289
290        textureObject = generateTextureObject(
291                this, contextID,GL_TEXTURE_CUBE_MAP,_numMipmapLevels,_internalFormat,_textureWidth,_textureHeight,1,0);
292       
293        textureObject->bind();
294
295        applyTexParameters(GL_TEXTURE_CUBE_MAP,state);
296
297        for (int n=0; n<6; n++)
298        {
299            const osg::Image* image = _images[n].get();
300            if (image)
301            {
302                if (textureObject->isAllocated())
303                {
304                    applyTexImage2D_subload( state, faceTarget[n], image, _textureWidth, _textureHeight, _internalFormat, _numMipmapLevels);
305                }
306                else
307                {
308                    applyTexImage2D_load( state, faceTarget[n], image, _textureWidth, _textureHeight, _numMipmapLevels);
309                }
310                getModifiedCount((Face)n,contextID) = image->getModifiedCount();
311            }
312
313
314        }
315
316        _textureObjectBuffer[contextID] = textureObject;
317
318        if (state.getMaxTexturePoolSize()==0 && _unrefImageDataAfterApply && areAllTextureObjectsLoaded())
319        {
320            TextureCubeMap* non_const_this = const_cast<TextureCubeMap*>(this);
321            for (int n=0; n<6; n++)
322            {               
323                if (_images[n].valid() && _images[n]->getDataVariance()==STATIC)
324                {
325                    non_const_this->_images[n] = 0;
326                }
327            }
328        }
329       
330    }
331    else if ( (_textureWidth!=0) && (_textureHeight!=0) && (_internalFormat!=0) )
332    {
333        _textureObjectBuffer[contextID] = textureObject = generateTextureObject(
334                this, contextID,GL_TEXTURE_CUBE_MAP,_numMipmapLevels,_internalFormat,_textureWidth,_textureHeight,1,0);
335       
336        textureObject->bind();
337
338        applyTexParameters(GL_TEXTURE_CUBE_MAP,state);
339
340        for (int n=0; n<6; n++)
341        {               
342            // no image present, but dimensions at set so less create the texture
343            glTexImage2D( faceTarget[n], 0, _internalFormat,
344                         _textureWidth, _textureHeight, _borderWidth,
345                         _sourceFormat ? _sourceFormat : _internalFormat,
346                         _sourceType ? _sourceType : GL_UNSIGNED_BYTE,
347                         0);               
348        }
349       
350    }
351    else
352    {
353        glBindTexture( GL_TEXTURE_CUBE_MAP, 0 );
354    }
355
356    // if texture object is now valid and we have to allocate mipmap levels, then
357    if (textureObject != 0 && _texMipmapGenerationDirtyList[contextID])
358    {
359        generateMipmap(state);
360    }
361}
362
363void TextureCubeMap::copyTexSubImageCubeMap(State& state, int face, int xoffset, int yoffset, int x, int y, int width, int height )
364{
365    const unsigned int contextID = state.getContextID();
366    const Extensions* extensions = getExtensions(contextID,true);
367
368    if (!extensions->isCubeMapSupported())
369        return;
370
371    if (_internalFormat==0) _internalFormat=GL_RGBA;
372
373    // get the texture object for the current contextID.
374    TextureObject* textureObject = getTextureObject(contextID);
375
376    if (!textureObject)
377    {
378
379        if (_textureWidth==0) _textureWidth = width;
380        if (_textureHeight==0) _textureHeight = height;
381
382        // create texture object.
383        apply(state);
384       
385        textureObject = getTextureObject(contextID);
386       
387        if (!textureObject)
388        {
389            // failed to create texture object
390            osg::notify(osg::NOTICE)<<"Warning : failed to create TextureCubeMap texture obeject, copyTexSubImageCubeMap abondoned."<<std::endl;
391            return;
392        }
393       
394    }
395
396    GLenum target = faceTarget[face];
397   
398    if (textureObject)
399    {
400        // we have a valid image
401        textureObject->bind();
402       
403        applyTexParameters(GL_TEXTURE_CUBE_MAP, state);
404
405        bool needHardwareMipMap = (_min_filter != LINEAR && _min_filter != NEAREST);
406        bool hardwareMipMapOn = false;
407        if (needHardwareMipMap)
408        {
409            hardwareMipMapOn = isHardwareMipmapGenerationEnabled(state);
410
411            if (!hardwareMipMapOn)
412            {
413                // have to switch off mip mapping
414                notify(NOTICE)<<"Warning: TextureCubeMap::copyTexImage2D(,,,,) switch off mip mapping as hardware support not available."<<std::endl;
415                _min_filter = LINEAR;
416            }
417        }
418
419        GenerateMipmapMode mipmapResult = mipmapBeforeTexImage(state, hardwareMipMapOn);
420
421        glCopyTexSubImage2D( target , 0, xoffset, yoffset, x, y, width, height);
422
423        mipmapAfterTexImage(state, mipmapResult);
424
425        // inform state that this texture is the current one bound.
426        state.haveAppliedTextureAttribute(state.getActiveTextureUnit(), this);
427
428    }
429}
430
431void TextureCubeMap::allocateMipmap(State& state) const
432{
433    const unsigned int contextID = state.getContextID();
434
435    // get the texture object for the current contextID.
436    TextureObject* textureObject = getTextureObject(contextID);
437   
438    if (textureObject && _textureWidth != 0 && _textureHeight != 0)
439    {
440        // bind texture
441        textureObject->bind();
442
443        // compute number of mipmap levels
444        int width = _textureWidth;
445        int height = _textureHeight;
446        int numMipmapLevels = Image::computeNumberOfMipmapLevels(width, height);
447
448        // we do not reallocate the level 0, since it was already allocated
449        width >>= 1;
450        height >>= 1;
451       
452        for( GLsizei k = 1; k < numMipmapLevels  && (width || height); k++)
453        {
454            if (width == 0)
455                width = 1;
456            if (height == 0)
457                height = 1;
458
459            for (int n=0; n<6; n++)
460            {
461                glTexImage2D( faceTarget[n], k, _internalFormat,
462                            width, height, _borderWidth,
463                            _sourceFormat ? _sourceFormat : _internalFormat,
464                            _sourceType ? _sourceType : GL_UNSIGNED_BYTE,
465                            0);
466            }
467       
468            width >>= 1;
469            height >>= 1;
470        }
471               
472        // inform state that this texture is the current one bound.
473        state.haveAppliedTextureAttribute(state.getActiveTextureUnit(), this);       
474    }
475}
476
477typedef buffered_value< ref_ptr<TextureCubeMap::Extensions> > BufferedExtensions;
478static BufferedExtensions s_extensions;
479
480TextureCubeMap::Extensions* TextureCubeMap::getExtensions(unsigned int contextID,bool createIfNotInitalized)
481{
482    if (!s_extensions[contextID] && createIfNotInitalized) s_extensions[contextID] = new Extensions(contextID);
483    return s_extensions[contextID].get();
484}
485
486void TextureCubeMap::setExtensions(unsigned int contextID,Extensions* extensions)
487{
488    s_extensions[contextID] = extensions;
489}
490
491TextureCubeMap::Extensions::Extensions(unsigned int contextID)
492{
493    setupGLExtensions(contextID);
494}
495
496TextureCubeMap::Extensions::Extensions(const Extensions& rhs):
497    Referenced()
498{
499    _isCubeMapSupported = rhs._isCubeMapSupported;
500}
501
502void TextureCubeMap::Extensions::lowestCommonDenominator(const Extensions& rhs)
503{
504    if (!rhs._isCubeMapSupported) _isCubeMapSupported = false;
505}
506
507void TextureCubeMap::Extensions::setupGLExtensions(unsigned int contextID)
508{
509    _isCubeMapSupported = OSG_GLES2_FEATURES || OSG_GL3_FEATURES ||
510                          isGLExtensionSupported(contextID,"GL_ARB_texture_cube_map") ||
511                          isGLExtensionSupported(contextID,"GL_EXT_texture_cube_map") ||
512                          strncmp((const char*)glGetString(GL_VERSION),"1.3",3)>=0;;
513}
Note: See TracBrowser for help on using the browser.