diff options
Diffstat (limited to 'libraries/irrlicht-1.8/source/Irrlicht/COpenGLTexture.cpp')
-rw-r--r-- | libraries/irrlicht-1.8/source/Irrlicht/COpenGLTexture.cpp | 963 |
1 files changed, 963 insertions, 0 deletions
diff --git a/libraries/irrlicht-1.8/source/Irrlicht/COpenGLTexture.cpp b/libraries/irrlicht-1.8/source/Irrlicht/COpenGLTexture.cpp new file mode 100644 index 0000000..3cc41c0 --- /dev/null +++ b/libraries/irrlicht-1.8/source/Irrlicht/COpenGLTexture.cpp | |||
@@ -0,0 +1,963 @@ | |||
1 | // Copyright (C) 2002-2012 Nikolaus Gebhardt | ||
2 | // This file is part of the "Irrlicht Engine". | ||
3 | // For conditions of distribution and use, see copyright notice in irrlicht.h | ||
4 | |||
5 | #include "IrrCompileConfig.h" | ||
6 | |||
7 | #ifdef _IRR_COMPILE_WITH_OPENGL_ | ||
8 | |||
9 | #include "irrTypes.h" | ||
10 | #include "COpenGLTexture.h" | ||
11 | #include "COpenGLDriver.h" | ||
12 | #include "os.h" | ||
13 | #include "CColorConverter.h" | ||
14 | |||
15 | #include "irrString.h" | ||
16 | |||
17 | namespace irr | ||
18 | { | ||
19 | namespace video | ||
20 | { | ||
21 | |||
22 | //! constructor for usual textures | ||
23 | COpenGLTexture::COpenGLTexture(IImage* origImage, const io::path& name, void* mipmapData, COpenGLDriver* driver) | ||
24 | : ITexture(name), ColorFormat(ECF_A8R8G8B8), Driver(driver), Image(0), MipImage(0), | ||
25 | TextureName(0), InternalFormat(GL_RGBA), PixelFormat(GL_BGRA_EXT), | ||
26 | PixelType(GL_UNSIGNED_BYTE), MipLevelStored(0), MipmapLegacyMode(true), | ||
27 | IsRenderTarget(false), AutomaticMipmapUpdate(false), | ||
28 | ReadOnlyLock(false), KeepImage(true) | ||
29 | { | ||
30 | #ifdef _DEBUG | ||
31 | setDebugName("COpenGLTexture"); | ||
32 | #endif | ||
33 | |||
34 | HasMipMaps = Driver->getTextureCreationFlag(ETCF_CREATE_MIP_MAPS); | ||
35 | getImageValues(origImage); | ||
36 | |||
37 | glGenTextures(1, &TextureName); | ||
38 | |||
39 | if (ImageSize==TextureSize) | ||
40 | { | ||
41 | Image = Driver->createImage(ColorFormat, ImageSize); | ||
42 | origImage->copyTo(Image); | ||
43 | } | ||
44 | else | ||
45 | { | ||
46 | Image = Driver->createImage(ColorFormat, TextureSize); | ||
47 | // scale texture | ||
48 | origImage->copyToScaling(Image); | ||
49 | } | ||
50 | uploadTexture(true, mipmapData); | ||
51 | if (!KeepImage) | ||
52 | { | ||
53 | Image->drop(); | ||
54 | Image=0; | ||
55 | } | ||
56 | } | ||
57 | |||
58 | |||
59 | //! constructor for basic setup (only for derived classes) | ||
60 | COpenGLTexture::COpenGLTexture(const io::path& name, COpenGLDriver* driver) | ||
61 | : ITexture(name), ColorFormat(ECF_A8R8G8B8), Driver(driver), Image(0), MipImage(0), | ||
62 | TextureName(0), InternalFormat(GL_RGBA), PixelFormat(GL_BGRA_EXT), | ||
63 | PixelType(GL_UNSIGNED_BYTE), MipLevelStored(0), HasMipMaps(true), | ||
64 | MipmapLegacyMode(true), IsRenderTarget(false), AutomaticMipmapUpdate(false), | ||
65 | ReadOnlyLock(false), KeepImage(true) | ||
66 | { | ||
67 | #ifdef _DEBUG | ||
68 | setDebugName("COpenGLTexture"); | ||
69 | #endif | ||
70 | } | ||
71 | |||
72 | |||
73 | //! destructor | ||
74 | COpenGLTexture::~COpenGLTexture() | ||
75 | { | ||
76 | if (TextureName) | ||
77 | glDeleteTextures(1, &TextureName); | ||
78 | if (Image) | ||
79 | Image->drop(); | ||
80 | } | ||
81 | |||
82 | |||
83 | //! Choose best matching color format, based on texture creation flags | ||
84 | ECOLOR_FORMAT COpenGLTexture::getBestColorFormat(ECOLOR_FORMAT format) | ||
85 | { | ||
86 | ECOLOR_FORMAT destFormat = ECF_A8R8G8B8; | ||
87 | switch (format) | ||
88 | { | ||
89 | case ECF_A1R5G5B5: | ||
90 | if (!Driver->getTextureCreationFlag(ETCF_ALWAYS_32_BIT)) | ||
91 | destFormat = ECF_A1R5G5B5; | ||
92 | break; | ||
93 | case ECF_R5G6B5: | ||
94 | if (!Driver->getTextureCreationFlag(ETCF_ALWAYS_32_BIT)) | ||
95 | destFormat = ECF_A1R5G5B5; | ||
96 | break; | ||
97 | case ECF_A8R8G8B8: | ||
98 | if (Driver->getTextureCreationFlag(ETCF_ALWAYS_16_BIT) || | ||
99 | Driver->getTextureCreationFlag(ETCF_OPTIMIZED_FOR_SPEED)) | ||
100 | destFormat = ECF_A1R5G5B5; | ||
101 | break; | ||
102 | case ECF_R8G8B8: | ||
103 | if (Driver->getTextureCreationFlag(ETCF_ALWAYS_16_BIT) || | ||
104 | Driver->getTextureCreationFlag(ETCF_OPTIMIZED_FOR_SPEED)) | ||
105 | destFormat = ECF_A1R5G5B5; | ||
106 | default: | ||
107 | break; | ||
108 | } | ||
109 | if (Driver->getTextureCreationFlag(ETCF_NO_ALPHA_CHANNEL)) | ||
110 | { | ||
111 | switch (destFormat) | ||
112 | { | ||
113 | case ECF_A1R5G5B5: | ||
114 | destFormat = ECF_R5G6B5; | ||
115 | break; | ||
116 | case ECF_A8R8G8B8: | ||
117 | destFormat = ECF_R8G8B8; | ||
118 | break; | ||
119 | default: | ||
120 | break; | ||
121 | } | ||
122 | } | ||
123 | return destFormat; | ||
124 | } | ||
125 | |||
126 | |||
127 | //! Get opengl values for the GPU texture storage | ||
128 | GLint COpenGLTexture::getOpenGLFormatAndParametersFromColorFormat(ECOLOR_FORMAT format, | ||
129 | GLint& filtering, | ||
130 | GLenum& colorformat, | ||
131 | GLenum& type) | ||
132 | { | ||
133 | // default | ||
134 | filtering = GL_LINEAR; | ||
135 | colorformat = GL_RGBA; | ||
136 | type = GL_UNSIGNED_BYTE; | ||
137 | GLenum internalformat = GL_RGBA; | ||
138 | |||
139 | switch(format) | ||
140 | { | ||
141 | case ECF_A1R5G5B5: | ||
142 | colorformat=GL_BGRA_EXT; | ||
143 | type=GL_UNSIGNED_SHORT_1_5_5_5_REV; | ||
144 | internalformat = GL_RGBA; | ||
145 | break; | ||
146 | case ECF_R5G6B5: | ||
147 | colorformat=GL_RGB; | ||
148 | type=GL_UNSIGNED_SHORT_5_6_5; | ||
149 | internalformat = GL_RGB; | ||
150 | break; | ||
151 | case ECF_R8G8B8: | ||
152 | colorformat=GL_BGR; | ||
153 | type=GL_UNSIGNED_BYTE; | ||
154 | internalformat = GL_RGB; | ||
155 | break; | ||
156 | case ECF_A8R8G8B8: | ||
157 | colorformat=GL_BGRA_EXT; | ||
158 | if (Driver->Version > 101) | ||
159 | type=GL_UNSIGNED_INT_8_8_8_8_REV; | ||
160 | internalformat = GL_RGBA; | ||
161 | break; | ||
162 | // Floating Point texture formats. Thanks to Patryk "Nadro" Nadrowski. | ||
163 | case ECF_R16F: | ||
164 | { | ||
165 | #ifdef GL_ARB_texture_rg | ||
166 | filtering = GL_NEAREST; | ||
167 | colorformat = GL_RED; | ||
168 | type = GL_FLOAT; | ||
169 | |||
170 | internalformat = GL_R16F; | ||
171 | #else | ||
172 | ColorFormat = ECF_A8R8G8B8; | ||
173 | internalformat = GL_RGB8; | ||
174 | #endif | ||
175 | } | ||
176 | break; | ||
177 | case ECF_G16R16F: | ||
178 | { | ||
179 | #ifdef GL_ARB_texture_rg | ||
180 | filtering = GL_NEAREST; | ||
181 | colorformat = GL_RG; | ||
182 | type = GL_FLOAT; | ||
183 | |||
184 | internalformat = GL_RG16F; | ||
185 | #else | ||
186 | ColorFormat = ECF_A8R8G8B8; | ||
187 | internalformat = GL_RGB8; | ||
188 | #endif | ||
189 | } | ||
190 | break; | ||
191 | case ECF_A16B16G16R16F: | ||
192 | { | ||
193 | #ifdef GL_ARB_texture_rg | ||
194 | filtering = GL_NEAREST; | ||
195 | colorformat = GL_RGBA; | ||
196 | type = GL_FLOAT; | ||
197 | |||
198 | internalformat = GL_RGBA16F_ARB; | ||
199 | #else | ||
200 | ColorFormat = ECF_A8R8G8B8; | ||
201 | internalformat = GL_RGBA8; | ||
202 | #endif | ||
203 | } | ||
204 | break; | ||
205 | case ECF_R32F: | ||
206 | { | ||
207 | #ifdef GL_ARB_texture_rg | ||
208 | filtering = GL_NEAREST; | ||
209 | colorformat = GL_RED; | ||
210 | type = GL_FLOAT; | ||
211 | |||
212 | internalformat = GL_R32F; | ||
213 | #else | ||
214 | ColorFormat = ECF_A8R8G8B8; | ||
215 | internalformat = GL_RGB8; | ||
216 | #endif | ||
217 | } | ||
218 | break; | ||
219 | case ECF_G32R32F: | ||
220 | { | ||
221 | #ifdef GL_ARB_texture_rg | ||
222 | filtering = GL_NEAREST; | ||
223 | colorformat = GL_RG; | ||
224 | type = GL_FLOAT; | ||
225 | |||
226 | internalformat = GL_RG32F; | ||
227 | #else | ||
228 | ColorFormat = ECF_A8R8G8B8; | ||
229 | internalformat = GL_RGB8; | ||
230 | #endif | ||
231 | } | ||
232 | break; | ||
233 | case ECF_A32B32G32R32F: | ||
234 | { | ||
235 | #ifdef GL_ARB_texture_float | ||
236 | filtering = GL_NEAREST; | ||
237 | colorformat = GL_RGBA; | ||
238 | type = GL_FLOAT; | ||
239 | |||
240 | internalformat = GL_RGBA32F_ARB; | ||
241 | #else | ||
242 | ColorFormat = ECF_A8R8G8B8; | ||
243 | internalformat = GL_RGBA8; | ||
244 | #endif | ||
245 | } | ||
246 | break; | ||
247 | default: | ||
248 | { | ||
249 | os::Printer::log("Unsupported texture format", ELL_ERROR); | ||
250 | internalformat = GL_RGBA8; | ||
251 | } | ||
252 | } | ||
253 | #if defined(GL_ARB_framebuffer_sRGB) || defined(GL_EXT_framebuffer_sRGB) | ||
254 | if (Driver->Params.HandleSRGB) | ||
255 | { | ||
256 | if (internalformat==GL_RGBA) | ||
257 | internalformat=GL_SRGB_ALPHA_EXT; | ||
258 | else if (internalformat==GL_RGB) | ||
259 | internalformat=GL_SRGB_EXT; | ||
260 | } | ||
261 | #endif | ||
262 | return internalformat; | ||
263 | } | ||
264 | |||
265 | |||
266 | // prepare values ImageSize, TextureSize, and ColorFormat based on image | ||
267 | void COpenGLTexture::getImageValues(IImage* image) | ||
268 | { | ||
269 | if (!image) | ||
270 | { | ||
271 | os::Printer::log("No image for OpenGL texture.", ELL_ERROR); | ||
272 | return; | ||
273 | } | ||
274 | |||
275 | ImageSize = image->getDimension(); | ||
276 | |||
277 | if ( !ImageSize.Width || !ImageSize.Height) | ||
278 | { | ||
279 | os::Printer::log("Invalid size of image for OpenGL Texture.", ELL_ERROR); | ||
280 | return; | ||
281 | } | ||
282 | |||
283 | const f32 ratio = (f32)ImageSize.Width/(f32)ImageSize.Height; | ||
284 | if ((ImageSize.Width>Driver->MaxTextureSize) && (ratio >= 1.0f)) | ||
285 | { | ||
286 | ImageSize.Width = Driver->MaxTextureSize; | ||
287 | ImageSize.Height = (u32)(Driver->MaxTextureSize/ratio); | ||
288 | } | ||
289 | else if (ImageSize.Height>Driver->MaxTextureSize) | ||
290 | { | ||
291 | ImageSize.Height = Driver->MaxTextureSize; | ||
292 | ImageSize.Width = (u32)(Driver->MaxTextureSize*ratio); | ||
293 | } | ||
294 | TextureSize=ImageSize.getOptimalSize(!Driver->queryFeature(EVDF_TEXTURE_NPOT)); | ||
295 | |||
296 | ColorFormat = getBestColorFormat(image->getColorFormat()); | ||
297 | } | ||
298 | |||
299 | |||
300 | //! copies the the texture into an open gl texture. | ||
301 | void COpenGLTexture::uploadTexture(bool newTexture, void* mipmapData, u32 level) | ||
302 | { | ||
303 | // check which image needs to be uploaded | ||
304 | IImage* image = level?MipImage:Image; | ||
305 | if (!image) | ||
306 | { | ||
307 | os::Printer::log("No image for OpenGL texture to upload", ELL_ERROR); | ||
308 | return; | ||
309 | } | ||
310 | |||
311 | // get correct opengl color data values | ||
312 | GLenum oldInternalFormat = InternalFormat; | ||
313 | GLint filtering; | ||
314 | InternalFormat = getOpenGLFormatAndParametersFromColorFormat(ColorFormat, filtering, PixelFormat, PixelType); | ||
315 | // make sure we don't change the internal format of existing images | ||
316 | if (!newTexture) | ||
317 | InternalFormat=oldInternalFormat; | ||
318 | |||
319 | Driver->setActiveTexture(0, this); | ||
320 | if (Driver->testGLError()) | ||
321 | os::Printer::log("Could not bind Texture", ELL_ERROR); | ||
322 | |||
323 | // mipmap handling for main texture | ||
324 | if (!level && newTexture) | ||
325 | { | ||
326 | #ifndef DISABLE_MIPMAPPING | ||
327 | #ifdef GL_SGIS_generate_mipmap | ||
328 | // auto generate if possible and no mipmap data is given | ||
329 | if (HasMipMaps && !mipmapData && Driver->queryFeature(EVDF_MIP_MAP_AUTO_UPDATE)) | ||
330 | { | ||
331 | if (Driver->getTextureCreationFlag(ETCF_OPTIMIZED_FOR_SPEED)) | ||
332 | glHint(GL_GENERATE_MIPMAP_HINT_SGIS, GL_FASTEST); | ||
333 | else if (Driver->getTextureCreationFlag(ETCF_OPTIMIZED_FOR_QUALITY)) | ||
334 | glHint(GL_GENERATE_MIPMAP_HINT_SGIS, GL_NICEST); | ||
335 | else | ||
336 | glHint(GL_GENERATE_MIPMAP_HINT_SGIS, GL_DONT_CARE); | ||
337 | |||
338 | AutomaticMipmapUpdate=true; | ||
339 | |||
340 | if (!Driver->queryFeature(EVDF_FRAMEBUFFER_OBJECT)) | ||
341 | { | ||
342 | glTexParameteri( GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE ); | ||
343 | MipmapLegacyMode=true; | ||
344 | } | ||
345 | else | ||
346 | MipmapLegacyMode=false; | ||
347 | } | ||
348 | else | ||
349 | #endif | ||
350 | { | ||
351 | // Either generate manually due to missing capability | ||
352 | // or use predefined mipmap data | ||
353 | AutomaticMipmapUpdate=false; | ||
354 | regenerateMipMapLevels(mipmapData); | ||
355 | } | ||
356 | if (HasMipMaps) // might have changed in regenerateMipMapLevels | ||
357 | { | ||
358 | // enable bilinear mipmap filter | ||
359 | glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST ); | ||
360 | glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | ||
361 | } | ||
362 | else | ||
363 | #else | ||
364 | HasMipMaps=false; | ||
365 | os::Printer::log("Did not create OpenGL texture mip maps.", ELL_INFORMATION); | ||
366 | #endif | ||
367 | { | ||
368 | // enable bilinear filter without mipmaps | ||
369 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | ||
370 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | ||
371 | } | ||
372 | } | ||
373 | |||
374 | // now get image data and upload to GPU | ||
375 | void* source = image->lock(); | ||
376 | if (newTexture) | ||
377 | glTexImage2D(GL_TEXTURE_2D, level, InternalFormat, image->getDimension().Width, | ||
378 | image->getDimension().Height, 0, PixelFormat, PixelType, source); | ||
379 | else | ||
380 | glTexSubImage2D(GL_TEXTURE_2D, level, 0, 0, image->getDimension().Width, | ||
381 | image->getDimension().Height, PixelFormat, PixelType, source); | ||
382 | image->unlock(); | ||
383 | |||
384 | if (!MipmapLegacyMode && AutomaticMipmapUpdate) | ||
385 | { | ||
386 | glEnable(GL_TEXTURE_2D); | ||
387 | Driver->extGlGenerateMipmap(GL_TEXTURE_2D); | ||
388 | } | ||
389 | |||
390 | if (Driver->testGLError()) | ||
391 | os::Printer::log("Could not glTexImage2D", ELL_ERROR); | ||
392 | } | ||
393 | |||
394 | |||
395 | //! lock function | ||
396 | void* COpenGLTexture::lock(E_TEXTURE_LOCK_MODE mode, u32 mipmapLevel) | ||
397 | { | ||
398 | // store info about which image is locked | ||
399 | IImage* image = (mipmapLevel==0)?Image:MipImage; | ||
400 | ReadOnlyLock |= (mode==ETLM_READ_ONLY); | ||
401 | MipLevelStored = mipmapLevel; | ||
402 | if (!ReadOnlyLock && mipmapLevel) | ||
403 | { | ||
404 | #ifdef GL_SGIS_generate_mipmap | ||
405 | if (Driver->queryFeature(EVDF_MIP_MAP_AUTO_UPDATE)) | ||
406 | { | ||
407 | // do not automatically generate and update mipmaps | ||
408 | glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_FALSE); | ||
409 | } | ||
410 | #endif | ||
411 | AutomaticMipmapUpdate=false; | ||
412 | } | ||
413 | |||
414 | // if data not available or might have changed on GPU download it | ||
415 | if (!image || IsRenderTarget) | ||
416 | { | ||
417 | // prepare the data storage if necessary | ||
418 | if (!image) | ||
419 | { | ||
420 | if (mipmapLevel) | ||
421 | { | ||
422 | u32 i=0; | ||
423 | u32 width = TextureSize.Width; | ||
424 | u32 height = TextureSize.Height; | ||
425 | do | ||
426 | { | ||
427 | if (width>1) | ||
428 | width>>=1; | ||
429 | if (height>1) | ||
430 | height>>=1; | ||
431 | ++i; | ||
432 | } | ||
433 | while (i != mipmapLevel); | ||
434 | MipImage = image = Driver->createImage(ECF_A8R8G8B8, core::dimension2du(width,height)); | ||
435 | } | ||
436 | else | ||
437 | Image = image = Driver->createImage(ECF_A8R8G8B8, ImageSize); | ||
438 | ColorFormat = ECF_A8R8G8B8; | ||
439 | } | ||
440 | if (!image) | ||
441 | return 0; | ||
442 | |||
443 | if (mode != ETLM_WRITE_ONLY) | ||
444 | { | ||
445 | u8* pixels = static_cast<u8*>(image->lock()); | ||
446 | if (!pixels) | ||
447 | return 0; | ||
448 | |||
449 | // we need to keep the correct texture bound later on | ||
450 | GLint tmpTexture; | ||
451 | glGetIntegerv(GL_TEXTURE_BINDING_2D, &tmpTexture); | ||
452 | glBindTexture(GL_TEXTURE_2D, TextureName); | ||
453 | |||
454 | // we need to flip textures vertical | ||
455 | // however, it seems that this does not hold for mipmap | ||
456 | // textures, for unknown reasons. | ||
457 | |||
458 | // allows to read pixels in top-to-bottom order | ||
459 | #ifdef GL_MESA_pack_invert | ||
460 | if (!mipmapLevel && Driver->queryOpenGLFeature(COpenGLExtensionHandler::IRR_MESA_pack_invert)) | ||
461 | glPixelStorei(GL_PACK_INVERT_MESA, GL_TRUE); | ||
462 | #endif | ||
463 | |||
464 | // download GPU data as ARGB8 to pixels; | ||
465 | glGetTexImage(GL_TEXTURE_2D, mipmapLevel, GL_BGRA_EXT, GL_UNSIGNED_BYTE, pixels); | ||
466 | |||
467 | if (!mipmapLevel) | ||
468 | { | ||
469 | #ifdef GL_MESA_pack_invert | ||
470 | if (Driver->queryOpenGLFeature(COpenGLExtensionHandler::IRR_MESA_pack_invert)) | ||
471 | glPixelStorei(GL_PACK_INVERT_MESA, GL_FALSE); | ||
472 | else | ||
473 | #endif | ||
474 | { | ||
475 | // opengl images are horizontally flipped, so we have to fix that here. | ||
476 | const s32 pitch=image->getPitch(); | ||
477 | u8* p2 = pixels + (image->getDimension().Height - 1) * pitch; | ||
478 | u8* tmpBuffer = new u8[pitch]; | ||
479 | for (u32 i=0; i < image->getDimension().Height; i += 2) | ||
480 | { | ||
481 | memcpy(tmpBuffer, pixels, pitch); | ||
482 | memcpy(pixels, p2, pitch); | ||
483 | memcpy(p2, tmpBuffer, pitch); | ||
484 | pixels += pitch; | ||
485 | p2 -= pitch; | ||
486 | } | ||
487 | delete [] tmpBuffer; | ||
488 | } | ||
489 | } | ||
490 | image->unlock(); | ||
491 | |||
492 | //reset old bound texture | ||
493 | glBindTexture(GL_TEXTURE_2D, tmpTexture); | ||
494 | } | ||
495 | } | ||
496 | return image->lock(); | ||
497 | } | ||
498 | |||
499 | |||
500 | //! unlock function | ||
501 | void COpenGLTexture::unlock() | ||
502 | { | ||
503 | // test if miplevel or main texture was locked | ||
504 | IImage* image = MipImage?MipImage:Image; | ||
505 | if (!image) | ||
506 | return; | ||
507 | // unlock image to see changes | ||
508 | image->unlock(); | ||
509 | // copy texture data to GPU | ||
510 | if (!ReadOnlyLock) | ||
511 | uploadTexture(false, 0, MipLevelStored); | ||
512 | ReadOnlyLock = false; | ||
513 | // cleanup local image | ||
514 | if (MipImage) | ||
515 | { | ||
516 | MipImage->drop(); | ||
517 | MipImage=0; | ||
518 | } | ||
519 | else if (!KeepImage) | ||
520 | { | ||
521 | Image->drop(); | ||
522 | Image=0; | ||
523 | } | ||
524 | // update information | ||
525 | if (Image) | ||
526 | ColorFormat=Image->getColorFormat(); | ||
527 | else | ||
528 | ColorFormat=ECF_A8R8G8B8; | ||
529 | } | ||
530 | |||
531 | |||
532 | //! Returns size of the original image. | ||
533 | const core::dimension2d<u32>& COpenGLTexture::getOriginalSize() const | ||
534 | { | ||
535 | return ImageSize; | ||
536 | } | ||
537 | |||
538 | |||
539 | //! Returns size of the texture. | ||
540 | const core::dimension2d<u32>& COpenGLTexture::getSize() const | ||
541 | { | ||
542 | return TextureSize; | ||
543 | } | ||
544 | |||
545 | |||
546 | //! returns driver type of texture, i.e. the driver, which created the texture | ||
547 | E_DRIVER_TYPE COpenGLTexture::getDriverType() const | ||
548 | { | ||
549 | return EDT_OPENGL; | ||
550 | } | ||
551 | |||
552 | |||
553 | //! returns color format of texture | ||
554 | ECOLOR_FORMAT COpenGLTexture::getColorFormat() const | ||
555 | { | ||
556 | return ColorFormat; | ||
557 | } | ||
558 | |||
559 | |||
560 | //! returns pitch of texture (in bytes) | ||
561 | u32 COpenGLTexture::getPitch() const | ||
562 | { | ||
563 | if (Image) | ||
564 | return Image->getPitch(); | ||
565 | else | ||
566 | return 0; | ||
567 | } | ||
568 | |||
569 | |||
570 | //! return open gl texture name | ||
571 | GLuint COpenGLTexture::getOpenGLTextureName() const | ||
572 | { | ||
573 | return TextureName; | ||
574 | } | ||
575 | |||
576 | |||
577 | //! Returns whether this texture has mipmaps | ||
578 | bool COpenGLTexture::hasMipMaps() const | ||
579 | { | ||
580 | return HasMipMaps; | ||
581 | } | ||
582 | |||
583 | |||
584 | //! Regenerates the mip map levels of the texture. Useful after locking and | ||
585 | //! modifying the texture | ||
586 | void COpenGLTexture::regenerateMipMapLevels(void* mipmapData) | ||
587 | { | ||
588 | if (AutomaticMipmapUpdate || !HasMipMaps || !Image) | ||
589 | return; | ||
590 | if ((Image->getDimension().Width==1) && (Image->getDimension().Height==1)) | ||
591 | return; | ||
592 | |||
593 | // Manually create mipmaps or use prepared version | ||
594 | u32 width=Image->getDimension().Width; | ||
595 | u32 height=Image->getDimension().Height; | ||
596 | u32 i=0; | ||
597 | u8* target = static_cast<u8*>(mipmapData); | ||
598 | do | ||
599 | { | ||
600 | if (width>1) | ||
601 | width>>=1; | ||
602 | if (height>1) | ||
603 | height>>=1; | ||
604 | ++i; | ||
605 | if (!target) | ||
606 | target = new u8[width*height*Image->getBytesPerPixel()]; | ||
607 | // create scaled version if no mipdata available | ||
608 | if (!mipmapData) | ||
609 | Image->copyToScaling(target, width, height, Image->getColorFormat()); | ||
610 | glTexImage2D(GL_TEXTURE_2D, i, InternalFormat, width, height, | ||
611 | 0, PixelFormat, PixelType, target); | ||
612 | // get next prepared mipmap data if available | ||
613 | if (mipmapData) | ||
614 | { | ||
615 | mipmapData = static_cast<u8*>(mipmapData)+width*height*Image->getBytesPerPixel(); | ||
616 | target = static_cast<u8*>(mipmapData); | ||
617 | } | ||
618 | } | ||
619 | while (width!=1 || height!=1); | ||
620 | // cleanup | ||
621 | if (!mipmapData) | ||
622 | delete [] target; | ||
623 | } | ||
624 | |||
625 | |||
626 | bool COpenGLTexture::isRenderTarget() const | ||
627 | { | ||
628 | return IsRenderTarget; | ||
629 | } | ||
630 | |||
631 | |||
632 | void COpenGLTexture::setIsRenderTarget(bool isTarget) | ||
633 | { | ||
634 | IsRenderTarget = isTarget; | ||
635 | } | ||
636 | |||
637 | |||
638 | bool COpenGLTexture::isFrameBufferObject() const | ||
639 | { | ||
640 | return false; | ||
641 | } | ||
642 | |||
643 | |||
644 | //! Bind Render Target Texture | ||
645 | void COpenGLTexture::bindRTT() | ||
646 | { | ||
647 | } | ||
648 | |||
649 | |||
650 | //! Unbind Render Target Texture | ||
651 | void COpenGLTexture::unbindRTT() | ||
652 | { | ||
653 | Driver->setActiveTexture(0, this); | ||
654 | |||
655 | // Copy Our ViewPort To The Texture | ||
656 | glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, getSize().Width, getSize().Height); | ||
657 | } | ||
658 | |||
659 | |||
660 | /* FBO Textures */ | ||
661 | |||
662 | // helper function for render to texture | ||
663 | static bool checkFBOStatus(COpenGLDriver* Driver); | ||
664 | |||
665 | //! RTT ColorFrameBuffer constructor | ||
666 | COpenGLFBOTexture::COpenGLFBOTexture(const core::dimension2d<u32>& size, | ||
667 | const io::path& name, COpenGLDriver* driver, | ||
668 | ECOLOR_FORMAT format) | ||
669 | : COpenGLTexture(name, driver), DepthTexture(0), ColorFrameBuffer(0) | ||
670 | { | ||
671 | #ifdef _DEBUG | ||
672 | setDebugName("COpenGLTexture_FBO"); | ||
673 | #endif | ||
674 | |||
675 | ImageSize = size; | ||
676 | TextureSize = size; | ||
677 | |||
678 | if (ECF_UNKNOWN == format) | ||
679 | format = getBestColorFormat(driver->getColorFormat()); | ||
680 | |||
681 | ColorFormat = format; | ||
682 | |||
683 | GLint FilteringType; | ||
684 | InternalFormat = getOpenGLFormatAndParametersFromColorFormat(format, FilteringType, PixelFormat, PixelType); | ||
685 | |||
686 | HasMipMaps = false; | ||
687 | IsRenderTarget = true; | ||
688 | |||
689 | #ifdef GL_EXT_framebuffer_object | ||
690 | // generate frame buffer | ||
691 | Driver->extGlGenFramebuffers(1, &ColorFrameBuffer); | ||
692 | bindRTT(); | ||
693 | |||
694 | // generate color texture | ||
695 | glGenTextures(1, &TextureName); | ||
696 | Driver->setActiveTexture(0, this); | ||
697 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, FilteringType); | ||
698 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | ||
699 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | ||
700 | glTexImage2D(GL_TEXTURE_2D, 0, InternalFormat, ImageSize.Width, | ||
701 | ImageSize.Height, 0, PixelFormat, PixelType, 0); | ||
702 | #ifdef _DEBUG | ||
703 | driver->testGLError(); | ||
704 | #endif | ||
705 | |||
706 | // attach color texture to frame buffer | ||
707 | Driver->extGlFramebufferTexture2D(GL_FRAMEBUFFER_EXT, | ||
708 | GL_COLOR_ATTACHMENT0_EXT, | ||
709 | GL_TEXTURE_2D, | ||
710 | TextureName, | ||
711 | 0); | ||
712 | #ifdef _DEBUG | ||
713 | checkFBOStatus(Driver); | ||
714 | #endif | ||
715 | |||
716 | #endif | ||
717 | unbindRTT(); | ||
718 | } | ||
719 | |||
720 | |||
721 | //! destructor | ||
722 | COpenGLFBOTexture::~COpenGLFBOTexture() | ||
723 | { | ||
724 | if (DepthTexture) | ||
725 | if (DepthTexture->drop()) | ||
726 | Driver->removeDepthTexture(DepthTexture); | ||
727 | if (ColorFrameBuffer) | ||
728 | Driver->extGlDeleteFramebuffers(1, &ColorFrameBuffer); | ||
729 | } | ||
730 | |||
731 | |||
732 | bool COpenGLFBOTexture::isFrameBufferObject() const | ||
733 | { | ||
734 | return true; | ||
735 | } | ||
736 | |||
737 | |||
738 | //! Bind Render Target Texture | ||
739 | void COpenGLFBOTexture::bindRTT() | ||
740 | { | ||
741 | #ifdef GL_EXT_framebuffer_object | ||
742 | if (ColorFrameBuffer != 0) | ||
743 | Driver->extGlBindFramebuffer(GL_FRAMEBUFFER_EXT, ColorFrameBuffer); | ||
744 | glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT); | ||
745 | #endif | ||
746 | } | ||
747 | |||
748 | |||
749 | //! Unbind Render Target Texture | ||
750 | void COpenGLFBOTexture::unbindRTT() | ||
751 | { | ||
752 | #ifdef GL_EXT_framebuffer_object | ||
753 | if (ColorFrameBuffer != 0) | ||
754 | Driver->extGlBindFramebuffer(GL_FRAMEBUFFER_EXT, 0); | ||
755 | #endif | ||
756 | } | ||
757 | |||
758 | |||
759 | /* FBO Depth Textures */ | ||
760 | |||
761 | //! RTT DepthBuffer constructor | ||
762 | COpenGLFBODepthTexture::COpenGLFBODepthTexture( | ||
763 | const core::dimension2d<u32>& size, | ||
764 | const io::path& name, | ||
765 | COpenGLDriver* driver, | ||
766 | bool useStencil) | ||
767 | : COpenGLTexture(name, driver), DepthRenderBuffer(0), | ||
768 | StencilRenderBuffer(0), UseStencil(useStencil) | ||
769 | { | ||
770 | #ifdef _DEBUG | ||
771 | setDebugName("COpenGLTextureFBO_Depth"); | ||
772 | #endif | ||
773 | |||
774 | ImageSize = size; | ||
775 | TextureSize = size; | ||
776 | InternalFormat = GL_RGBA; | ||
777 | PixelFormat = GL_RGBA; | ||
778 | PixelType = GL_UNSIGNED_BYTE; | ||
779 | HasMipMaps = false; | ||
780 | |||
781 | if (useStencil) | ||
782 | { | ||
783 | glGenTextures(1, &DepthRenderBuffer); | ||
784 | glBindTexture(GL_TEXTURE_2D, DepthRenderBuffer); | ||
785 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | ||
786 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | ||
787 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | ||
788 | #ifdef GL_EXT_packed_depth_stencil | ||
789 | if (Driver->queryOpenGLFeature(COpenGLExtensionHandler::IRR_EXT_packed_depth_stencil)) | ||
790 | { | ||
791 | // generate packed depth stencil texture | ||
792 | glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_STENCIL_EXT, ImageSize.Width, | ||
793 | ImageSize.Height, 0, GL_DEPTH_STENCIL_EXT, GL_UNSIGNED_INT_24_8_EXT, 0); | ||
794 | StencilRenderBuffer = DepthRenderBuffer; // stencil is packed with depth | ||
795 | } | ||
796 | else // generate separate stencil and depth textures | ||
797 | #endif | ||
798 | { | ||
799 | // generate depth texture | ||
800 | glTexImage2D(GL_TEXTURE_2D, 0, Driver->getZBufferBits(), ImageSize.Width, | ||
801 | ImageSize.Height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, 0); | ||
802 | |||
803 | // generate stencil texture | ||
804 | glGenTextures(1, &StencilRenderBuffer); | ||
805 | glBindTexture(GL_TEXTURE_2D, StencilRenderBuffer); | ||
806 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | ||
807 | glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | ||
808 | glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | ||
809 | glTexImage2D(GL_TEXTURE_2D, 0, GL_STENCIL_INDEX, ImageSize.Width, | ||
810 | ImageSize.Height, 0, GL_STENCIL_INDEX, GL_UNSIGNED_BYTE, 0); | ||
811 | } | ||
812 | } | ||
813 | #ifdef GL_EXT_framebuffer_object | ||
814 | else | ||
815 | { | ||
816 | // generate depth buffer | ||
817 | Driver->extGlGenRenderbuffers(1, &DepthRenderBuffer); | ||
818 | Driver->extGlBindRenderbuffer(GL_RENDERBUFFER_EXT, DepthRenderBuffer); | ||
819 | Driver->extGlRenderbufferStorage(GL_RENDERBUFFER_EXT, | ||
820 | Driver->getZBufferBits(), ImageSize.Width, | ||
821 | ImageSize.Height); | ||
822 | } | ||
823 | #endif | ||
824 | } | ||
825 | |||
826 | |||
827 | //! destructor | ||
828 | COpenGLFBODepthTexture::~COpenGLFBODepthTexture() | ||
829 | { | ||
830 | if (DepthRenderBuffer && UseStencil) | ||
831 | glDeleteTextures(1, &DepthRenderBuffer); | ||
832 | else | ||
833 | Driver->extGlDeleteRenderbuffers(1, &DepthRenderBuffer); | ||
834 | if (StencilRenderBuffer && StencilRenderBuffer != DepthRenderBuffer) | ||
835 | glDeleteTextures(1, &StencilRenderBuffer); | ||
836 | } | ||
837 | |||
838 | |||
839 | //combine depth texture and rtt | ||
840 | bool COpenGLFBODepthTexture::attach(ITexture* renderTex) | ||
841 | { | ||
842 | if (!renderTex) | ||
843 | return false; | ||
844 | video::COpenGLFBOTexture* rtt = static_cast<video::COpenGLFBOTexture*>(renderTex); | ||
845 | rtt->bindRTT(); | ||
846 | #ifdef GL_EXT_framebuffer_object | ||
847 | if (UseStencil) | ||
848 | { | ||
849 | // attach stencil texture to stencil buffer | ||
850 | Driver->extGlFramebufferTexture2D(GL_FRAMEBUFFER_EXT, | ||
851 | GL_STENCIL_ATTACHMENT_EXT, | ||
852 | GL_TEXTURE_2D, | ||
853 | StencilRenderBuffer, | ||
854 | 0); | ||
855 | |||
856 | // attach depth texture to depth buffer | ||
857 | Driver->extGlFramebufferTexture2D(GL_FRAMEBUFFER_EXT, | ||
858 | GL_DEPTH_ATTACHMENT_EXT, | ||
859 | GL_TEXTURE_2D, | ||
860 | DepthRenderBuffer, | ||
861 | 0); | ||
862 | } | ||
863 | else | ||
864 | { | ||
865 | // attach depth renderbuffer to depth buffer | ||
866 | Driver->extGlFramebufferRenderbuffer(GL_FRAMEBUFFER_EXT, | ||
867 | GL_DEPTH_ATTACHMENT_EXT, | ||
868 | GL_RENDERBUFFER_EXT, | ||
869 | DepthRenderBuffer); | ||
870 | } | ||
871 | #endif | ||
872 | // check the status | ||
873 | if (!checkFBOStatus(Driver)) | ||
874 | { | ||
875 | os::Printer::log("FBO incomplete"); | ||
876 | return false; | ||
877 | } | ||
878 | rtt->DepthTexture=this; | ||
879 | grab(); // grab the depth buffer, not the RTT | ||
880 | rtt->unbindRTT(); | ||
881 | return true; | ||
882 | } | ||
883 | |||
884 | |||
885 | //! Bind Render Target Texture | ||
886 | void COpenGLFBODepthTexture::bindRTT() | ||
887 | { | ||
888 | } | ||
889 | |||
890 | |||
891 | //! Unbind Render Target Texture | ||
892 | void COpenGLFBODepthTexture::unbindRTT() | ||
893 | { | ||
894 | } | ||
895 | |||
896 | |||
897 | bool checkFBOStatus(COpenGLDriver* Driver) | ||
898 | { | ||
899 | #ifdef GL_EXT_framebuffer_object | ||
900 | GLenum status = Driver->extGlCheckFramebufferStatus(GL_FRAMEBUFFER_EXT); | ||
901 | |||
902 | switch (status) | ||
903 | { | ||
904 | //Our FBO is perfect, return true | ||
905 | case GL_FRAMEBUFFER_COMPLETE_EXT: | ||
906 | return true; | ||
907 | |||
908 | case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT: | ||
909 | os::Printer::log("FBO has invalid read buffer", ELL_ERROR); | ||
910 | break; | ||
911 | |||
912 | case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT: | ||
913 | os::Printer::log("FBO has invalid draw buffer", ELL_ERROR); | ||
914 | break; | ||
915 | |||
916 | case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT: | ||
917 | os::Printer::log("FBO has one or several incomplete image attachments", ELL_ERROR); | ||
918 | break; | ||
919 | |||
920 | case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT: | ||
921 | os::Printer::log("FBO has one or several image attachments with different internal formats", ELL_ERROR); | ||
922 | break; | ||
923 | |||
924 | case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT: | ||
925 | os::Printer::log("FBO has one or several image attachments with different dimensions", ELL_ERROR); | ||
926 | break; | ||
927 | |||
928 | // not part of fbo_object anymore, but won't harm as it is just a return value | ||
929 | #ifdef GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT_EXT | ||
930 | case GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT_EXT: | ||
931 | os::Printer::log("FBO has a duplicate image attachment", ELL_ERROR); | ||
932 | break; | ||
933 | #endif | ||
934 | |||
935 | case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT: | ||
936 | os::Printer::log("FBO missing an image attachment", ELL_ERROR); | ||
937 | break; | ||
938 | |||
939 | #ifdef GL_EXT_framebuffer_multisample | ||
940 | case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT: | ||
941 | os::Printer::log("FBO wrong multisample setup", ELL_ERROR); | ||
942 | break; | ||
943 | #endif | ||
944 | |||
945 | case GL_FRAMEBUFFER_UNSUPPORTED_EXT: | ||
946 | os::Printer::log("FBO format unsupported", ELL_ERROR); | ||
947 | break; | ||
948 | |||
949 | default: | ||
950 | break; | ||
951 | } | ||
952 | #endif | ||
953 | os::Printer::log("FBO error", ELL_ERROR); | ||
954 | // _IRR_DEBUG_BREAK_IF(true); | ||
955 | return false; | ||
956 | } | ||
957 | |||
958 | |||
959 | } // end namespace video | ||
960 | } // end namespace irr | ||
961 | |||
962 | #endif // _IRR_COMPILE_WITH_OPENGL_ | ||
963 | |||