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