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