root/OpenSceneGraph/trunk/src/osg/Texture2D.cpp @ 10924

Revision 10924, 15.5 kB (checked in by robert, 4 years ago)

Refactored the way that osg::Image/ImageSequence manages the update callback that needs to be attached to Textures to make it possible to use the Image::update() mechansim in other subclasses from osg::Image.
To enable the automatic attachment of the required update callback to call osg::Image::update(..) subclasses from osg::Image will
need to implement the osg::Image::requestUpdateCall() and return true, and implement the osg::Image::update(NodeVisitor?*) method to recieve the update call during the update traversal.

  • 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
14#include <osg/GLExtensions>
15#include <osg/Texture2D>
16#include <osg/State>
17#include <osg/Notify>
18
19using namespace osg;
20
21Texture2D::Texture2D():
22            _textureWidth(0),
23            _textureHeight(0),
24            _numMipmapLevels(0)
25{
26    setUseHardwareMipMapGeneration(true);
27}
28
29Texture2D::Texture2D(Image* image):
30            _textureWidth(0),
31            _textureHeight(0),
32            _numMipmapLevels(0)
33{
34    setUseHardwareMipMapGeneration(true);
35    setImage(image);
36}
37
38Texture2D::Texture2D(const Texture2D& text,const CopyOp& copyop):
39            Texture(text,copyop),
40            _image(copyop(text._image.get())),
41            _textureWidth(text._textureWidth),
42            _textureHeight(text._textureHeight),
43            _numMipmapLevels(text._numMipmapLevels),
44            _subloadCallback(text._subloadCallback)
45{
46}
47
48Texture2D::~Texture2D()
49{
50}
51
52int Texture2D::compare(const StateAttribute& sa) const
53{
54    // check the types are equal and then create the rhs variable
55    // used by the COMPARE_StateAttribute_Parameter macro's below.
56    COMPARE_StateAttribute_Types(Texture2D,sa)
57
58    if (_image!=rhs._image) // smart pointer comparison.
59    {
60        if (_image.valid())
61        {
62            if (rhs._image.valid())
63            {
64                int result = _image->compare(*rhs._image);
65                if (result!=0) return result;
66            }
67            else
68            {
69                return 1; // valid lhs._image is greater than null.
70            }
71        }
72        else if (rhs._image.valid())
73        {
74            return -1; // valid rhs._image is greater than null.
75        }
76    }
77
78    if (!_image && !rhs._image)
79    {
80        // no image attached to either Texture2D
81        // but could these textures already be downloaded?
82        // check the _textureObjectBuffer to see if they have been
83        // downloaded
84
85        int result = compareTextureObjects(rhs);
86        if (result!=0) return result;
87    }
88
89    int result = compareTexture(rhs);
90    if (result!=0) return result;
91
92    // compare each parameter in turn against the rhs.
93#if 1   
94    if (_textureWidth != 0 && rhs._textureWidth != 0)
95    {
96        COMPARE_StateAttribute_Parameter(_textureWidth)
97    }
98    if (_textureHeight != 0 && rhs._textureHeight != 0)
99    {
100        COMPARE_StateAttribute_Parameter(_textureHeight)
101    }
102#endif
103    COMPARE_StateAttribute_Parameter(_subloadCallback)
104
105    return 0; // passed all the above comparison macro's, must be equal.
106}
107
108void Texture2D::setImage(Image* image)
109{
110    if (_image == image) return;
111
112    if (_image.valid() && _image->requiresUpdateCall())
113    {
114        setUpdateCallback(0);
115        setDataVariance(osg::Object::STATIC);
116    }
117
118    _image = image;
119    _modifiedCount.setAllElementsTo(0);
120
121    if (_image.valid() && _image->requiresUpdateCall())
122    {
123        setUpdateCallback(new Image::UpdateCallback());
124        setDataVariance(osg::Object::DYNAMIC);
125    }
126}
127
128
129void Texture2D::apply(State& state) const
130{
131
132    //state.setReportGLErrors(true);
133
134    // get the contextID (user defined ID of 0 upwards) for the
135    // current OpenGL context.
136    const unsigned int contextID = state.getContextID();
137
138    Texture::TextureObjectManager* tom = Texture::getTextureObjectManager(contextID).get();
139    ElapsedTime elapsedTime(&(tom->getApplyTime()));
140    tom->getNumberApplied()++;
141
142    // get the texture object for the current contextID.
143    TextureObject* textureObject = getTextureObject(contextID);
144
145    if (textureObject)
146    {
147        if (_image.valid() && getModifiedCount(contextID) != _image->getModifiedCount())
148        {
149            // compute the internal texture format, this set the _internalFormat to an appropriate value.
150            computeInternalFormat();
151
152            GLsizei new_width, new_height, new_numMipmapLevels;
153
154            // compute the dimensions of the texture.
155            computeRequiredTextureDimensions(state, *_image, new_width, new_height, new_numMipmapLevels);
156
157            if (!textureObject->match(GL_TEXTURE_2D, new_numMipmapLevels, _internalFormat, new_width, new_height, 1, _borderWidth))
158            {
159                Texture::releaseTextureObject(contextID, _textureObjectBuffer[contextID].get());
160                _textureObjectBuffer[contextID] = 0;
161                textureObject = 0;
162            }
163        }
164    }
165
166    if (textureObject)
167    {
168        textureObject->bind();
169
170        if (getTextureParameterDirty(state.getContextID()))
171            applyTexParameters(GL_TEXTURE_2D,state);
172
173        if (_subloadCallback.valid())
174        {
175            _subloadCallback->subload(*this,state);
176        }
177        else if (_image.valid() && getModifiedCount(contextID) != _image->getModifiedCount())
178        {
179            applyTexImage2D_subload(state,GL_TEXTURE_2D,_image.get(),
180                                    _textureWidth, _textureHeight, _internalFormat, _numMipmapLevels);
181 
182            // update the modified tag to show that it is up to date.
183            getModifiedCount(contextID) = _image->getModifiedCount();
184     
185        }
186        else if (_readPBuffer.valid())
187        {
188            _readPBuffer->bindPBufferToTexture(GL_FRONT);
189        }
190
191    }
192    else if (_subloadCallback.valid())
193    {
194
195        _textureObjectBuffer[contextID] = textureObject = generateTextureObject(this, contextID,GL_TEXTURE_2D);
196
197        textureObject->bind();
198
199        applyTexParameters(GL_TEXTURE_2D,state);
200
201        _subloadCallback->load(*this,state);
202       
203        textureObject->setAllocated(_numMipmapLevels,_internalFormat,_textureWidth,_textureHeight,1,_borderWidth);
204
205        // in theory the following line is redundent, but in practice
206        // have found that the first frame drawn doesn't apply the textures
207        // unless a second bind is called?!!
208        // perhaps it is the first glBind which is not required...
209        //glBindTexture( GL_TEXTURE_2D, handle );
210
211        // update the modified tag to show that it is upto date.
212        if (_image.valid()) getModifiedCount(contextID) = _image->getModifiedCount();
213    }
214    else if (_image.valid() && _image->data())
215    {
216
217        // keep the image around at least till we go out of scope.
218        osg::ref_ptr<osg::Image> image = _image;
219
220        // compute the internal texture format, this set the _internalFormat to an appropriate value.
221        computeInternalFormat();
222
223        // compute the dimensions of the texture.
224        computeRequiredTextureDimensions(state,*image,_textureWidth, _textureHeight, _numMipmapLevels);
225
226        _textureObjectBuffer[contextID] = textureObject = generateTextureObject(
227                this, contextID,GL_TEXTURE_2D,_numMipmapLevels,_internalFormat,_textureWidth,_textureHeight,1,_borderWidth);
228
229        textureObject->bind();
230
231        applyTexParameters(GL_TEXTURE_2D,state);
232
233        if (textureObject->isAllocated())
234        {
235            //notify(NOTICE)<<"Reusing texture object"<<std::endl;
236            applyTexImage2D_subload(state,GL_TEXTURE_2D,image.get(),
237                                 _textureWidth, _textureHeight, _internalFormat, _numMipmapLevels);
238        }
239        else
240        {
241            //notify(NOTICE)<<"Creating new texture object"<<std::endl;
242            applyTexImage2D_load(state,GL_TEXTURE_2D,image.get(),
243                                 _textureWidth, _textureHeight, _numMipmapLevels);
244
245            textureObject->setAllocated(true);
246        }
247
248        // update the modified tag to show that it is upto date.
249        getModifiedCount(contextID) = image->getModifiedCount();
250
251        if (state.getMaxTexturePoolSize()==0 &&
252            _unrefImageDataAfterApply &&
253            areAllTextureObjectsLoaded() &&
254            image->getDataVariance()==STATIC)
255        {
256            Texture2D* non_const_this = const_cast<Texture2D*>(this);
257            non_const_this->_image = 0;
258        }
259
260        // in theory the following line is redundent, but in practice
261        // have found that the first frame drawn doesn't apply the textures
262        // unless a second bind is called?!!
263        // perhaps it is the first glBind which is not required...
264        //glBindTexture( GL_TEXTURE_2D, handle );
265       
266    }
267    else if ( (_textureWidth!=0) && (_textureHeight!=0) && (_internalFormat!=0) )
268    {
269        _textureObjectBuffer[contextID] = textureObject = generateTextureObject(
270                this, contextID,GL_TEXTURE_2D,_numMipmapLevels,_internalFormat,_textureWidth,_textureHeight,1,_borderWidth);
271       
272        textureObject->bind();
273
274        applyTexParameters(GL_TEXTURE_2D,state);
275
276        // no image present, but dimensions at set so lets create the texture
277        glTexImage2D( GL_TEXTURE_2D, 0, _internalFormat,
278                     _textureWidth, _textureHeight, _borderWidth,
279                     _sourceFormat ? _sourceFormat : _internalFormat,
280                     _sourceType ? _sourceType : GL_UNSIGNED_BYTE,
281                     0);               
282                     
283        if (_readPBuffer.valid())
284        {
285            _readPBuffer->bindPBufferToTexture(GL_FRONT);
286        }
287       
288    }
289    else
290    {
291        glBindTexture( GL_TEXTURE_2D, 0 );
292    }
293
294    // if texture object is now valid and we have to allocate mipmap levels, then
295    if (textureObject != 0 && _texMipmapGenerationDirtyList[contextID])
296    {
297        generateMipmap(state);
298    }
299}
300
301void Texture2D::computeInternalFormat() const
302{
303    if (_image.valid()) computeInternalFormatWithImage(*_image);
304    else computeInternalFormatType();
305}
306
307void Texture2D::copyTexImage2D(State& state, int x, int y, int width, int height )
308{
309    const unsigned int contextID = state.getContextID();
310   
311    if (_internalFormat==0) _internalFormat=GL_RGBA;
312
313    // get the globj for the current contextID.
314    TextureObject* textureObject = getTextureObject(contextID);
315   
316    if (textureObject)
317    {
318        if (width==(int)_textureWidth && height==(int)_textureHeight)
319        {
320            // we have a valid texture object which is the right size
321            // so lets play clever and use copyTexSubImage2D instead.
322            // this allows use to reuse the texture object and avoid
323            // expensive memory allocations.
324            copyTexSubImage2D(state,0 ,0, x, y, width, height);
325            return;
326        }
327        // the relevent texture object is not of the right size so
328        // needs to been deleted   
329        // remove previously bound textures.
330        dirtyTextureObject();
331        // note, dirtyTextureObject() dirties all the texture objects for
332        // this texture, is this right?  Perhaps we should dirty just the
333        // one for this context.  Note sure yet will leave till later.
334        // RO July 2001.
335    }
336   
337   
338    // remove any previously assigned images as these are nolonger valid.
339    _image = NULL;
340
341    // switch off mip-mapping.
342    //
343    _textureObjectBuffer[contextID] = textureObject = generateTextureObject(this, contextID,GL_TEXTURE_2D);
344
345    textureObject->bind();
346   
347    applyTexParameters(GL_TEXTURE_2D,state);
348
349
350    bool needHardwareMipMap = (_min_filter != LINEAR && _min_filter != NEAREST);
351    bool hardwareMipMapOn = false;
352    if (needHardwareMipMap)
353    {
354        hardwareMipMapOn = isHardwareMipmapGenerationEnabled(state);
355       
356        if (!hardwareMipMapOn)
357        {
358            // have to switch off mip mapping
359            notify(NOTICE)<<"Warning: Texture2D::copyTexImage2D(,,,,) switch off mip mapping as hardware support not available."<<std::endl;
360            _min_filter = LINEAR;
361        }
362    }
363   
364    GenerateMipmapMode mipmapResult = mipmapBeforeTexImage(state, hardwareMipMapOn);
365
366    glCopyTexImage2D( GL_TEXTURE_2D, 0, _internalFormat, x, y, width, height, 0 );
367
368    mipmapAfterTexImage(state, mipmapResult);
369
370    _textureWidth = width;
371    _textureHeight = height;
372
373    _numMipmapLevels = 1;
374    if (needHardwareMipMap)
375    {
376        for(int s=1; s<width || s<height; s <<= 1, ++_numMipmapLevels) {}
377    }
378
379    textureObject->setAllocated(_numMipmapLevels,_internalFormat,_textureWidth,_textureHeight,1,0);
380
381    // inform state that this texture is the current one bound.
382    state.haveAppliedTextureAttribute(state.getActiveTextureUnit(), this);
383}
384
385void Texture2D::copyTexSubImage2D(State& state, int xoffset, int yoffset, int x, int y, int width, int height )
386{
387    const unsigned int contextID = state.getContextID();
388
389    if (_internalFormat==0) _internalFormat=GL_RGBA;
390
391    // get the texture object for the current contextID.
392    TextureObject* textureObject = getTextureObject(contextID);
393   
394    if (textureObject)
395    {
396        // we have a valid image
397        textureObject->bind();
398       
399        applyTexParameters(GL_TEXTURE_2D,state);
400
401        bool needHardwareMipMap = (_min_filter != LINEAR && _min_filter != NEAREST);
402        bool hardwareMipMapOn = false;
403        if (needHardwareMipMap)
404        {
405            hardwareMipMapOn = isHardwareMipmapGenerationEnabled(state);
406
407            if (!hardwareMipMapOn)
408            {
409                // have to switch off mip mapping
410                notify(NOTICE)<<"Warning: Texture2D::copyTexImage2D(,,,,) switch off mip mapping as hardware support not available."<<std::endl;
411                _min_filter = LINEAR;
412            }
413        }
414
415        GenerateMipmapMode mipmapResult = mipmapBeforeTexImage(state, hardwareMipMapOn);
416
417        glCopyTexSubImage2D( GL_TEXTURE_2D, 0, xoffset, yoffset, x, y, width, height);
418
419        mipmapAfterTexImage(state, mipmapResult);
420
421        // inform state that this texture is the current one bound.
422        state.haveAppliedTextureAttribute(state.getActiveTextureUnit(), this);
423
424    }
425    else
426    {
427        // no texture object already exsits for this context so need to
428        // create it upfront - simply call copyTexImage2D.
429        copyTexImage2D(state,x,y,width,height);
430    }
431}
432
433void Texture2D::allocateMipmap(State& state) const
434{
435    const unsigned int contextID = state.getContextID();
436
437    // get the texture object for the current contextID.
438    TextureObject* textureObject = getTextureObject(contextID);
439   
440    if (textureObject && _textureWidth != 0 && _textureHeight != 0)
441    {
442        // bind texture
443        textureObject->bind();
444
445        // compute number of mipmap levels
446        int width = _textureWidth;
447        int height = _textureHeight;
448        int numMipmapLevels = Image::computeNumberOfMipmapLevels(width, height);
449
450        // we do not reallocate the level 0, since it was already allocated
451        width >>= 1;
452        height >>= 1;
453       
454        for( GLsizei k = 1; k < numMipmapLevels  && (width || height); k++)
455        {
456            if (width == 0)
457                width = 1;
458            if (height == 0)
459                height = 1;
460
461            glTexImage2D( GL_TEXTURE_2D, k, _internalFormat,
462                     width, height, _borderWidth,
463                     _sourceFormat ? _sourceFormat : _internalFormat,
464                     _sourceType ? _sourceType : GL_UNSIGNED_BYTE, NULL);
465
466            width >>= 1;
467            height >>= 1;
468        }
469               
470        // inform state that this texture is the current one bound.
471        state.haveAppliedTextureAttribute(state.getActiveTextureUnit(), this);       
472    }
473}
Note: See TracBrowser for help on using the browser.