root/OpenSceneGraph/trunk/src/osg/Texture3D.cpp @ 12912

Revision 12912, 21.1 kB (checked in by robert, 2 years ago)

Added support for using GL_UNPACK_ROW_LENGTH in conjunction with texture's + osg::Image via new RowLength?
parameter in osg::Image. To support this Image::setData(..) now has a new optional rowLength parameter which
defaults to 0, which provides the original behaviour, Image::setRowLength(int) and int Image::getRowLength() are also provided.

With the introduction of RowLength? support in osg::Image it is now possible to create a sub image where
the t size of the image are smaller than the row length, useful for when you have a large image on the CPU
and which to use a small portion of it on the GPU. However, when these sub images are created the data
within the image is no longer contiguous so data access can no longer assume that all the data is in
one block. The new method Image::isDataContiguous() enables the user to check whether the data is contiguous,
and if not one can either access the data row by row using Image::data(column,row,image) accessor, or use the
new Image::DataIterator? for stepping through each block on memory assocatied with the image.

To support the possibility of non contiguous osg::Image usage of image objects has had to be updated to
check DataContiguous? and handle the case or use access via the DataIerator? or by row by row. To achieve
this a relatively large number of files has had to be modified, in particular the texture classes and
image plugins that doing writing.

  • 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/Texture3D>
