mirror of https://github.com/axmolengine/axmol.git
402 lines
13 KiB
C++
402 lines
13 KiB
C++
#include "TextureGL.h"
|
|
#include "base/ccMacros.h"
|
|
#include "base/CCEventListenerCustom.h"
|
|
#include "base/CCEventDispatcher.h"
|
|
#include "base/CCEventType.h"
|
|
#include "base/CCDirector.h"
|
|
#include "platform/CCPlatformConfig.h"
|
|
#include "renderer/backend/opengl/UtilsGL.h"
|
|
|
|
CC_BACKEND_BEGIN
|
|
|
|
#define ISPOW2(n) (((n) & (n-1)) == 0)
|
|
|
|
void TextureInfoGL::applySamplerDescriptor(const SamplerDescriptor& descriptor, bool isPow2)
|
|
{
|
|
if (descriptor.magFilter != SamplerFilter::DONT_CARE)
|
|
{
|
|
magFilterGL = UtilsGL::toGLMagFilter(descriptor.magFilter);
|
|
}
|
|
|
|
if (descriptor.minFilter != SamplerFilter::DONT_CARE)
|
|
{
|
|
minFilterGL = UtilsGL::toGLMinFilter(descriptor.minFilter, descriptor.mipmapFilter, descriptor.mipmapEnabled, isPow2);
|
|
}
|
|
|
|
if (descriptor.sAddressMode != SamplerAddressMode::DONT_CARE)
|
|
{
|
|
sAddressModeGL = UtilsGL::toGLAddressMode(descriptor.sAddressMode, isPow2);
|
|
}
|
|
|
|
if (descriptor.tAddressMode != SamplerAddressMode::DONT_CARE)
|
|
{
|
|
tAddressModeGL = UtilsGL::toGLAddressMode(descriptor.tAddressMode, isPow2);
|
|
}
|
|
}
|
|
|
|
Texture2DGL::Texture2DGL(const TextureDescriptor& descriptor) : Texture2D(descriptor)
|
|
{
|
|
glGenTextures(1, &_textureInfo.texture);
|
|
UtilsGL::toGLTypes(_textureFormat, _textureInfo.internalFormat, _textureInfo.format, _textureInfo.type, _isCompressed);
|
|
|
|
bool isPow2 = ISPOW2(_width) && ISPOW2(_height);
|
|
_textureInfo.magFilterGL = UtilsGL::toGLMagFilter(descriptor.samplerDescriptor.magFilter);
|
|
_textureInfo.minFilterGL = UtilsGL::toGLMinFilter(descriptor.samplerDescriptor.minFilter,
|
|
descriptor.samplerDescriptor.mipmapFilter, _isMipmapEnabled, isPow2);
|
|
|
|
_textureInfo.sAddressModeGL = UtilsGL::toGLAddressMode(descriptor.samplerDescriptor.sAddressMode, isPow2);
|
|
_textureInfo.tAddressModeGL = UtilsGL::toGLAddressMode(descriptor.samplerDescriptor.tAddressMode, isPow2);
|
|
|
|
// Update data here because `updateData()` may not be invoked later.
|
|
// For example, a texture used as depth buffer will not invoke updateData().
|
|
initWithZeros();
|
|
|
|
#if CC_ENABLE_CACHE_TEXTURE_DATA
|
|
// Listen this event to restored texture id after coming to foreground on Android.
|
|
_backToForegroundListener = EventListenerCustom::create(EVENT_RENDERER_RECREATED, [this](EventCustom*){
|
|
glGenTextures(1, &(this->_textureInfo.texture));
|
|
this->initWithZeros();
|
|
});
|
|
Director::getInstance()->getEventDispatcher()->addEventListenerWithFixedPriority(_backToForegroundListener, -1);
|
|
#endif
|
|
}
|
|
|
|
void Texture2DGL::initWithZeros()
|
|
{
|
|
auto size = _width * _height * _bitsPerElement / 8;
|
|
uint8_t* data = (uint8_t*)malloc(size);
|
|
memset(data, 0, size);
|
|
updateData(data);
|
|
free(data);
|
|
}
|
|
|
|
void Texture2DGL::updateTextureDescriptor(const cocos2d::backend::TextureDescriptor &descriptor)
|
|
{
|
|
Texture::updateTextureDescriptor(descriptor);
|
|
UtilsGL::toGLTypes(descriptor.textureFormat, _textureInfo.internalFormat, _textureInfo.format, _textureInfo.type, _isCompressed);
|
|
_width = descriptor.width;
|
|
_height = descriptor.height;
|
|
updateSamplerDescriptor(descriptor.samplerDescriptor);
|
|
initWithZeros();
|
|
}
|
|
|
|
Texture2DGL::~Texture2DGL()
|
|
{
|
|
if (_textureInfo.texture)
|
|
glDeleteTextures(1, &_textureInfo.texture);
|
|
_textureInfo.texture = 0;
|
|
#if CC_ENABLE_CACHE_TEXTURE_DATA
|
|
Director::getInstance()->getEventDispatcher()->removeEventListener(_backToForegroundListener);
|
|
#endif
|
|
}
|
|
|
|
void Texture2DGL::updateSamplerDescriptor(const SamplerDescriptor &sampler) {
|
|
bool isPow2 = ISPOW2(_width) && ISPOW2(_height);
|
|
_textureInfo.applySamplerDescriptor(sampler, isPow2);
|
|
|
|
glActiveTexture(GL_TEXTURE0);
|
|
glBindTexture(GL_TEXTURE_2D, _textureInfo.texture);
|
|
|
|
if (sampler.magFilter != SamplerFilter::DONT_CARE)
|
|
{
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, _textureInfo.magFilterGL);
|
|
}
|
|
|
|
if (sampler.minFilter != SamplerFilter::DONT_CARE)
|
|
{
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, _textureInfo.minFilterGL);
|
|
}
|
|
|
|
if (sampler.sAddressMode != SamplerAddressMode::DONT_CARE)
|
|
{
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, _textureInfo.sAddressModeGL);
|
|
}
|
|
|
|
if (sampler.tAddressMode != SamplerAddressMode::DONT_CARE)
|
|
{
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, _textureInfo.tAddressModeGL);
|
|
}
|
|
}
|
|
|
|
void Texture2DGL::updateData(uint8_t* data)
|
|
{
|
|
// TODO: support texture cube, and compressed data.
|
|
|
|
//Set the row align only when mipmapsNum == 1 and the data is uncompressed
|
|
if(!_isMipmapEnabled && !_isCompressed)
|
|
{
|
|
unsigned int bytesPerRow = _width * _bitsPerElement / 8;
|
|
|
|
if(bytesPerRow % 8 == 0)
|
|
{
|
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 8);
|
|
}
|
|
else if(bytesPerRow % 4 == 0)
|
|
{
|
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
|
|
}
|
|
else if(bytesPerRow % 2 == 0)
|
|
{
|
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 2);
|
|
}
|
|
else
|
|
{
|
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
|
}
|
|
|
|
glActiveTexture(GL_TEXTURE0);
|
|
glBindTexture(GL_TEXTURE_2D, _textureInfo.texture);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, _textureInfo.magFilterGL);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, _textureInfo.minFilterGL);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, _textureInfo.sAddressModeGL);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, _textureInfo.tAddressModeGL);
|
|
|
|
|
|
if(_isCompressed)
|
|
{
|
|
auto datalen = _width * _height * _bitsPerElement / 8;
|
|
glCompressedTexImage2D(GL_TEXTURE_2D, 0, _textureInfo.internalFormat, (GLsizei)_width, (GLsizei)_height, 0, datalen, data);
|
|
}
|
|
else
|
|
{
|
|
glTexImage2D(GL_TEXTURE_2D,
|
|
0,
|
|
_textureInfo.internalFormat,
|
|
_width,
|
|
_height,
|
|
0,
|
|
_textureInfo.format,
|
|
_textureInfo.type,
|
|
data);
|
|
}
|
|
CHECK_GL_ERROR_DEBUG();
|
|
|
|
if(_isMipmapEnabled)
|
|
{
|
|
_isMipmapGenerated = false;
|
|
generateMipmaps();
|
|
}
|
|
CHECK_GL_ERROR_DEBUG();
|
|
}
|
|
|
|
void Texture2DGL::updateSubData(unsigned int xoffset, unsigned int yoffset, unsigned int width, unsigned int height, uint8_t* data)
|
|
{
|
|
glActiveTexture(GL_TEXTURE0);
|
|
glBindTexture(GL_TEXTURE_2D, _textureInfo.texture);
|
|
glTexSubImage2D(GL_TEXTURE_2D,
|
|
0,
|
|
xoffset,
|
|
yoffset,
|
|
width,
|
|
height,
|
|
_textureInfo.format,
|
|
_textureInfo.type,
|
|
data);
|
|
CHECK_GL_ERROR_DEBUG();
|
|
|
|
if(_isMipmapEnabled)
|
|
{
|
|
_isMipmapGenerated = false;
|
|
generateMipmaps();
|
|
}
|
|
CHECK_GL_ERROR_DEBUG();
|
|
}
|
|
|
|
void Texture2DGL::apply(int index) const
|
|
{
|
|
glActiveTexture(GL_TEXTURE0 + index);
|
|
glBindTexture(GL_TEXTURE_2D, _textureInfo.texture);
|
|
}
|
|
|
|
void Texture2DGL::generateMipmaps()
|
|
{
|
|
if (TextureUsage::RENDER_TARGET == _textureUsage)
|
|
return;
|
|
|
|
if(!_isMipmapGenerated)
|
|
{
|
|
_isMipmapGenerated = true;
|
|
glGenerateMipmap(GL_TEXTURE_2D);
|
|
}
|
|
}
|
|
|
|
void Texture2DGL::getBytes(int x, int y, int width, int height, bool flipImage, std::function<void(const unsigned char*, int, int)> callback)
|
|
{
|
|
GLint defaultFBO = 0;
|
|
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &defaultFBO);
|
|
|
|
GLuint frameBuffer = 0;
|
|
glGenFramebuffers(1, &frameBuffer);
|
|
glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer);
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _textureInfo.texture, 0);
|
|
glPixelStorei(GL_PACK_ALIGNMENT, 1);
|
|
|
|
auto bytePerRow = width * _bitsPerElement / 8;
|
|
unsigned char* image = new unsigned char[bytePerRow * height];
|
|
glReadPixels(x,y,width, height,GL_RGBA,GL_UNSIGNED_BYTE, image);
|
|
|
|
if(flipImage)
|
|
{
|
|
unsigned char* flippedImage = new unsigned char[bytePerRow * height];
|
|
for (int i = 0; i < height; ++i)
|
|
{
|
|
memcpy(&flippedImage[i * bytePerRow],
|
|
&image[(height - i - 1) * bytePerRow],
|
|
bytePerRow);
|
|
}
|
|
callback(flippedImage, width, height);
|
|
CC_SAFE_DELETE_ARRAY(flippedImage);
|
|
} else
|
|
{
|
|
callback(image, width, height);
|
|
CC_SAFE_DELETE_ARRAY(image);
|
|
}
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO);
|
|
glDeleteFramebuffers(1, &frameBuffer);
|
|
}
|
|
|
|
TextureCubeGL::TextureCubeGL(const TextureDescriptor& descriptor)
|
|
:TextureCubemap(descriptor)
|
|
{
|
|
assert(descriptor.width == descriptor.height);
|
|
_size = descriptor.width;
|
|
_textureType = TextureType::TEXTURE_CUBE;
|
|
UtilsGL::toGLTypes(_textureFormat, _textureInfo.internalFormat, _textureInfo.format, _textureInfo.type, _isCompressed);
|
|
glGenTextures(1, &_textureInfo.texture);
|
|
updateSamplerDescriptor(descriptor.samplerDescriptor);
|
|
|
|
#if CC_ENABLE_CACHE_TEXTURE_DATA
|
|
// Listen this event to restored texture id after coming to foreground on Android.
|
|
_backToForegroundListener = EventListenerCustom::create(EVENT_COME_TO_FOREGROUND, [this](EventCustom*){
|
|
glGenTextures(1, &(this->_textureInfo.texture));
|
|
this->setTexParameters();
|
|
});
|
|
Director::getInstance()->getEventDispatcher()->addEventListenerWithFixedPriority(_backToForegroundListener, -1);
|
|
#endif
|
|
CHECK_GL_ERROR_DEBUG();
|
|
}
|
|
|
|
void TextureCubeGL::setTexParameters()
|
|
{
|
|
glActiveTexture(GL_TEXTURE0);
|
|
glBindTexture(GL_TEXTURE_CUBE_MAP, _textureInfo.texture);
|
|
|
|
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, _textureInfo.minFilterGL);
|
|
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, _textureInfo.magFilterGL);
|
|
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, _textureInfo.sAddressModeGL);
|
|
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, _textureInfo.tAddressModeGL);
|
|
|
|
glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
|
|
}
|
|
|
|
void TextureCubeGL::updateTextureDescriptor(const cocos2d::backend::TextureDescriptor &descriptor)
|
|
{
|
|
UtilsGL::toGLTypes(descriptor.textureFormat, _textureInfo.internalFormat, _textureInfo.format, _textureInfo.type, _isCompressed);
|
|
_textureFormat = descriptor.textureFormat;
|
|
_size = descriptor.width;
|
|
updateSamplerDescriptor(descriptor.samplerDescriptor);
|
|
}
|
|
|
|
TextureCubeGL::~TextureCubeGL()
|
|
{
|
|
if(_textureInfo.texture)
|
|
glDeleteTextures(1, &_textureInfo.texture);
|
|
_textureInfo.texture = 0;
|
|
|
|
#if CC_ENABLE_CACHE_TEXTURE_DATA
|
|
Director::getInstance()->getEventDispatcher()->removeEventListener(_backToForegroundListener);
|
|
#endif
|
|
}
|
|
|
|
void TextureCubeGL::updateSamplerDescriptor(const SamplerDescriptor &sampler)
|
|
{
|
|
_textureInfo.applySamplerDescriptor(sampler, true);
|
|
setTexParameters();
|
|
}
|
|
|
|
void TextureCubeGL::apply(int index) const
|
|
{
|
|
glActiveTexture(GL_TEXTURE0+ index);
|
|
glBindTexture(GL_TEXTURE_CUBE_MAP, _textureInfo.texture);
|
|
CHECK_GL_ERROR_DEBUG();
|
|
}
|
|
|
|
void TextureCubeGL::updateFaceData(TextureCubeFace side, void *data)
|
|
{
|
|
glActiveTexture(GL_TEXTURE0);
|
|
glBindTexture(GL_TEXTURE_CUBE_MAP, _textureInfo.texture);
|
|
CHECK_GL_ERROR_DEBUG();
|
|
int i = static_cast<int>(side);
|
|
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i,
|
|
0, // level
|
|
GL_RGBA, // internal format
|
|
_size, // width
|
|
_size, // height
|
|
0, // border
|
|
_textureInfo.internalFormat, // format
|
|
_textureInfo.type, // type
|
|
data); // pixel data
|
|
if(_isMipmapEnabled)
|
|
{
|
|
_isMipmapGenerated = false;
|
|
generateMipmaps();
|
|
}
|
|
CHECK_GL_ERROR_DEBUG();
|
|
glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
|
|
}
|
|
|
|
void TextureCubeGL::getBytes(int x, int y, int width, int height, bool flipImage, std::function<void(const unsigned char*, int, int)> callback)
|
|
{
|
|
GLint defaultFBO = 0;
|
|
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &defaultFBO);
|
|
GLuint frameBuffer = 0;
|
|
glGenFramebuffers(1, &frameBuffer);
|
|
glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer);
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP, _textureInfo.texture, 0);
|
|
glPixelStorei(GL_PACK_ALIGNMENT, 1);
|
|
|
|
auto bytePerRow = width * _bitsPerElement / 8;
|
|
unsigned char* image = new unsigned char[bytePerRow * height];
|
|
glReadPixels(x,y,width, height,GL_RGBA,GL_UNSIGNED_BYTE, image);
|
|
|
|
if(flipImage)
|
|
{
|
|
unsigned char* flippedImage = new unsigned char[bytePerRow * height];
|
|
for (int i = 0; i < height; ++i)
|
|
{
|
|
memcpy(&flippedImage[i * bytePerRow],
|
|
&image[(height - i - 1) * bytePerRow],
|
|
bytePerRow);
|
|
}
|
|
callback(flippedImage, width, height);
|
|
CC_SAFE_DELETE_ARRAY(flippedImage);
|
|
} else
|
|
{
|
|
callback(image, width, height);
|
|
CC_SAFE_DELETE_ARRAY(image);
|
|
}
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO);
|
|
glDeleteFramebuffers(1, &frameBuffer);
|
|
}
|
|
|
|
void TextureCubeGL::generateMipmaps()
|
|
{
|
|
if (TextureUsage::RENDER_TARGET == _textureUsage)
|
|
return;
|
|
|
|
if(!_isMipmapGenerated)
|
|
{
|
|
_isMipmapGenerated = true;
|
|
glGenerateMipmap(GL_TEXTURE_CUBE_MAP);
|
|
}
|
|
}
|
|
|
|
CC_BACKEND_END
|