blob: d727128eb5dedf3cc3f167dd1d725996827cce5f [file] [log] [blame]
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001//
2// Copyright (c) 2002-2010 The ANGLE Project Authors. All rights reserved.
3// Use of this source code is governed by a BSD-style license that can be
4// found in the LICENSE file.
5//
6
7// Texture.cpp: Implements the gl::Texture class and its derived classes
8// Texture2D and TextureCubeMap. Implements GL texture objects and related
9// functionality. [OpenGL ES 2.0.24] section 3.7 page 63.
10
11#include "Texture.h"
12
daniel@transgaming.com16973022010-03-11 19:22:19 +000013#include <algorithm>
14
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000015#include "main.h"
16#include "mathutil.h"
17#include "debug.h"
18
19namespace gl
20{
daniel@transgaming.com842f7a42010-03-21 04:31:03 +000021
22Texture::Image::Image()
23 : dirty(false), surface(NULL)
24{
25}
26
27Texture::Image::~Image()
28{
29 if (surface) surface->Release();
30}
31
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000032Texture::Texture() : Colorbuffer(0)
33{
34 mMinFilter = GL_NEAREST_MIPMAP_LINEAR;
35 mMagFilter = GL_LINEAR;
36 mWrapS = GL_REPEAT;
37 mWrapT = GL_REPEAT;
daniel@transgaming.com29d27002010-03-11 19:41:22 +000038
daniel@transgaming.com00c75962010-03-11 20:36:15 +000039 mDirtyMetaData = true;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000040}
41
42Texture::~Texture()
43{
44}
45
46// Returns true on successful filter state update (valid enum parameter)
47bool Texture::setMinFilter(GLenum filter)
48{
49 switch (filter)
50 {
51 case GL_NEAREST:
52 case GL_LINEAR:
53 case GL_NEAREST_MIPMAP_NEAREST:
54 case GL_LINEAR_MIPMAP_NEAREST:
55 case GL_NEAREST_MIPMAP_LINEAR:
56 case GL_LINEAR_MIPMAP_LINEAR:
57 mMinFilter = filter;
58 return true;
59 default:
60 return false;
daniel@transgaming.comfab5a1a2010-03-11 19:22:30 +000061 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000062}
63
64// Returns true on successful filter state update (valid enum parameter)
65bool Texture::setMagFilter(GLenum filter)
66{
67 switch (filter)
68 {
69 case GL_NEAREST:
70 case GL_LINEAR:
71 mMagFilter = filter;
72 return true;
73 default:
74 return false;
75 }
76}
77
78// Returns true on successful wrap state update (valid enum parameter)
79bool Texture::setWrapS(GLenum wrap)
80{
81 switch (wrap)
82 {
83 case GL_REPEAT:
84 case GL_CLAMP_TO_EDGE:
85 case GL_MIRRORED_REPEAT:
86 mWrapS = wrap;
87 return true;
88 default:
89 return false;
90 }
91}
92
93// Returns true on successful wrap state update (valid enum parameter)
94bool Texture::setWrapT(GLenum wrap)
95{
96 switch (wrap)
97 {
98 case GL_REPEAT:
99 case GL_CLAMP_TO_EDGE:
100 case GL_MIRRORED_REPEAT:
101 mWrapT = wrap;
102 return true;
103 default:
104 return false;
105 }
106}
107
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000108GLenum Texture::getMinFilter() const
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000109{
110 return mMinFilter;
111}
112
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000113GLenum Texture::getMagFilter() const
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000114{
115 return mMagFilter;
116}
117
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000118GLenum Texture::getWrapS() const
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000119{
120 return mWrapS;
121}
122
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000123GLenum Texture::getWrapT() const
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000124{
125 return mWrapT;
126}
127
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000128// Selects an internal Direct3D 9 format for storing an Image
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000129D3DFORMAT Texture::selectFormat(GLenum format)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000130{
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000131 return D3DFMT_A8R8G8B8;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000132}
133
134// Returns the size, in bytes, of a single texel in an Image
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000135int Texture::pixelSize(GLenum format, GLenum type)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000136{
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000137 switch (type)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000138 {
139 case GL_UNSIGNED_BYTE:
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000140 switch (format)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000141 {
142 case GL_ALPHA: return sizeof(unsigned char);
143 case GL_LUMINANCE: return sizeof(unsigned char);
144 case GL_LUMINANCE_ALPHA: return sizeof(unsigned char) * 2;
145 case GL_RGB: return sizeof(unsigned char) * 3;
146 case GL_RGBA: return sizeof(unsigned char) * 4;
147 default: UNREACHABLE();
148 }
149 break;
150 case GL_UNSIGNED_SHORT_4_4_4_4:
151 case GL_UNSIGNED_SHORT_5_5_5_1:
152 case GL_UNSIGNED_SHORT_5_6_5:
153 return sizeof(unsigned short);
154 default: UNREACHABLE();
155 }
156
157 return 0;
158}
159
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000160int Texture::imagePitch(const Image &img) const
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000161{
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000162 return img.width * 4;
163}
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000164
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000165// Store the pixel rectangle designated by xoffset,yoffset,width,height with pixels stored as format/type at input
166// into the BGRA8 pixel rectangle at output with outputPitch bytes in between each line.
167void Texture::loadImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type,
168 const void *input, size_t outputPitch, void *output) const
169{
170 size_t inputPitch = width * pixelSize(format, type);
171
172 for (int y = 0; y < height; y++)
173 {
174 const unsigned char *source = static_cast<const unsigned char*>(input) + y * inputPitch;
175 const unsigned short *source16 = reinterpret_cast<const unsigned short*>(source);
176 unsigned char *dest = static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 4;
177
178 for (int x = 0; x < width; x++)
179 {
180 unsigned char r;
181 unsigned char g;
182 unsigned char b;
183 unsigned char a;
184
185 switch (format)
186 {
187 case GL_ALPHA:
188 a = source[x];
189 r = 0;
190 g = 0;
191 b = 0;
192 break;
193
194 case GL_LUMINANCE:
195 r = source[x];
196 g = source[x];
197 b = source[x];
198 a = 0xFF;
199 break;
200
201 case GL_LUMINANCE_ALPHA:
202 r = source[2*x+0];
203 g = source[2*x+0];
204 b = source[2*x+0];
205 a = source[2*x+1];
206 break;
207
208 case GL_RGB:
209 switch (type)
210 {
211 case GL_UNSIGNED_BYTE:
212 r = source[x * 3 + 0];
213 b = source[x * 3 + 1];
214 g = source[x * 3 + 2];
215 a = 0xFF;
216 break;
217
218 case GL_UNSIGNED_SHORT_5_6_5:
219 {
220 unsigned short rgba = source16[x];
221
222 a = 0xFF;
223 b = ((rgba & 0x001F) << 3) | ((rgba & 0x001F) >> 2);
224 g = ((rgba & 0x07E0) >> 3) | ((rgba & 0x07E0) >> 9);
225 r = ((rgba & 0xF800) >> 8) | ((rgba & 0xF800) >> 13);
226 }
227 break;
228
229 default: UNREACHABLE();
230 }
231 break;
232
233 case GL_RGBA:
234 switch (type)
235 {
236 case GL_UNSIGNED_BYTE:
237 r = source[x * 4 + 0];
238 g = source[x * 4 + 1];
239 b = source[x * 4 + 2];
240 a = source[x * 4 + 3];
241 break;
242
243 case GL_UNSIGNED_SHORT_4_4_4_4:
244 {
245 unsigned short rgba = source16[x];
246
247 a = ((rgba & 0x000F) << 4) | ((rgba & 0x000F) >> 0);
248 b = ((rgba & 0x00F0) << 0) | ((rgba & 0x00F0) >> 4);
249 g = ((rgba & 0x0F00) >> 4) | ((rgba & 0x0F00) >> 8);
250 r = ((rgba & 0xF000) >> 8) | ((rgba & 0xF000) >> 12);
251 }
252 break;
253
254 case GL_UNSIGNED_SHORT_5_5_5_1:
255 {
256 unsigned short rgba = source16[x];
257
258 a = (rgba & 0x0001) ? 0xFF : 0;
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000259 b = ((rgba & 0x003E) << 2) | ((rgba & 0x003E) >> 3);
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000260 g = ((rgba & 0x07C0) >> 3) | ((rgba & 0x07C0) >> 8);
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000261 r = ((rgba & 0xF800) >> 8) | ((rgba & 0xF800) >> 13);
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000262 }
263 break;
264
265 default: UNREACHABLE();
266 }
267 break;
268 default: UNREACHABLE();
269 }
270
271 dest[4 * x + 0] = b;
272 dest[4 * x + 1] = g;
273 dest[4 * x + 2] = r;
274 dest[4 * x + 3] = a;
275 }
276 }
277}
278
279void Texture::setImage(GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels, Image *img)
280{
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000281 IDirect3DSurface9 *newSurface = NULL;
282 HRESULT result = getDevice()->CreateOffscreenPlainSurface(width, height, selectFormat(format), D3DPOOL_SYSTEMMEM, &newSurface, NULL);
283
284 if (FAILED(result))
285 {
286 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
287 return error(GL_OUT_OF_MEMORY);
288 }
289
290 if (img->surface) img->surface->Release();
291 img->surface = newSurface;
292
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000293 img->width = width;
294 img->height = height;
295 img->format = format;
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000296
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000297 if (pixels != NULL)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000298 {
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000299 D3DLOCKED_RECT locked;
300 HRESULT result = newSurface->LockRect(&locked, NULL, 0);
301
302 ASSERT(SUCCEEDED(result));
303
304 if (SUCCEEDED(result))
305 {
306 loadImageData(0, 0, width, height, format, type, pixels, locked.Pitch, locked.pBits);
307 newSurface->UnlockRect();
308 }
309
310 img->dirty = true;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000311 }
312
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000313 mDirtyMetaData = true;
314}
315
316void Texture::subImage(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels, Image *img)
317{
318 if (width + xoffset > img->width || height + yoffset > img->height) return error(GL_INVALID_VALUE);
319
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000320 D3DLOCKED_RECT locked;
321 HRESULT result = img->surface->LockRect(&locked, NULL, 0);
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000322
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000323 ASSERT(SUCCEEDED(result));
324
325 if (SUCCEEDED(result))
326 {
327 loadImageData(xoffset, yoffset, width, height, format, type, pixels, locked.Pitch, locked.pBits);
328 img->surface->UnlockRect();
329 }
330
331 img->dirty = true;
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000332}
333
334IDirect3DBaseTexture9 *Texture::getTexture()
335{
336 if (!isComplete())
337 {
338 return NULL;
339 }
340
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000341 if (mDirtyMetaData)
342 {
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000343 mBaseTexture = createTexture();
344 }
345
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000346 if (mDirtyMetaData || dirtyImageData())
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000347 {
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000348 updateTexture();
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000349 }
350
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000351 mDirtyMetaData = false;
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000352 ASSERT(!dirtyImageData());
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000353
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000354 return mBaseTexture;
355}
356
357Texture2D::Texture2D()
358{
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000359 mTexture = NULL;
360}
361
362Texture2D::~Texture2D()
363{
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000364 if (mTexture)
365 {
366 mTexture->Release();
367 mTexture = NULL;
368 }
369}
370
371GLenum Texture2D::getTarget() const
372{
373 return GL_TEXTURE_2D;
374}
375
376void Texture2D::setImage(GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels)
377{
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000378 Texture::setImage(width, height, format, type, pixels, &mImageArray[level]);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000379
380 if (level == 0)
381 {
382 mWidth = width;
383 mHeight = height;
384 }
385}
386
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000387void Texture2D::commitRect(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height)
388{
389 ASSERT(mImageArray[level].surface != NULL);
390
391 if (mTexture != NULL)
392 {
393 IDirect3DSurface9 *destLevel = NULL;
394 mTexture->GetSurfaceLevel(level, &destLevel);
395
396 Image *img = &mImageArray[level];
397
398 RECT sourceRect;
399 sourceRect.left = xoffset;
400 sourceRect.top = yoffset;
401 sourceRect.right = xoffset + width;
402 sourceRect.bottom = yoffset + height;
403
404 POINT destPoint;
405 destPoint.x = xoffset;
406 destPoint.y = yoffset;
407
408 getDevice()->UpdateSurface(img->surface, &sourceRect, destLevel, &destPoint);
409
410 destLevel->Release();
411
412 img->dirty = false;
413 }
414}
415
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000416void Texture2D::subImage(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000417{
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000418 Texture::subImage(xoffset, yoffset, width, height, format, type, pixels, &mImageArray[level]);
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000419 commitRect(level, xoffset, yoffset, width, height);
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000420}
421
422// Tests for GL texture object completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81.
423bool Texture2D::isComplete() const
424{
425 ASSERT(mWidth == mImageArray[0].width && mHeight == mImageArray[0].height);
426
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000427 if (mWidth <= 0 || mHeight <= 0)
428 {
429 return false;
430 }
431
432 bool mipmapping;
433
daniel@transgaming.com12d54072010-03-16 06:23:26 +0000434 switch (mMinFilter)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000435 {
436 case GL_NEAREST:
437 case GL_LINEAR:
438 mipmapping = false;
439 break;
440 case GL_NEAREST_MIPMAP_NEAREST:
441 case GL_LINEAR_MIPMAP_NEAREST:
442 case GL_NEAREST_MIPMAP_LINEAR:
443 case GL_LINEAR_MIPMAP_LINEAR:
444 mipmapping = true;
445 break;
446 default: UNREACHABLE();
447 }
448
449 if (mipmapping)
450 {
daniel@transgaming.com16973022010-03-11 19:22:19 +0000451 int q = log2(std::max(mWidth, mHeight));
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000452
453 for (int level = 1; level <= q; level++)
454 {
455 if (mImageArray[level].format != mImageArray[0].format)
456 {
457 return false;
458 }
459
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000460 if (mImageArray[level].width != (mImageArray[level - 1].width + 1) / 2)
461 {
462 return false;
463 }
464
465 if (mImageArray[level].height != (mImageArray[level - 1].height + 1) / 2)
466 {
467 return false;
468 }
469 }
470 }
471
472 return true;
473}
474
475// Constructs a Direct3D 9 texture resource from the texture images, or returns an existing one
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000476IDirect3DBaseTexture9 *Texture2D::createTexture()
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000477{
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000478 IDirect3DTexture9 *texture;
479
480 IDirect3DDevice9 *device = getDevice();
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000481 D3DFORMAT format = selectFormat(mImageArray[0].format);
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000482
483 HRESULT result = device->CreateTexture(mWidth, mHeight, 0, D3DUSAGE_RENDERTARGET, format, D3DPOOL_DEFAULT, &texture, NULL);
484
485 if (result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000486 {
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000487 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000488 }
489
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000490 if (mTexture) mTexture->Release();
491 mTexture = texture;
492 return texture;
493}
494
495void Texture2D::updateTexture()
496{
497 IDirect3DDevice9 *device = getDevice();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000498
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000499 int levelCount = mTexture->GetLevelCount();
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000500
501 for (int level = 0; level < levelCount; level++)
502 {
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000503 if (mImageArray[level].dirty)
504 {
505 IDirect3DSurface9 *levelSurface = NULL;
506 mTexture->GetSurfaceLevel(level, &levelSurface);
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000507
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000508 device->UpdateSurface(mImageArray[level].surface, NULL, levelSurface, NULL);
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000509
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000510 levelSurface->Release();
511
512 mImageArray[level].dirty = false;
513 }
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000514 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000515}
516
517// Returns the top-level texture surface as a render target
518IDirect3DSurface9 *Texture2D::getRenderTarget()
519{
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000520 if (mDirtyMetaData && mRenderTarget)
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000521 {
522 mRenderTarget->Release();
523 mRenderTarget = NULL;
524 }
525
526 if (!mRenderTarget && getTexture()) // FIXME: getTexture fails for incomplete textures. Check spec.
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000527 {
daniel@transgaming.comfab5a1a2010-03-11 19:22:30 +0000528 mTexture->GetSurfaceLevel(0, &mRenderTarget);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000529 }
530
531 return mRenderTarget;
532}
533
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000534bool Texture2D::dirtyImageData() const
535{
536 int q = log2(std::max(mWidth, mHeight));
537
538 for (int i = 0; i <= q; i++)
539 {
540 if (mImageArray[i].dirty) return true;
541 }
542
543 return false;
544}
545
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000546TextureCubeMap::TextureCubeMap()
547{
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000548 mTexture = NULL;
549}
550
551TextureCubeMap::~TextureCubeMap()
552{
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000553 if (mTexture)
554 {
555 mTexture->Release();
556 mTexture = NULL;
557 }
558}
559
560GLenum TextureCubeMap::getTarget() const
561{
562 return GL_TEXTURE_CUBE_MAP;
563}
564
565void TextureCubeMap::setImagePosX(GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels)
566{
567 setImage(0, level, internalFormat, width, height, format, type, pixels);
568}
569
570void TextureCubeMap::setImageNegX(GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels)
571{
572 setImage(1, level, internalFormat, width, height, format, type, pixels);
573}
574
575void TextureCubeMap::setImagePosY(GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels)
576{
577 setImage(2, level, internalFormat, width, height, format, type, pixels);
578}
579
580void TextureCubeMap::setImageNegY(GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels)
581{
582 setImage(3, level, internalFormat, width, height, format, type, pixels);
583}
584
585void TextureCubeMap::setImagePosZ(GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels)
586{
587 setImage(4, level, internalFormat, width, height, format, type, pixels);
588}
589
590void TextureCubeMap::setImageNegZ(GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels)
591{
592 setImage(5, level, internalFormat, width, height, format, type, pixels);
593}
594
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000595void TextureCubeMap::commitRect(GLenum faceTarget, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height)
596{
597 int face = faceIndex(faceTarget);
598
599 ASSERT(mImageArray[face][level].surface != NULL);
600
601 if (mTexture != NULL)
602 {
603 IDirect3DSurface9 *destLevel = NULL;
604 mTexture->GetCubeMapSurface(static_cast<D3DCUBEMAP_FACES>(face), level, &destLevel);
605
606 Image *img = &mImageArray[face][level];
607
608 RECT sourceRect;
609 sourceRect.left = xoffset;
610 sourceRect.top = yoffset;
611 sourceRect.right = xoffset + width;
612 sourceRect.bottom = yoffset + height;
613
614 POINT destPoint;
615 destPoint.x = xoffset;
616 destPoint.y = yoffset;
617
618 getDevice()->UpdateSurface(img->surface, &sourceRect, destLevel, &destPoint);
619
620 destLevel->Release();
621
622 img->dirty = false;
623 }
624}
625
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000626void TextureCubeMap::subImage(GLenum face, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels)
627{
628 Texture::subImage(xoffset, yoffset, width, height, format, type, pixels, &mImageArray[faceIndex(face)][level]);
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000629 commitRect(face, level, xoffset, yoffset, width, height);
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000630}
631
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000632// Tests for GL texture object completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81.
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000633bool TextureCubeMap::isComplete() const
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000634{
635 if (mWidth <= 0 || mHeight <= 0 || mWidth != mHeight)
636 {
637 return false;
638 }
639
640 bool mipmapping;
641
daniel@transgaming.com12d54072010-03-16 06:23:26 +0000642 switch (mMinFilter)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000643 {
644 case GL_NEAREST:
645 case GL_LINEAR:
646 mipmapping = false;
647 break;
648 case GL_NEAREST_MIPMAP_NEAREST:
649 case GL_LINEAR_MIPMAP_NEAREST:
650 case GL_NEAREST_MIPMAP_LINEAR:
651 case GL_LINEAR_MIPMAP_LINEAR:
652 mipmapping = true;
653 break;
654 default: UNREACHABLE();
655 }
656
657 for (int face = 0; face < 6; face++)
658 {
659 if (mImageArray[face][0].width != mWidth || mImageArray[face][0].height != mHeight)
660 {
661 return false;
662 }
663 }
664
665 if (mipmapping)
666 {
667 int q = log2(mWidth);
668
669 for (int face = 0; face < 6; face++)
670 {
671 for (int level = 1; level <= q; level++)
672 {
673 if (mImageArray[face][level].format != mImageArray[0][0].format)
674 {
675 return false;
676 }
677
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000678 if (mImageArray[face][level].width != (mImageArray[0][level - 1].width + 1) / 2)
679 {
680 return false;
681 }
682
683 if (mImageArray[face][level].height != (mImageArray[0][level - 1].height + 1) / 2)
684 {
685 return false;
686 }
687 }
688 }
689 }
690
691 return true;
692}
693
694// Constructs a Direct3D 9 texture resource from the texture images, or returns an existing one
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000695IDirect3DBaseTexture9 *TextureCubeMap::createTexture()
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000696{
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000697 IDirect3DDevice9 *device = getDevice();
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000698 D3DFORMAT format = selectFormat(mImageArray[0][0].format);
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000699
700 IDirect3DCubeTexture9 *texture;
701
702 HRESULT result = device->CreateCubeTexture(mWidth, 0, D3DUSAGE_RENDERTARGET, format, D3DPOOL_DEFAULT, &texture, NULL);
703
704 if (result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000705 {
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000706 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000707 }
708
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000709 if (mTexture) mTexture->Release();
710
711 mTexture = texture;
712 return mTexture;
713}
714
715void TextureCubeMap::updateTexture()
716{
717 IDirect3DDevice9 *device = getDevice();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000718
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000719 for (int face = 0; face < 6; face++)
720 {
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000721 for (int level = 0; level <= log2(mWidth); level++)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000722 {
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000723 Image *img = &mImageArray[face][level];
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000724
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000725 if (img->dirty)
726 {
727 IDirect3DSurface9 *levelSurface = NULL;
728 mTexture->GetCubeMapSurface(static_cast<D3DCUBEMAP_FACES>(face), level, &levelSurface);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000729
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000730 device->UpdateSurface(img->surface, NULL, levelSurface, NULL);
731
732 levelSurface->Release();
733
734 img->dirty = false;
735 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000736 }
737 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000738}
739
740void TextureCubeMap::setImage(int face, GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels)
741{
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000742 Texture::setImage(width, height, format, type, pixels, &mImageArray[face][level]);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000743
744 if (face == 0 && level == 0)
745 {
746 mWidth = width;
747 mHeight = height;
748 }
749}
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000750
751unsigned int TextureCubeMap::faceIndex(GLenum face)
752{
753 META_ASSERT(GL_TEXTURE_CUBE_MAP_NEGATIVE_X - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 1);
754 META_ASSERT(GL_TEXTURE_CUBE_MAP_POSITIVE_Y - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 2);
755 META_ASSERT(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 3);
756 META_ASSERT(GL_TEXTURE_CUBE_MAP_POSITIVE_Z - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 4);
757 META_ASSERT(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 5);
758
759 return face - GL_TEXTURE_CUBE_MAP_POSITIVE_X;
760}
761
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000762bool TextureCubeMap::dirtyImageData() const
763{
764 int q = log2(mWidth);
765
766 for (int f = 0; f < 6; f++)
767 {
768 for (int i = 0; i <= q; i++)
769 {
770 if (mImageArray[f][i].dirty) return true;
771 }
772 }
773
774 return false;
775}
776
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000777}