15#include <osg/State>
16#include <osg/GLU>
17#include <osg/Notify>
18
19#include <string.h>
20
21
22
23using namespace osg;
24
25Texture3D::Texture3D():
26            _textureWidth(0),
27            _textureHeight(0),
28            _textureDepth(0),
29            _numMipmapLevels(0)
30{
31}
32
33
34Texture3D::Texture3D(Image* image):
35            _textureWidth(0),
36            _textureHeight(0),
37            _textureDepth(0),
38            _numMipmapLevels(0)
39{
40    setImage(image);
41}
42
43Texture3D::Texture3D(const Texture3D& text,const CopyOp& copyop):
44            Texture(text,copyop),
45            _image(copyop(text._image.get())),
46            _textureWidth(text._textureWidth),
47            _textureHeight(text._textureHeight),
48            _textureDepth(text._textureDepth),
49            _numMipmapLevels(text._numMipmapLevels),
50            _subloadCallback(text._subloadCallback)
51{
52}
53
54Texture3D::~Texture3D()
55{
56}
57
58int Texture3D::compare(const StateAttribute& sa) const
59{
60    // check the types are equal and then create the rhs variable
61    // used by the COMPARE_StateAttribute_Parameter macros below.
62    COMPARE_StateAttribute_Types(Texture3D,sa)
63
64    if (_image!=rhs._image) // smart pointer comparison.
65    {
66        if (_image.valid())
67        {
68            if (rhs._image.valid())
69            {
70                int result = _image->compare(*rhs._image);
71                if (result!=0) return result;
72            }
73            else
74            {
75                return 1; // valid lhs._image is greater than null.
76            }
77        }
78        else if (rhs._image.valid())
79        {
80            return -1; // valid rhs._image is greater than null.
81        }
82    }
83
84    if (!_image && !rhs._image)
85    {
86        // no image attached to either Texture2D
87        // but could these textures already be downloaded?
88        // check the _textureObjectBuffer to see if they have been
89        // downloaded
90
91        int result = compareTextureObjects(rhs);
92        if (result!=0) return result;
93    }
94
95    int result = compareTexture(rhs);
96    if (result!=0) return result;
97
98    // compare each parameter in turn against the rhs.
99    COMPARE_StateAttribute_Parameter(_textureWidth)
100    COMPARE_StateAttribute_Parameter(_textureHeight)
101    COMPARE_StateAttribute_Parameter(_textureDepth)
102    COMPARE_StateAttribute_Parameter(_subloadCallback)
103
104    return 0; // passed all the above comparison macros, must be equal.
105}
106
107void Texture3D::setImage(Image* image)
108{
109    if (_image == image) return;
110
111    if (_image.valid() && _image->requiresUpdateCall())
112    {
113        setUpdateCallback(0);
114        setDataVariance(osg::Object::STATIC);
115    }
116
117    // delete old texture objects.
118    dirtyTextureObject();
119
120    _modifiedCount.setAllElementsTo(0);
121
122    _image = image;
123
124    if (_image.valid() && _image->requiresUpdateCall())
125    {
126        setUpdateCallback(new Image::UpdateCallback());
127        setDataVariance(osg::Object::DYNAMIC);
128    }
129}
130
131void Texture3D::computeRequiredTextureDimensions(State& state, const osg::Image& image,GLsizei& inwidth, GLsizei& inheight,GLsizei& indepth, GLsizei& numMipmapLevels) const
132{
133    const unsigned int contextID = state.getContextID();
134    const Extensions* extensions = getExtensions(contextID,true);
135    const Texture::Extensions* texExtensions = Texture::getExtensions(contextID,true);
136
137    int width,height,depth;
138
139    if( !_resizeNonPowerOfTwoHint && texExtensions->isNonPowerOfTwoTextureSupported(_min_filter) )
140    {
141        width = image.s();
142        height = image.t();
143        depth = image.r();
144    }
145    else
146    {
147        width = Image::computeNearestPowerOfTwo(image.s()-2*_borderWidth)+2*_borderWidth;
148        height = Image::computeNearestPowerOfTwo(image.t()-2*_borderWidth)+2*_borderWidth;
149        depth = Image::computeNearestPowerOfTwo(image.r()-2*_borderWidth)+2*_borderWidth;
150    }
151
152    // cap the size to what the graphics hardware can handle.
153    if (width>extensions->maxTexture3DSize()) width = extensions->maxTexture3DSize();
154    if (height>extensions->maxTexture3DSize()) height = extensions->maxTexture3DSize();
155    if (depth>extensions->maxTexture3DSize()) depth = extensions->maxTexture3DSize();
156   
157    inwidth = width;
158    inheight = height;
159    indepth = depth;
160   
161    bool useHardwareMipMapGeneration = !image.isMipmap() && _useHardwareMipMapGeneration && texExtensions->isGenerateMipMapSupported();
162
163    if( _min_filter == LINEAR || _min_filter == NEAREST || useHardwareMipMapGeneration )
164    {
165        numMipmapLevels = 1;
166    }
167    else if( image.isMipmap() )
168    {
169        numMipmapLevels = image.getNumMipmapLevels();
170    }
171    else
172    {
173        numMipmapLevels = 0;
174        for( ; (width || height || depth) ;++numMipmapLevels)
175        {
176
177            if (width == 0)
178                width = 1;
179            if (height == 0)
180                height = 1;
181            if (depth == 0)
182                depth = 1;
183
184            width >>= 1;
185            height >>= 1;
186            depth >>= 1;
187        }   
188    }
189}
190
191void Texture3D::apply(State& state) const
192{
193
194    // get the contextID (user defined ID of 0 upwards) for the
195    // current OpenGL context.
196    const unsigned int contextID = state.getContextID();
197
198    Texture::TextureObjectManager* tom = Texture::getTextureObjectManager(contextID).get();
199    ElapsedTime elapsedTime(&(tom->getApplyTime()));
200    tom->getNumberApplied()++;
201
202    const Extensions* extensions = getExtensions(contextID,true);
203                                       
204    if (!extensions->isTexture3DSupported())
205    {
206        OSG_WARN<<"Warning: Texture3D::apply(..) failed, 3D texturing is not support by OpenGL driver."<<std::endl;
207        return;
208    }
209
210    // get the texture object for the current contextID.
211    TextureObject* textureObject = getTextureObject(contextID);
212
213    if (textureObject)
214    {
215        if (_image.valid() && getModifiedCount(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_depth, new_numMipmapLevels;
221
222            // compute the dimensions of the texture.
223            computeRequiredTextureDimensions(state, *_image, new_width, new_height, new_depth, new_numMipmapLevels);
224
225            if (!textureObject->match(GL_TEXTURE_3D, new_numMipmapLevels, _internalFormat, new_width, new_height, new_depth, _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        // we have a valid image
237        textureObject->bind();
238
239        if (getTextureParameterDirty(state.getContextID())) applyTexParameters(GL_TEXTURE_3D,state);
240
241        if (_subloadCallback.valid())
242        {
243            _subloadCallback->subload(*this,state);
244        }
245        else if (_image.get() && getModifiedCount(contextID) != _image->getModifiedCount())
246        {
247           computeRequiredTextureDimensions(state,*_image,_textureWidth, _textureHeight, _textureDepth,_numMipmapLevels);
248
249            applyTexImage3D(GL_TEXTURE_3D,_image.get(),state, _textureWidth, _textureHeight, _textureDepth,_numMipmapLevels);
250
251            // update the modified count to show that it is upto date.
252            getModifiedCount(contextID) = _image->getModifiedCount();
253        }
254
255    }
256    else if (_subloadCallback.valid())
257    {
258
259        _textureObjectBuffer[contextID] = textureObject = generateTextureObject(this, contextID,GL_TEXTURE_3D);
260
261        textureObject->bind();
262
263        applyTexParameters(GL_TEXTURE_3D,state);
264
265        _subloadCallback->load(*this,state);
266
267        textureObject->setAllocated(_numMipmapLevels,_internalFormat,_textureWidth,_textureHeight,_textureDepth,0);
268
269        // in theory the following line is redundent, but in practice
270        // have found that the first frame drawn doesn't apply the textures
271        // unless a second bind is called?!!
272        // perhaps it is the first glBind which is not required...
273        //glBindTexture( GL_TEXTURE_3D, handle );
274
275    }
276    else if (_image.valid() && _image->data())
277    {
278
279        // compute the internal texture format, this set the _internalFormat to an appropriate value.
280        computeInternalFormat();
281
282        // compute the dimensions of the texture.
283        computeRequiredTextureDimensions(state,*_image,_textureWidth, _textureHeight, _textureDepth,_numMipmapLevels);
284
285        textureObject = generateTextureObject(this, contextID,GL_TEXTURE_3D);
286
287        textureObject->bind();
288
289
290        applyTexParameters(GL_TEXTURE_3D,state);
291
292        applyTexImage3D(GL_TEXTURE_3D,_image.get(),state, _textureWidth, _textureHeight, _textureDepth,_numMipmapLevels);
293
294        textureObject->setAllocated(_numMipmapLevels,_internalFormat,_textureWidth,_textureHeight,_textureDepth,0);
295
296        // update the modified count to show that it is upto date.
297        getModifiedCount(contextID) = _image->getModifiedCount();
298
299        _textureObjectBuffer[contextID] = textureObject;
300
301        // unref image data?
302        if (isSafeToUnrefImageData(state) && _image->getDataVariance()==STATIC)
303        {
304            Texture3D* non_const_this = const_cast<Texture3D*>(this);
305            non_const_this->_image = NULL;
306        }
307
308    }
309    else if ( (_textureWidth!=0) && (_textureHeight!=0) && (_textureDepth!=0) && (_internalFormat!=0) )
310    {
311        _textureObjectBuffer[contextID] = textureObject = generateTextureObject(
312                this, contextID,GL_TEXTURE_3D,_numMipmapLevels,_internalFormat,_textureWidth,_textureHeight,_textureDepth,0);
313       
314        textureObject->bind();
315
316        applyTexParameters(GL_TEXTURE_3D,state);
317
318        // no image present, but dimensions at set so lets create the texture
319        extensions->glTexImage3D( GL_TEXTURE_3D, 0, _internalFormat,
320                     _textureWidth, _textureHeight, _textureDepth,
321                     _borderWidth,
322                     _sourceFormat ? _sourceFormat : _internalFormat,
323                     _sourceType ? _sourceType : GL_UNSIGNED_BYTE,
324                     0);               
325                     
326        if (_readPBuffer.valid())
327        {
328            _readPBuffer->bindPBufferToTexture(GL_FRONT);
329        }
330       
331    }
332    else
333    {
334        glBindTexture( GL_TEXTURE_3D, 0 );
335    }
336   
337    // if texture object is now valid and we have to allocate mipmap levels, then
338    if (textureObject != 0 && _texMipmapGenerationDirtyList[contextID])
339    {
340        generateMipmap(state);
341    }
342}
343
344void Texture3D::computeInternalFormat() const
345{
346    if (_image.valid()) computeInternalFormatWithImage(*_image);
347    else computeInternalFormatType();
348}
349
350void Texture3D::applyTexImage3D(GLenum target, Image* image, State& state, GLsizei& inwidth, GLsizei& inheight, GLsizei& indepth, GLsizei& numMipmapLevels) const
351{
352    // if we don't have a valid image we can't create a texture!
353    if (!image || !image->data())
354        return;
355
356    // get the contextID (user defined ID of 0 upwards) for the
357    // current OpenGL context.
358    const unsigned int contextID = state.getContextID();
359    const Extensions* extensions = getExtensions(contextID,true);   
360    const Texture::Extensions* texExtensions = Texture::getExtensions(contextID,true);
361
362    // compute the internal texture format, this set the _internalFormat to an appropriate value.
363    computeInternalFormat();
364
365    // select the internalFormat required for the texture.
366    bool compressed = isCompressedInternalFormat(_internalFormat);
367    bool compressed_image = isCompressedInternalFormat((GLenum)image->getPixelFormat());
368
369    if (compressed)
370    {
371        //OSG_WARN<<"Warning::cannot currently use compressed format with 3D textures."<<std::endl;
372        //return;
373    }   
374   
375    //Rescale if resize hint is set or NPOT not supported or dimensions exceed max size
376    if( _resizeNonPowerOfTwoHint || !texExtensions->isNonPowerOfTwoTextureSupported(_min_filter)
377        || inwidth > extensions->maxTexture3DSize()
378        || inheight > extensions->maxTexture3DSize()
379        || indepth > extensions->maxTexture3DSize() )
380        image->ensureValidSizeForTexturing(extensions->maxTexture3DSize());
381
382    glPixelStorei(GL_UNPACK_ALIGNMENT,image->getPacking());
383    glPixelStorei(GL_UNPACK_ROW_LENGTH,image->getRowLength());
384
385    bool useHardwareMipMapGeneration = !image->isMipmap() && _useHardwareMipMapGeneration && texExtensions->isGenerateMipMapSupported();
386
387    if( _min_filter == LINEAR || _min_filter == NEAREST || useHardwareMipMapGeneration )
388    {
389        bool hardwareMipMapOn = false;
390        if (_min_filter != LINEAR && _min_filter != NEAREST)
391        {
392            if (useHardwareMipMapGeneration) glTexParameteri(GL_TEXTURE_3D, GL_GENERATE_MIPMAP_SGIS,GL_TRUE);
393            hardwareMipMapOn = true;
394        }
395
396        numMipmapLevels = 1;
397
398        if (!compressed_image)
399        {
400            extensions->glTexImage3D( target, 0, _internalFormat,
401                                      inwidth, inheight, indepth,
402                                      _borderWidth,
403                                      (GLenum)image->getPixelFormat(),
404                                      (GLenum)image->getDataType(),
405                                      image->data() );
406        }
407        else if (extensions->isCompressedTexImage3DSupported())
408        {
409            // OSG_WARN<<"glCompressedTexImage3D "<<inwidth<<", "<<inheight<<", "<<indepth<<std::endl;
410            numMipmapLevels = 1;
411
412            GLint blockSize, size;
413            getCompressedSize(_internalFormat, inwidth, inheight, indepth, blockSize,size);
414
415            extensions->glCompressedTexImage3D(target, 0, _internalFormat,
416                inwidth, inheight, indepth,
417                _borderWidth,
418                size,
419                image->data());
420        }
421
422        if (hardwareMipMapOn) glTexParameteri(GL_TEXTURE_3D, GL_GENERATE_MIPMAP_SGIS,GL_FALSE);
423    }
424    else
425    {
426        if(!image->isMipmap())
427        {
428
429            numMipmapLevels = 1;
430
431            gluBuild3DMipmaps( extensions->glTexImage3D,
432                               target, _internalFormat,
433                               image->s(),image->t(),image->r(),
434                               (GLenum)image->getPixelFormat(), (GLenum)image->getDataType(),
435                               image->data() );
436
437        }
438        else
439        {
440            numMipmapLevels = image->getNumMipmapLevels();
441
442            int width  = image->s();
443            int height = image->t();
444            int depth = image->r();
445
446            for( GLsizei k = 0 ; k < numMipmapLevels  && (width || height || depth) ;k++)
447            {
448
449                if (width == 0)
450                    width = 1;
451                if (height == 0)
452                    height = 1;
453                if (depth == 0)
454                    depth = 1;
455
456                extensions->glTexImage3D( target, k, _internalFormat,
457                                          width, height, depth, _borderWidth,
458                                          (GLenum)image->getPixelFormat(),
459                                          (GLenum)image->getDataType(),
460                                          image->getMipmapData(k));
461
462                width >>= 1;
463                height >>= 1;
464                depth >>= 1;
465            }
466        }
467
468    }
469
470    inwidth  = image->s();
471    inheight = image->t();
472    indepth  = image->r();
473   
474}
475
476void Texture3D::copyTexSubImage3D(State& state, int xoffset, int yoffset, int zoffset, int x, int y, int width, int height )
477{
478    const unsigned int contextID = state.getContextID();
479    const Extensions* extensions = getExtensions(contextID,true);
480
481    // get the texture object for the current contextID.
482    TextureObject* textureObject = getTextureObject(contextID);
483
484    if (textureObject != 0)
485    {
486        textureObject->bind();
487
488        applyTexParameters(GL_TEXTURE_3D,state);
489        extensions->glCopyTexSubImage3D( GL_TEXTURE_3D, 0, xoffset,yoffset,zoffset, x, y, width, height);
490
491        /* Redundant, delete later */
492        //glBindTexture( GL_TEXTURE_3D, handle );
493
494        // inform state that this texture is the current one bound.
495        state.haveAppliedTextureAttribute(state.getActiveTextureUnit(), this);
496
497    }
498    else
499    {
500        OSG_WARN<<"Warning: Texture3D::copyTexSubImage3D(..) failed, cannot not copy to a non existant texture."<<std::endl;
501    }
502}
503
504void Texture3D::allocateMipmap(State& state) const
505{
506    const unsigned int contextID = state.getContextID();
507
508    // get the texture object for the current contextID.
509    TextureObject* textureObject = getTextureObject(contextID);
510   
511    if (textureObject && _textureWidth != 0 && _textureHeight != 0 && _textureDepth != 0)
512    {
513        const Extensions* extensions = getExtensions(contextID,true);
514   
515        // bind texture
516        textureObject->bind();
517
518        // compute number of mipmap levels
519        int width = _textureWidth;
520        int height = _textureHeight;
521        int depth = _textureDepth;
522        int numMipmapLevels = Image::computeNumberOfMipmapLevels(width, height, depth);
523
524        // we do not reallocate the level 0, since it was already allocated
525        width >>= 1;
526        height >>= 1;
527        depth >>= 1;
528               
529        for( GLsizei k = 1; k < numMipmapLevels  && (width || height || depth); k++)
530        {
531            if (width == 0)
532                width = 1;
533            if (height == 0)
534                height = 1;
535            if (depth == 0)
536                depth = 1;
537
538            extensions->glTexImage3D( GL_TEXTURE_3D, k, _internalFormat,
539                     width, height, depth, _borderWidth,
540                     _sourceFormat ? _sourceFormat : _internalFormat,
541                     _sourceType ? _sourceType : GL_UNSIGNED_BYTE, NULL);
542
543            width >>= 1;
544            height >>= 1;
545            depth >>= 1;
546        }
547               
548        // inform state that this texture is the current one bound.
549        state.haveAppliedTextureAttribute(state.getActiveTextureUnit(), this);       
550    }
551}
552
553typedef buffered_value< ref_ptr<Texture3D::Extensions> > BufferedExtensions;
554static BufferedExtensions s_extensions;
555
556Texture3D::Extensions* Texture3D::getExtensions(unsigned int contextID,bool createIfNotInitalized)
557{
558    if (!s_extensions[contextID] && createIfNotInitalized) s_extensions[contextID] = new Extensions(contextID);
559    return s_extensions[contextID].get();
560}
561
562void Texture3D::setExtensions(unsigned int contextID,Extensions* extensions)
563{
564    s_extensions[contextID] = extensions;
565}
566
567#ifndef GL_MAX_3D_TEXTURE_SIZE
568#define GL_MAX_3D_TEXTURE_SIZE 0x8073
569#endif
570
571Texture3D::Extensions::Extensions(unsigned int contextID)
572{
573    setupGLExtensions(contextID);
574}
575
576Texture3D::Extensions::Extensions(const Extensions& rhs):
577    Referenced()
578{
579    _isTexture3DSupported = rhs._isTexture3DSupported;
580    _isTexture3DFast = rhs._isTexture3DFast;
581    _maxTexture3DSize = rhs._maxTexture3DSize;
582
583    glTexImage3D = rhs.glTexImage3D;
584    glTexSubImage3D = rhs.glTexSubImage3D;
585    glCompressedTexImage3D = rhs.glCompressedTexImage3D;
586    glCompressedTexSubImage3D = rhs.glCompressedTexSubImage3D;
587    glCopyTexSubImage3D = rhs.glCopyTexSubImage3D;
588}
589
590void Texture3D::Extensions::lowestCommonDenominator(const Extensions& rhs)
591{
592    if (!rhs._isTexture3DSupported)                 _isTexture3DSupported = false;
593    if (!rhs._isTexture3DFast)                      _isTexture3DFast = false;
594    if (rhs._maxTexture3DSize<_maxTexture3DSize)    _maxTexture3DSize = rhs._maxTexture3DSize;
595
596    if (!rhs.glTexImage3D)                         glTexImage3D = 0;
597    if (!rhs.glTexSubImage3D)                      glTexSubImage3D = 0;
598    if (!rhs.glCompressedTexImage3D)               glCompressedTexImage3D = 0;
599    if (!rhs.glCompressedTexSubImage3D)            glCompressedTexSubImage3D = 0;
600    if (!rhs.glCopyTexSubImage3D)                  glCopyTexSubImage3D = 0;
601}
602
603void Texture3D::Extensions::setupGLExtensions(unsigned int contextID)
604{
605    _isTexture3DFast = OSG_GL3_FEATURES || isGLExtensionSupported(contextID,"GL_EXT_texture3D");
606
607    if (_isTexture3DFast) _isTexture3DSupported = true;
608    else _isTexture3DSupported = strncmp((const char*)glGetString(GL_VERSION),"1.2",3)>=0;
609   
610    _maxTexture3DSize = 0;
611    glGetIntegerv(GL_MAX_3D_TEXTURE_SIZE, &_maxTexture3DSize);
612
613    setGLExtensionFuncPtr(glTexImage3D,"glTexImage3D","glTexImage3DEXT");
614    setGLExtensionFuncPtr(glTexSubImage3D,"glTexSubImage3D","glTexSubImage3DEXT");
615    setGLExtensionFuncPtr(glCompressedTexImage3D,"glCompressedTexImage3D","glCompressedTexImage3DARB");
616    setGLExtensionFuncPtr(glCompressedTexSubImage3D,"glCompressedTexSubImage3D","glCompressedTexSubImage3DARB");
617    setGLExtensionFuncPtr(glCopyTexSubImage3D,"glCopyTexSubImage3D","glCopyTexSubImage3DEXT");
618
619}
Note: See TracBrowser for help on using the browser.