1 /* bzflag
2 * Copyright (c) 1993-2021 Tim Riker
3 *
4 * This package is free software; you can redistribute it and/or
5 * modify it under the terms of the license found in the file
6 * named COPYING that should have accompanied this file.
7 *
8 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
9 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
10 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
11 */
12
13 #include "common.h"
14
15 // system headers
16 #include <string>
17 #include <string.h>
18
19 // common headers
20 #include "bzfio.h"
21 #include "StateDatabase.h"
22 #include "bzfgl.h"
23 #include "OpenGLTexture.h"
24 #include "OpenGLGState.h"
25 #include "OpenGLUtils.h"
26
27 #ifndef _WIN32
28 typedef int64_t s64;
29 #else
30 typedef __int64 s64;
31 #endif
32
33
34 //
35 // OpenGLTexture::Rep
36 //
37
38 const GLenum OpenGLTexture::minifyFilter[] =
39 {
40 GL_NEAREST,
41 GL_NEAREST,
42 GL_LINEAR,
43 GL_NEAREST_MIPMAP_NEAREST,
44 GL_LINEAR_MIPMAP_NEAREST,
45 GL_NEAREST_MIPMAP_LINEAR,
46 GL_LINEAR_MIPMAP_LINEAR
47 };
48 const GLenum OpenGLTexture::magnifyFilter[] =
49 {
50 GL_NEAREST,
51 GL_NEAREST,
52 GL_LINEAR,
53 GL_NEAREST,
54 GL_LINEAR,
55 GL_NEAREST,
56 GL_LINEAR
57 };
58 const char* OpenGLTexture::configFilterNames[] =
59 {
60 "no",
61 "nearest",
62 "linear",
63 "nearestmipmapnearest",
64 "linearmipmapnearest",
65 "nearestmipmaplinear",
66 "linearmipmaplinear"
67 };
68
69
70 //
71 // OpenGLTexture
72 //
73
74 const int OpenGLTexture::filterCount = Max + 1;
75 OpenGLTexture::Filter OpenGLTexture::maxFilter = Default;
76
77
OpenGLTexture(int _width,int _height,const GLvoid * pixels,Filter _filter,bool _repeat)78 OpenGLTexture::OpenGLTexture(int _width, int _height, const GLvoid* pixels,
79 Filter _filter, bool _repeat) :
80 width(_width), height(_height)
81 {
82 repeat = _repeat;
83 filter = _filter;
84 list = INVALID_GL_TEXTURE_ID;
85
86 // copy/scale the original texture image
87 setupImage((const GLubyte*)pixels);
88
89 // get internal format
90 getBestFormat();
91
92 // build and bind the GL texture
93 initContext();
94
95 // watch for context recreation
96 OpenGLGState::registerContextInitializer(static_freeContext,
97 static_initContext, (void*)this);
98 }
99
100
~OpenGLTexture()101 OpenGLTexture::~OpenGLTexture()
102 {
103 OpenGLGState::unregisterContextInitializer(static_freeContext,
104 static_initContext, (void*)this);
105 delete[] imageMemory;
106
107 freeContext();
108 return;
109 }
110
111
static_freeContext(void * that)112 void OpenGLTexture::static_freeContext(void *that)
113 {
114 ((OpenGLTexture*) that)->freeContext();
115 }
116
117
static_initContext(void * that)118 void OpenGLTexture::static_initContext(void *that)
119 {
120 ((OpenGLTexture*) that)->initContext();
121 }
122
123
freeContext()124 void OpenGLTexture::freeContext()
125 {
126 // glDeleteTextures should set binding to 0 by itself when the texture
127 // is in use, but some stacks (Linux/glx/matrox) are broken, so play it safe
128 glBindTexture(GL_TEXTURE_2D, 0);
129
130 if (list != INVALID_GL_TEXTURE_ID)
131 {
132 glDeleteTextures(1, &list);
133 list = INVALID_GL_TEXTURE_ID;
134 }
135 return;
136 }
137
138
initContext()139 void OpenGLTexture::initContext()
140 {
141 // make texture map object/list
142 glGenTextures(1, &list);
143
144 // now make texture map display list (compute all mipmaps, if requested).
145 // compute next mipmap from current mipmap to save time.
146 setFilter(filter);
147 glBindTexture(GL_TEXTURE_2D, list);
148 if (GLEW_VERSION_1_4)
149 {
150 glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);
151 glTexImage2D(GL_TEXTURE_2D, 0, internalFormat,
152 scaledWidth, scaledHeight,
153 0, internalFormat, GL_UNSIGNED_BYTE, image);
154 }
155 else
156 {
157 gluBuild2DMipmaps(GL_TEXTURE_2D, internalFormat,
158 scaledWidth, scaledHeight,
159 internalFormat, GL_UNSIGNED_BYTE, image);
160 }
161 glBindTexture(GL_TEXTURE_2D, 0);
162
163 return;
164 }
165
166
setupImage(const GLubyte * pixels)167 void OpenGLTexture::setupImage(const GLubyte* pixels)
168 {
169 if (GLEW_ARB_texture_non_power_of_two)
170 {
171 scaledWidth = width;
172 scaledHeight = height;
173 // Remove check of max size. I don't want to use gluScale if GL can handle non POT
174 }
175 else
176 {
177 // align to a 2^N value
178 scaledWidth = 1;
179 scaledHeight = 1;
180 while (scaledWidth < width)
181 scaledWidth <<= 1;
182 while (scaledHeight < height)
183 scaledHeight <<= 1;
184
185 // get maximum valid size for texture (boost to 2^m x 2^n)
186 GLint maxTextureSize;
187 glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
188
189 // hard limit, some drivers have problems with sizes greater
190 // then this (espeically if you are using glTexSubImage2D)
191 const GLint dbMaxTexSize = BZDB.evalInt("maxTextureSize");
192 GLint bzMaxTexSize = 1;
193 // align the max size to a power of two (wasteful)
194 while (bzMaxTexSize < dbMaxTexSize)
195 bzMaxTexSize <<= 1;
196
197 if ((maxTextureSize < 0) || (maxTextureSize > bzMaxTexSize))
198 maxTextureSize = bzMaxTexSize;
199
200 // clamp to the maximum size
201 if (scaledWidth > maxTextureSize)
202 scaledWidth = maxTextureSize;
203 if (scaledHeight > maxTextureSize)
204 scaledHeight = maxTextureSize;
205 }
206
207 // copy the data into a 4-byte aligned buffer
208 GLubyte* unaligned = new GLubyte[4 * width * height + 4];
209 GLubyte* aligned = (GLubyte*)(((unsigned long)unaligned & ~3) + 4);
210 ::memcpy(aligned, pixels, 4 * width * height);
211
212 // scale the image if required
213 if ((scaledWidth != width) || (scaledHeight != height))
214 {
215 GLubyte* unalignedScaled = new GLubyte[4 * scaledWidth * scaledHeight + 4];
216 GLubyte* alignedScaled = (GLubyte*)(((unsigned long)unalignedScaled & ~3) + 4);
217
218 // FIXME: 0 is success, return false otherwise...
219 gluScaleImage (GL_RGBA, width, height, GL_UNSIGNED_BYTE, aligned,
220 scaledWidth, scaledHeight, GL_UNSIGNED_BYTE, alignedScaled);
221
222 delete[] unaligned;
223 unaligned = unalignedScaled;
224 aligned = alignedScaled;
225 logDebugMessage(1,"Scaling texture from %ix%i to %ix%i\n",
226 width, height, scaledWidth, scaledHeight);
227 }
228
229 // set the image
230 image = aligned;
231 imageMemory = unaligned;
232 }
233
234
getFilter()235 OpenGLTexture::Filter OpenGLTexture::getFilter()
236 {
237 return filter;
238 }
239
240
setFilter(Filter _filter)241 void OpenGLTexture::setFilter(Filter _filter)
242 {
243 filter = _filter;
244
245 int filterIndex = (int) filter;
246 // limit filter. try to keep nearest... filters as nearest and
247 // linear... as linear.
248 if (filterIndex > maxFilter)
249 {
250 if ((filterIndex & 1) == 1) // nearest...
251 {
252 if ((maxFilter & 1) == 1)
253 filterIndex = maxFilter;
254 else
255 filterIndex = maxFilter > 0 ? maxFilter - 1 : 0;
256 }
257 else // linear...
258 {
259 if ((maxFilter & 1) == 1)
260 filterIndex = maxFilter - 1;
261 else
262 filterIndex = maxFilter;
263 }
264 }
265 GLint binding;
266 glGetIntegerv (GL_TEXTURE_BINDING_2D, &binding);
267 glBindTexture(GL_TEXTURE_2D, list);
268 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minifyFilter[filterIndex]);
269 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magnifyFilter[filterIndex]);
270 if (OpenGLGState::hasAnisotropicFiltering)
271 {
272 GLint aniso = BZDB.evalInt("aniso");
273 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, aniso);
274 }
275 glBindTexture(GL_TEXTURE_2D, binding);
276 }
277
278
getMaxFilter()279 OpenGLTexture::Filter OpenGLTexture::getMaxFilter()
280 {
281 return maxFilter;
282 }
283
setMaxFilter(Filter _filter)284 void OpenGLTexture::setMaxFilter(Filter _filter)
285 {
286 maxFilter = _filter;
287 }
288
289
execute()290 void OpenGLTexture::execute()
291 {
292 bind();
293 }
294
295
getFilterName(OpenGLTexture::Filter filter)296 const char* OpenGLTexture::getFilterName(OpenGLTexture::Filter filter)
297 {
298 if ((filter < 0) || (filter > Max))
299 return configFilterNames[Max];
300 else
301 return configFilterNames[filter];
302 }
303
304
getFilterCount()305 int OpenGLTexture::getFilterCount()
306 {
307 return filterCount;
308 }
309
310
getFilterNames()311 const char** OpenGLTexture::getFilterNames()
312 {
313 return configFilterNames;
314 }
315
316
getAspectRatio() const317 float OpenGLTexture::getAspectRatio() const
318 {
319 return ((float) height) / ((float) width);
320 }
321
322
bind()323 void OpenGLTexture::bind()
324 {
325 if (list != INVALID_GL_TEXTURE_ID)
326 glBindTexture(GL_TEXTURE_2D, list);
327 else
328 {
329 glBindTexture(GL_TEXTURE_2D, 0); // heh, it's the same call
330 }
331 }
332
333
getBestFormat()334 void OpenGLTexture::getBestFormat()
335 {
336 GLubyte* scan = image;
337 const int size = scaledWidth * scaledHeight;
338 int i;
339 bool useLuminance = true;
340 alpha = false;
341 for (i = 0; i < size; scan += 4, i++)
342 {
343 // see if all pixels are achromatic
344 if (scan[0] != scan[1] || scan[0] != scan[2])
345 useLuminance = false;
346 // see if all pixels are opaque
347 if (scan[3] != 0xff)
348 alpha = true;
349 if (!useLuminance && alpha)
350 break;
351 }
352
353 scan = image;
354 GLubyte* scanOut = image;
355 for (i = 0; i < size; i++)
356 {
357 *scanOut++ = *scan++;
358 if (useLuminance)
359 {
360 scan++;
361 scan++;
362 }
363 else
364 {
365 *scanOut++ = *scan++;
366 *scanOut++ = *scan++;
367 }
368 if (alpha)
369 *scanOut++ = *scan++;
370 else
371 scan++;
372 }
373
374 // pick internal format
375 internalFormat = useLuminance ?
376 (alpha ? GL_LUMINANCE_ALPHA : GL_LUMINANCE) :
377 (alpha ? GL_RGBA : GL_RGB);
378 }
379
380
381 // Local Variables: ***
382 // mode: C++ ***
383 // tab-width: 4 ***
384 // c-basic-offset: 4 ***
385 // indent-tabs-mode: nil ***
386 // End: ***
387 // ex: shiftwidth=4 tabstop=4
388