1 #include "lc_global.h"
2 #include "lc_texture.h"
3 #include "lc_application.h"
4 #include "lc_library.h"
5 #include "image.h"
6 #include "lc_glextensions.h"
7
8 lcTexture* gGridTexture;
9
lcLoadTexture(const QString & FileName,int Flags)10 lcTexture* lcLoadTexture(const QString& FileName, int Flags)
11 {
12 lcTexture* Texture = new lcTexture();
13
14 if (!Texture->Load(FileName, Flags))
15 {
16 delete Texture;
17 Texture = nullptr;
18 }
19 else
20 {
21 strcpy(Texture->mName, QFileInfo(FileName).baseName().toLatin1());
22 Texture->SetTemporary(true);
23 }
24
25 return Texture;
26 }
27
lcReleaseTexture(lcTexture * Texture)28 void lcReleaseTexture(lcTexture* Texture)
29 {
30 if (Texture && !Texture->Release())
31 delete Texture;
32 }
33
lcTexture()34 lcTexture::lcTexture()
35 {
36 mTexture = 0;
37 mRefCount = 0;
38 mTemporary = false;
39 }
40
~lcTexture()41 lcTexture::~lcTexture()
42 {
43 Unload();
44 }
45
CreateGridTexture()46 void lcTexture::CreateGridTexture()
47 {
48 constexpr int NumLevels = 9;
49 mImages.resize(NumLevels);
50 quint8* Previous = nullptr;
51
52 for (int ImageLevel = 0; ImageLevel < NumLevels; ImageLevel++)
53 {
54 Image& GridImage = mImages[ImageLevel];
55 const int GridSize = 256 >> ImageLevel;
56 GridImage.Allocate(GridSize, GridSize, LC_PIXEL_FORMAT_A8);
57
58 if (Previous)
59 {
60 const int PreviousGridSize = 2 * GridSize;
61
62 for (int y = 0; y < GridSize - 1; y++)
63 {
64 for (int x = 0; x < GridSize - 1; x++)
65 {
66 const quint8 a = Previous[x * 2 + y * 2 * PreviousGridSize] > 64 ? 255 : 0;
67 const quint8 b = Previous[x * 2 + 1 + y * 2 * PreviousGridSize] > 64 ? 255 : 0;
68 const quint8 c = Previous[x * 2 + (y * 2 + 1) * PreviousGridSize] > 64 ? 255 : 0;
69 const quint8 d = Previous[x * 2 + 1 + (y * 2 + 1) * PreviousGridSize] > 64 ? 255 : 0;
70 GridImage.mData[x + y * GridSize] = (a + b + c + d) / 4;
71 }
72
73 int x = GridSize - 1;
74 const quint8 a = Previous[x * 2 + y * 2 * PreviousGridSize];
75 const quint8 c = Previous[x * 2 + (y * 2 + 1) * PreviousGridSize];
76 GridImage.mData[x + y * GridSize] = (a + c) / 2;
77 }
78
79 int y = GridSize - 1;
80 for (int x = 0; x < GridSize - 1; x++)
81 {
82 const quint8 a = Previous[x * 2 + y * 2 * PreviousGridSize];
83 const quint8 b = Previous[x * 2 + 1 + y * 2 * PreviousGridSize];
84 GridImage.mData[x + y * GridSize] = (a + b) / 2;
85 }
86
87 int x = GridSize - 1;
88 GridImage.mData[x + y * GridSize] = Previous[x + y * PreviousGridSize];
89 }
90 else
91 {
92 const float Radius1 = (80 >> ImageLevel) * (80 >> ImageLevel);
93 const float Radius2 = (72 >> ImageLevel) * (72 >> ImageLevel);
94 quint8* TempBuffer = new quint8[GridSize * GridSize];
95
96 for (int y = 0; y < GridSize; y++)
97 {
98 quint8* Pixel = TempBuffer + y * GridSize;
99 memset(Pixel, 0, GridSize);
100
101 const float y2 = (y - GridSize / 2) * (y - GridSize / 2);
102
103 if (Radius1 <= y2)
104 continue;
105
106 if (Radius2 <= y2)
107 {
108 const int x1 = sqrtf(Radius1 - y2);
109
110 for (int x = GridSize / 2 - x1; x < GridSize / 2 + x1; x++)
111 Pixel[x] = 255;
112 }
113 else
114 {
115 const int x1 = sqrtf(Radius1 - y2);
116 const int x2 = sqrtf(Radius2 - y2);
117
118 for (int x = GridSize / 2 - x1; x < GridSize / 2 - x2; x++)
119 Pixel[x] = 255;
120
121 for (int x = GridSize / 2 + x2; x < GridSize / 2 + x1; x++)
122 Pixel[x] = 255;
123 }
124 }
125
126 for (int y = 0; y < GridSize - 1; y++)
127 {
128 for (int x = 0; x < GridSize - 1; x++)
129 {
130 const quint8 a = TempBuffer[x + y * GridSize];
131 const quint8 b = TempBuffer[x + 1 + y * GridSize];
132 const quint8 c = TempBuffer[x + (y + 1) * GridSize];
133 const quint8 d = TempBuffer[x + 1 + (y + 1) * GridSize];
134 GridImage.mData[x + y * GridSize] = (a + b + c + d) / 4;
135 }
136
137 int x = GridSize - 1;
138 const quint8 a = TempBuffer[x + y * GridSize];
139 const quint8 c = TempBuffer[x + (y + 1) * GridSize];
140 GridImage.mData[x + y * GridSize] = (a + c) / 2;
141 }
142
143 int y = GridSize - 1;
144 for (int x = 0; x < GridSize - 1; x++)
145 {
146 const quint8 a = TempBuffer[x + y * GridSize];
147 const quint8 b = TempBuffer[x + 1 + y * GridSize];
148 GridImage.mData[x + y * GridSize] = (a + b) / 2;
149 }
150
151 int x = GridSize - 1;
152 GridImage.mData[x + y * GridSize] = TempBuffer[x + y * GridSize];
153 delete[] TempBuffer;
154 }
155
156 Previous = GridImage.mData;
157 }
158
159 mRefCount = 1;
160 mFlags = LC_TEXTURE_WRAPU | LC_TEXTURE_WRAPV | LC_TEXTURE_MIPMAPS | LC_TEXTURE_ANISOTROPIC;
161
162 lcContext* Context = lcContext::GetGlobalOffscreenContext();
163 Context->MakeCurrent();
164 Upload(Context);
165 }
166
Load()167 bool lcTexture::Load()
168 {
169 return lcGetPiecesLibrary()->LoadTexture(this);
170 }
171
Load(const QString & FileName,int Flags)172 bool lcTexture::Load(const QString& FileName, int Flags)
173 {
174 mImages.resize(1);
175
176 if (!mImages[0].FileLoad(FileName))
177 return false;
178
179 return Load(Flags);
180 }
181
Load(lcMemFile & File,int Flags)182 bool lcTexture::Load(lcMemFile& File, int Flags)
183 {
184 mImages.resize(1);
185
186 if (!mImages[0].FileLoad(File))
187 return false;
188
189 return Load(Flags);
190 }
191
SetImage(Image * Image,int Flags)192 void lcTexture::SetImage(Image* Image, int Flags)
193 {
194 mImages.clear();
195 mImages.emplace_back(std::move(*Image));
196
197 Load(Flags);
198 }
199
SetImage(std::vector<Image> && Images,int Flags)200 void lcTexture::SetImage(std::vector<Image>&& Images, int Flags)
201 {
202 mImages = std::move(Images);
203
204 Load(Flags);
205 }
206
Upload(lcContext * Context)207 void lcTexture::Upload(lcContext* Context)
208 {
209 mWidth = mImages[0].mWidth;
210 mHeight = mImages[0].mHeight;
211
212 if (!mTexture)
213 glGenTextures(1, &mTexture);
214
215 constexpr int Filters[2][5] =
216 {
217 { GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST_MIPMAP_LINEAR, GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR_MIPMAP_LINEAR },
218 { GL_NEAREST, GL_LINEAR, GL_LINEAR, GL_LINEAR, GL_LINEAR },
219 };
220
221 const int FilterFlags = mFlags & LC_TEXTURE_FILTER_MASK;
222 const int FilterIndex = FilterFlags >> LC_TEXTURE_FILTER_SHIFT;
223 const int MipIndex = mFlags & LC_TEXTURE_MIPMAPS ? 0 : 1;
224
225 unsigned int Faces, Target;
226
227 if ((mFlags & LC_TEXTURE_CUBEMAP) == 0)
228 {
229 Faces = 1;
230 Target = GL_TEXTURE_2D;
231 Context->BindTexture2D(mTexture);
232 }
233 else
234 {
235 Faces = 6;
236 Target = GL_TEXTURE_CUBE_MAP;
237 Context->BindTextureCubeMap(mTexture);
238 }
239
240 glTexParameteri(Target, GL_TEXTURE_WRAP_S, (mFlags & LC_TEXTURE_WRAPU) ? GL_REPEAT : GL_CLAMP_TO_EDGE);
241 glTexParameteri(Target, GL_TEXTURE_WRAP_T, (mFlags & LC_TEXTURE_WRAPV) ? GL_REPEAT : GL_CLAMP_TO_EDGE);
242 glTexParameteri(Target, GL_TEXTURE_MIN_FILTER, Filters[MipIndex][FilterIndex]);
243 glTexParameteri(Target, GL_TEXTURE_MAG_FILTER, Filters[1][FilterIndex]);
244 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
245
246 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
247 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
248 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
249
250 if (gSupportsAnisotropic && FilterFlags == LC_TEXTURE_ANISOTROPIC)
251 glTexParameterf(Target, GL_TEXTURE_MAX_ANISOTROPY_EXT, lcMin(4.0f, gMaxAnisotropy));
252
253 int Format;
254 switch (mImages[0].mFormat)
255 {
256 default:
257 case LC_PIXEL_FORMAT_INVALID:
258 Format = 0;
259 break;
260 case LC_PIXEL_FORMAT_A8:
261 Format = GL_ALPHA;
262 break;
263 case LC_PIXEL_FORMAT_L8A8:
264 Format = GL_LUMINANCE_ALPHA;
265 break;
266 case LC_PIXEL_FORMAT_R8G8B8:
267 Format = GL_RGB;
268 break;
269 case LC_PIXEL_FORMAT_R8G8B8A8:
270 Format = GL_RGBA;
271 break;
272 }
273
274 int CurrentImage = 0;
275 if (mFlags & LC_TEXTURE_CUBEMAP)
276 Target = GL_TEXTURE_CUBE_MAP_POSITIVE_X;
277
278 for (size_t FaceIdx = 0; FaceIdx < Faces; FaceIdx++)
279 {
280 void* Data = mImages[CurrentImage].mData;
281 glTexImage2D(Target, 0, Format, mWidth, mHeight, 0, Format, GL_UNSIGNED_BYTE, Data);
282
283 if (mFlags & LC_TEXTURE_MIPMAPS || FilterFlags >= LC_TEXTURE_BILINEAR)
284 {
285 int Width = mWidth;
286 int Height = mHeight;
287 int Components = mImages[CurrentImage].GetBPP();
288
289 for (int Level = 1; ((Width != 1) || (Height != 1)); Level++)
290 {
291 int RowStride = Width * Components;
292
293 Width = lcMax(1, Width >> 1);
294 Height = lcMax(1, Height >> 1);
295
296 if (mImages.size() == Faces)
297 {
298 GLubyte *Out, *In;
299
300 In = Out = (GLubyte*)Data;
301
302 for (int y = 0; y < Height; y++, In += RowStride)
303 for (int x = 0; x < Width; x++, Out += Components, In += 2 * Components)
304 for (int c = 0; c < Components; c++)
305 Out[c] = (In[c] + In[c + Components] + In[RowStride] + In[c + RowStride + Components]) / 4;
306 }
307 else
308 Data = mImages[++CurrentImage].mData;
309
310 glTexImage2D(Target, Level, Format, Width, Height, 0, Format, GL_UNSIGNED_BYTE, Data);
311 }
312
313 if (mImages.size() == Faces)
314 CurrentImage++;
315 }
316 else
317 CurrentImage++;
318
319 Target++;
320 }
321
322 if ((mFlags & LC_TEXTURE_CUBEMAP) == 0)
323 Context->UnbindTexture2D(mTexture);
324 else
325 Context->UnbindTextureCubeMap(mTexture);
326 }
327
Load(int Flags)328 bool lcTexture::Load(int Flags)
329 {
330 for (Image& Image : mImages)
331 Image.ResizePow2();
332 mFlags = Flags;
333
334 if (QThread::currentThread() == qApp->thread())
335 {
336 lcContext* Context = lcContext::GetGlobalOffscreenContext();
337 Context->MakeCurrent();
338 Upload(Context);
339 }
340
341 return true;
342 }
343
Unload()344 void lcTexture::Unload()
345 {
346 if (mTexture)
347 glDeleteTextures(1, &mTexture);
348 mTexture = 0;
349 }
350