2013-08-09 06:57:18 +02:00
|
|
|
#include "lc_global.h"
|
|
|
|
#include "lc_texture.h"
|
|
|
|
#include "lc_application.h"
|
|
|
|
#include "lc_library.h"
|
|
|
|
#include "image.h"
|
2015-05-09 21:54:29 +02:00
|
|
|
#include "lc_glextensions.h"
|
2013-08-09 06:57:18 +02:00
|
|
|
|
2014-08-24 00:56:59 +02:00
|
|
|
lcTexture* gGridTexture;
|
|
|
|
|
2014-10-05 07:21:51 +02:00
|
|
|
lcTexture* lcLoadTexture(const QString& FileName, int Flags)
|
|
|
|
{
|
|
|
|
lcTexture* Texture = new lcTexture();
|
|
|
|
|
2017-05-29 22:31:24 +02:00
|
|
|
if (!Texture->Load(FileName, Flags))
|
2014-10-05 07:21:51 +02:00
|
|
|
{
|
|
|
|
delete Texture;
|
2017-04-14 02:26:40 +02:00
|
|
|
Texture = nullptr;
|
2014-10-05 07:21:51 +02:00
|
|
|
}
|
2017-07-02 02:12:09 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
strcpy(Texture->mName, QFileInfo(FileName).baseName().toLatin1());
|
|
|
|
Texture->SetTemporary(true);
|
|
|
|
}
|
2014-10-05 07:21:51 +02:00
|
|
|
|
|
|
|
return Texture;
|
|
|
|
}
|
|
|
|
|
|
|
|
void lcReleaseTexture(lcTexture* Texture)
|
|
|
|
{
|
2017-12-22 14:42:28 +01:00
|
|
|
if (Texture && !Texture->Release())
|
2014-10-05 07:21:51 +02:00
|
|
|
delete Texture;
|
|
|
|
}
|
|
|
|
|
2013-08-09 06:57:18 +02:00
|
|
|
lcTexture::lcTexture()
|
|
|
|
{
|
|
|
|
mTexture = 0;
|
|
|
|
mRefCount = 0;
|
2017-07-02 02:12:09 +02:00
|
|
|
mTemporary = false;
|
2013-08-09 06:57:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
lcTexture::~lcTexture()
|
|
|
|
{
|
|
|
|
Unload();
|
|
|
|
}
|
|
|
|
|
2014-08-24 00:56:59 +02:00
|
|
|
void lcTexture::CreateGridTexture()
|
|
|
|
{
|
|
|
|
const int NumLevels = 9;
|
2017-12-22 14:42:28 +01:00
|
|
|
mImages.resize(NumLevels);
|
2017-12-02 21:22:04 +01:00
|
|
|
quint8* Previous = nullptr;
|
2014-08-24 00:56:59 +02:00
|
|
|
|
|
|
|
for (int ImageLevel = 0; ImageLevel < NumLevels; ImageLevel++)
|
|
|
|
{
|
2017-12-22 14:42:28 +01:00
|
|
|
Image& GridImage = mImages[ImageLevel];
|
2014-08-24 00:56:59 +02:00
|
|
|
const int GridSize = 256 >> ImageLevel;
|
|
|
|
GridImage.Allocate(GridSize, GridSize, LC_PIXEL_FORMAT_A8);
|
|
|
|
|
2015-05-26 01:26:03 +02:00
|
|
|
if (Previous)
|
2014-08-24 00:56:59 +02:00
|
|
|
{
|
2015-05-26 01:26:03 +02:00
|
|
|
int PreviousGridSize = 2 * GridSize;
|
2014-08-24 00:56:59 +02:00
|
|
|
|
2015-05-26 01:26:03 +02:00
|
|
|
for (int y = 0; y < GridSize - 1; y++)
|
2014-08-24 00:56:59 +02:00
|
|
|
{
|
2015-05-26 01:26:03 +02:00
|
|
|
for (int x = 0; x < GridSize - 1; x++)
|
|
|
|
{
|
2017-12-02 21:22:04 +01:00
|
|
|
quint8 a = Previous[x * 2 + y * 2 * PreviousGridSize] > 64 ? 255 : 0;
|
|
|
|
quint8 b = Previous[x * 2 + 1 + y * 2 * PreviousGridSize] > 64 ? 255 : 0;
|
|
|
|
quint8 c = Previous[x * 2 + (y * 2 + 1) * PreviousGridSize] > 64 ? 255 : 0;
|
|
|
|
quint8 d = Previous[x * 2 + 1 + (y * 2 + 1) * PreviousGridSize] > 64 ? 255 : 0;
|
2015-05-26 01:26:03 +02:00
|
|
|
GridImage.mData[x + y * GridSize] = (a + b + c + d) / 4;
|
|
|
|
}
|
|
|
|
|
|
|
|
int x = GridSize - 1;
|
2017-12-02 21:22:04 +01:00
|
|
|
quint8 a = Previous[x * 2 + y * 2 * PreviousGridSize];
|
|
|
|
quint8 c = Previous[x * 2 + (y * 2 + 1) * PreviousGridSize];
|
2015-05-26 01:26:03 +02:00
|
|
|
GridImage.mData[x + y * GridSize] = (a + c) / 2;
|
|
|
|
}
|
2014-08-24 00:56:59 +02:00
|
|
|
|
2015-05-26 01:26:03 +02:00
|
|
|
int y = GridSize - 1;
|
|
|
|
for (int x = 0; x < GridSize - 1; x++)
|
|
|
|
{
|
2017-12-02 21:22:04 +01:00
|
|
|
quint8 a = Previous[x * 2 + y * 2 * PreviousGridSize];
|
|
|
|
quint8 b = Previous[x * 2 + 1 + y * 2 * PreviousGridSize];
|
2015-05-26 01:26:03 +02:00
|
|
|
GridImage.mData[x + y * GridSize] = (a + b) / 2;
|
2014-08-24 00:56:59 +02:00
|
|
|
}
|
2015-05-26 01:26:03 +02:00
|
|
|
|
|
|
|
int x = GridSize - 1;
|
|
|
|
GridImage.mData[x + y * GridSize] = Previous[x + y * PreviousGridSize];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
const float Radius1 = (80 >> ImageLevel) * (80 >> ImageLevel);
|
|
|
|
const float Radius2 = (72 >> ImageLevel) * (72 >> ImageLevel);
|
2017-12-02 21:22:04 +01:00
|
|
|
quint8* TempBuffer = new quint8[GridSize * GridSize];
|
2015-05-26 01:26:03 +02:00
|
|
|
|
|
|
|
for (int y = 0; y < GridSize; y++)
|
2014-08-24 00:56:59 +02:00
|
|
|
{
|
2017-12-02 21:22:04 +01:00
|
|
|
quint8* Pixel = TempBuffer + y * GridSize;
|
2015-05-26 01:26:03 +02:00
|
|
|
memset(Pixel, 0, GridSize);
|
|
|
|
|
|
|
|
const float y2 = (y - GridSize / 2) * (y - GridSize / 2);
|
2014-08-24 00:56:59 +02:00
|
|
|
|
2015-05-26 01:26:03 +02:00
|
|
|
if (Radius1 <= y2)
|
|
|
|
continue;
|
2014-08-24 00:56:59 +02:00
|
|
|
|
2015-05-26 01:26:03 +02:00
|
|
|
if (Radius2 <= y2)
|
|
|
|
{
|
|
|
|
int x1 = sqrtf(Radius1 - y2);
|
|
|
|
|
|
|
|
for (int x = GridSize / 2 - x1; x < GridSize / 2 + x1; x++)
|
|
|
|
Pixel[x] = 255;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
int x1 = sqrtf(Radius1 - y2);
|
|
|
|
int x2 = sqrtf(Radius2 - y2);
|
|
|
|
|
|
|
|
for (int x = GridSize / 2 - x1; x < GridSize / 2 - x2; x++)
|
|
|
|
Pixel[x] = 255;
|
|
|
|
|
|
|
|
for (int x = GridSize / 2 + x2; x < GridSize / 2 + x1; x++)
|
|
|
|
Pixel[x] = 255;
|
|
|
|
}
|
2014-08-24 00:56:59 +02:00
|
|
|
}
|
|
|
|
|
2015-05-26 01:26:03 +02:00
|
|
|
for (int y = 0; y < GridSize - 1; y++)
|
|
|
|
{
|
|
|
|
for (int x = 0; x < GridSize - 1; x++)
|
|
|
|
{
|
2017-12-02 21:22:04 +01:00
|
|
|
quint8 a = TempBuffer[x + y * GridSize];
|
|
|
|
quint8 b = TempBuffer[x + 1 + y * GridSize];
|
|
|
|
quint8 c = TempBuffer[x + (y + 1) * GridSize];
|
|
|
|
quint8 d = TempBuffer[x + 1 + (y + 1) * GridSize];
|
2015-05-26 01:26:03 +02:00
|
|
|
GridImage.mData[x + y * GridSize] = (a + b + c + d) / 4;
|
|
|
|
}
|
|
|
|
|
|
|
|
int x = GridSize - 1;
|
2017-12-02 21:22:04 +01:00
|
|
|
quint8 a = TempBuffer[x + y * GridSize];
|
|
|
|
quint8 c = TempBuffer[x + (y + 1) * GridSize];
|
2015-05-26 01:26:03 +02:00
|
|
|
GridImage.mData[x + y * GridSize] = (a + c) / 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
int y = GridSize - 1;
|
2014-08-24 00:56:59 +02:00
|
|
|
for (int x = 0; x < GridSize - 1; x++)
|
|
|
|
{
|
2017-12-02 21:22:04 +01:00
|
|
|
quint8 a = TempBuffer[x + y * GridSize];
|
|
|
|
quint8 b = TempBuffer[x + 1 + y * GridSize];
|
2015-05-26 01:26:03 +02:00
|
|
|
GridImage.mData[x + y * GridSize] = (a + b) / 2;
|
2014-08-24 00:56:59 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int x = GridSize - 1;
|
2015-05-26 01:26:03 +02:00
|
|
|
GridImage.mData[x + y * GridSize] = TempBuffer[x + y * GridSize];
|
|
|
|
delete[] TempBuffer;
|
2014-08-24 00:56:59 +02:00
|
|
|
}
|
|
|
|
|
2015-05-26 01:26:03 +02:00
|
|
|
Previous = GridImage.mData;
|
2014-08-24 00:56:59 +02:00
|
|
|
}
|
|
|
|
|
2017-12-22 14:42:28 +01:00
|
|
|
mRefCount = 1;
|
|
|
|
mFlags = LC_TEXTURE_WRAPU | LC_TEXTURE_WRAPV | LC_TEXTURE_MIPMAPS | LC_TEXTURE_ANISOTROPIC;
|
2015-01-08 06:40:22 +01:00
|
|
|
|
2017-12-22 14:42:28 +01:00
|
|
|
lcGetPiecesLibrary()->QueueTextureUpload(this);
|
2014-08-24 00:56:59 +02:00
|
|
|
}
|
|
|
|
|
2013-08-09 06:57:18 +02:00
|
|
|
bool lcTexture::Load()
|
|
|
|
{
|
|
|
|
return lcGetPiecesLibrary()->LoadTexture(this);
|
|
|
|
}
|
|
|
|
|
2017-05-29 22:31:24 +02:00
|
|
|
bool lcTexture::Load(const QString& FileName, int Flags)
|
2013-08-09 06:57:18 +02:00
|
|
|
{
|
2017-12-22 14:42:28 +01:00
|
|
|
mImages.resize(1);
|
2013-08-09 06:57:18 +02:00
|
|
|
|
2017-12-22 14:42:28 +01:00
|
|
|
if (!mImages[0].FileLoad(FileName))
|
2013-08-09 06:57:18 +02:00
|
|
|
return false;
|
|
|
|
|
2017-12-22 14:42:28 +01:00
|
|
|
return Load(Flags);
|
2013-08-09 06:57:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
bool lcTexture::Load(lcMemFile& File, int Flags)
|
|
|
|
{
|
2017-12-22 14:42:28 +01:00
|
|
|
mImages.resize(1);
|
2013-08-09 06:57:18 +02:00
|
|
|
|
2017-12-22 14:42:28 +01:00
|
|
|
if (!mImages[0].FileLoad(File))
|
2013-08-09 06:57:18 +02:00
|
|
|
return false;
|
|
|
|
|
2017-12-22 14:42:28 +01:00
|
|
|
return Load(Flags);
|
2013-08-09 06:57:18 +02:00
|
|
|
}
|
|
|
|
|
2018-09-30 04:45:21 +02:00
|
|
|
void lcTexture::SetImage(Image* Image, int Flags)
|
|
|
|
{
|
|
|
|
mImages.clear();
|
|
|
|
mImages.emplace_back(std::move(*Image));
|
|
|
|
|
|
|
|
Load(Flags);
|
|
|
|
}
|
|
|
|
|
2018-10-29 01:59:01 +01:00
|
|
|
void lcTexture::SetImage(std::vector<Image>&& Images, int Flags)
|
|
|
|
{
|
|
|
|
mImages = std::move(Images);
|
|
|
|
|
|
|
|
Load(Flags);
|
|
|
|
}
|
|
|
|
|
2018-09-30 04:45:21 +02:00
|
|
|
void lcTexture::Upload(lcContext* Context)
|
2013-08-09 06:57:18 +02:00
|
|
|
{
|
2017-12-22 14:42:28 +01:00
|
|
|
mWidth = mImages[0].mWidth;
|
|
|
|
mHeight = mImages[0].mHeight;
|
2013-08-09 06:57:18 +02:00
|
|
|
|
2018-09-30 04:45:21 +02:00
|
|
|
if (!mTexture)
|
|
|
|
glGenTextures(1, &mTexture);
|
2013-08-09 06:57:18 +02:00
|
|
|
|
2013-08-31 23:58:47 +02:00
|
|
|
int Filters[2][5] =
|
|
|
|
{
|
|
|
|
{ GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST_MIPMAP_LINEAR, GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR_MIPMAP_LINEAR },
|
|
|
|
{ GL_NEAREST, GL_LINEAR, GL_LINEAR, GL_LINEAR, GL_LINEAR },
|
|
|
|
};
|
|
|
|
|
2017-12-22 14:42:28 +01:00
|
|
|
int FilterFlags = mFlags & LC_TEXTURE_FILTER_MASK;
|
2013-08-31 23:58:47 +02:00
|
|
|
int FilterIndex = FilterFlags >> LC_TEXTURE_FILTER_SHIFT;
|
2017-12-22 14:42:28 +01:00
|
|
|
int MipIndex = mFlags & LC_TEXTURE_MIPMAPS ? 0 : 1;
|
2013-08-31 23:58:47 +02:00
|
|
|
|
2019-02-06 01:35:15 +01:00
|
|
|
unsigned int Faces, Target;
|
2018-10-29 01:59:01 +01:00
|
|
|
|
|
|
|
if ((mFlags & LC_TEXTURE_CUBEMAP) == 0)
|
|
|
|
{
|
|
|
|
Faces = 1;
|
|
|
|
Target = GL_TEXTURE_2D;
|
|
|
|
Context->BindTexture2D(mTexture);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Faces = 6;
|
|
|
|
Target = GL_TEXTURE_CUBE_MAP;
|
|
|
|
Context->BindTextureCubeMap(mTexture);
|
|
|
|
}
|
|
|
|
|
|
|
|
glTexParameteri(Target, GL_TEXTURE_WRAP_S, (mFlags & LC_TEXTURE_WRAPU) ? GL_REPEAT : GL_CLAMP_TO_EDGE);
|
|
|
|
glTexParameteri(Target, GL_TEXTURE_WRAP_T, (mFlags & LC_TEXTURE_WRAPV) ? GL_REPEAT : GL_CLAMP_TO_EDGE);
|
|
|
|
glTexParameteri(Target, GL_TEXTURE_MIN_FILTER, Filters[MipIndex][FilterIndex]);
|
|
|
|
glTexParameteri(Target, GL_TEXTURE_MAG_FILTER, Filters[1][FilterIndex]);
|
2013-08-09 06:57:18 +02:00
|
|
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
|
|
|
|
2018-10-29 01:59:01 +01:00
|
|
|
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
|
|
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
|
|
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
|
|
|
|
|
2015-05-09 21:54:29 +02:00
|
|
|
if (gSupportsAnisotropic && FilterFlags == LC_TEXTURE_ANISOTROPIC)
|
2018-10-29 01:59:01 +01:00
|
|
|
glTexParameterf(Target, GL_TEXTURE_MAX_ANISOTROPY_EXT, lcMin(4.0f, gMaxAnisotropy));
|
2013-08-31 23:58:47 +02:00
|
|
|
|
|
|
|
int Format;
|
2017-12-22 14:42:28 +01:00
|
|
|
switch (mImages[0].mFormat)
|
2013-08-31 23:58:47 +02:00
|
|
|
{
|
2013-09-02 22:31:48 +02:00
|
|
|
default:
|
|
|
|
case LC_PIXEL_FORMAT_INVALID:
|
|
|
|
Format = 0;
|
|
|
|
break;
|
|
|
|
case LC_PIXEL_FORMAT_A8:
|
2013-08-31 23:58:47 +02:00
|
|
|
Format = GL_ALPHA;
|
|
|
|
break;
|
|
|
|
case LC_PIXEL_FORMAT_L8A8:
|
|
|
|
Format = GL_LUMINANCE_ALPHA;
|
|
|
|
break;
|
|
|
|
case LC_PIXEL_FORMAT_R8G8B8:
|
|
|
|
Format = GL_RGB;
|
|
|
|
break;
|
|
|
|
case LC_PIXEL_FORMAT_R8G8B8A8:
|
|
|
|
Format = GL_RGBA;
|
|
|
|
break;
|
|
|
|
}
|
2013-08-09 06:57:18 +02:00
|
|
|
|
2018-10-29 01:59:01 +01:00
|
|
|
int CurrentImage = 0;
|
|
|
|
if (mFlags & LC_TEXTURE_CUBEMAP)
|
|
|
|
Target = GL_TEXTURE_CUBE_MAP_POSITIVE_X;
|
2013-08-09 06:57:18 +02:00
|
|
|
|
2019-02-06 01:35:15 +01:00
|
|
|
for (size_t FaceIdx = 0; FaceIdx < Faces; FaceIdx++)
|
2013-08-09 06:57:18 +02:00
|
|
|
{
|
2018-10-29 01:59:01 +01:00
|
|
|
void* Data = mImages[CurrentImage].mData;
|
|
|
|
glTexImage2D(Target, 0, Format, mWidth, mHeight, 0, Format, GL_UNSIGNED_BYTE, Data);
|
2013-08-09 06:57:18 +02:00
|
|
|
|
2018-10-29 01:59:01 +01:00
|
|
|
if (mFlags & LC_TEXTURE_MIPMAPS || FilterFlags >= LC_TEXTURE_BILINEAR)
|
2013-08-09 06:57:18 +02:00
|
|
|
{
|
2018-10-29 01:59:01 +01:00
|
|
|
int Width = mWidth;
|
|
|
|
int Height = mHeight;
|
|
|
|
int Components = mImages[CurrentImage].GetBPP();
|
2013-08-09 06:57:18 +02:00
|
|
|
|
2018-10-29 01:59:01 +01:00
|
|
|
for (int Level = 1; ((Width != 1) || (Height != 1)); Level++)
|
2013-08-31 23:58:47 +02:00
|
|
|
{
|
2018-10-29 01:59:01 +01:00
|
|
|
int RowStride = Width * Components;
|
|
|
|
|
|
|
|
Width = lcMax(1, Width >> 1);
|
|
|
|
Height = lcMax(1, Height >> 1);
|
2013-08-31 23:58:47 +02:00
|
|
|
|
2018-10-29 01:59:01 +01:00
|
|
|
if (mImages.size() == Faces)
|
|
|
|
{
|
|
|
|
GLubyte *Out, *In;
|
|
|
|
|
|
|
|
In = Out = (GLubyte*)Data;
|
2013-08-09 06:57:18 +02:00
|
|
|
|
2018-10-29 01:59:01 +01:00
|
|
|
for (int y = 0; y < Height; y++, In += RowStride)
|
|
|
|
for (int x = 0; x < Width; x++, Out += Components, In += 2 * Components)
|
|
|
|
for (int c = 0; c < Components; c++)
|
|
|
|
Out[c] = (In[c] + In[c + Components] + In[RowStride] + In[c + RowStride + Components]) / 4;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
Data = mImages[++CurrentImage].mData;
|
|
|
|
|
|
|
|
glTexImage2D(Target, Level, Format, Width, Height, 0, Format, GL_UNSIGNED_BYTE, Data);
|
2013-08-31 23:58:47 +02:00
|
|
|
}
|
2013-08-09 06:57:18 +02:00
|
|
|
|
2018-10-29 01:59:01 +01:00
|
|
|
if (mImages.size() == Faces)
|
|
|
|
CurrentImage++;
|
2013-08-09 06:57:18 +02:00
|
|
|
}
|
2018-10-29 01:59:01 +01:00
|
|
|
else
|
|
|
|
CurrentImage++;
|
|
|
|
|
|
|
|
Target++;
|
2013-08-09 06:57:18 +02:00
|
|
|
}
|
|
|
|
|
2018-10-29 01:59:01 +01:00
|
|
|
if ((mFlags & LC_TEXTURE_CUBEMAP) == 0)
|
|
|
|
Context->UnbindTexture2D(mTexture);
|
|
|
|
else
|
|
|
|
Context->UnbindTextureCubeMap(mTexture);
|
2013-08-09 06:57:18 +02:00
|
|
|
}
|
|
|
|
|
2017-12-22 14:42:28 +01:00
|
|
|
bool lcTexture::Load(int Flags)
|
2013-08-31 23:58:47 +02:00
|
|
|
{
|
2017-12-22 14:42:28 +01:00
|
|
|
for (Image& Image : mImages)
|
|
|
|
Image.ResizePow2();
|
|
|
|
mFlags = Flags;
|
2013-08-31 23:58:47 +02:00
|
|
|
|
2017-12-22 14:42:28 +01:00
|
|
|
lcGetPiecesLibrary()->QueueTextureUpload(this);
|
|
|
|
return true;
|
2013-08-31 23:58:47 +02:00
|
|
|
}
|
|
|
|
|
2013-08-09 06:57:18 +02:00
|
|
|
void lcTexture::Unload()
|
|
|
|
{
|
|
|
|
if (mTexture)
|
|
|
|
glDeleteTextures(1, &mTexture);
|
|
|
|
mTexture = 0;
|
|
|
|
}
|