1 // stbgl - v0.04 - Sean Barrett 2008 - public domain
3 // Note that the gl extensions support requires glext.h. In fact, it works
4 // if you just concatenate glext.h onto the end of this file. In that case,
5 // this file is covered by the SGI FreeB license, and is not public domain.
9 // 1. Make a file called something like "extlist.txt" which contains stuff like:
10 // GLE(ShaderSourceARB,SHADERSOURCEARB)
11 // GLE(Uniform1iARB,UNIFORM1IARB)
12 // GLARB(ActiveTexture,ACTIVETEXTURE) // same as GLE(ActiveTextureARB,ACTIVETEXTUREARB)
13 // GLARB(ClientActiveTexture,CLIENTACTIVETEXTURE)
14 // GLE(MultiTexCoord2f,MULTITEXCOORD2F)
16 // 2. To declare functions (to make a header file), do this:
17 // #define STB_GLEXT_DECLARE "extlist.txt"
18 // #include "stb_gl.h"
20 // A good way to do this is to define STB_GLEXT_DECLARE project-wide.
22 // 3. To define functions (implement), do this in some C file:
23 // #define STB_GLEXT_DEFINE "extlist.txt"
24 // #include "stb_gl.h"
26 // If you've already defined STB_GLEXT_DECLARE, you can just do:
27 // #define STB_GLEXT_DEFINE_DECLARE
28 // #include "stb_gl.h"
30 // 4. Now you need to initialize:
32 // stbgl_initExtensions();
35 #ifndef INCLUDE_STB_GL_H
36 #define INCLUDE_STB_GL_H
42 #define CALLBACK __stdcall
43 #define WINGDIAPI __declspec(dllimport)
44 #define APIENTRY __stdcall
54 #define M_PI 3.14159265358979323846f
61 // like gluPerspective, but:
62 // fov is chosen to satisfy both hfov <= max_hfov & vfov <= max_vfov;
63 // set one to 179 or 0 to ignore it
64 // zoom is applied separately, so you can do linear zoom without
65 // mucking with trig with fov; 1 -> use exact fov
66 // 'aspect' is inferred from the current viewport, and ignores the
67 // possibility of non-square pixels
68 extern void stbgl_Perspective(float zoom
, float max_hfov
, float max_vfov
, float znear
, float zfar
);
69 extern void stbgl_PerspectiveViewport(int x
, int y
, int w
, int h
, float zoom
, float max_hfov
, float max_vfov
, float znear
, float zfar
);
70 extern void stbgl_initCamera_zup_facing_x(void);
71 extern void stbgl_initCamera_zup_facing_y(void);
72 extern void stbgl_positionCameraWithEulerAngles(float *loc
, float *ang
);
73 extern void stbgl_drawRect(float x0
, float y0
, float x1
, float y1
);
74 extern void stbgl_drawRectTC(float x0
, float y0
, float x1
, float y1
, float s0
, float t0
, float s1
, float t1
);
75 extern void stbgl_drawBox(float x
, float y
, float z
, float sx
, float sy
, float sz
, int cw
);
77 extern int stbgl_hasExtension(char *ext
);
78 extern void stbgl_SimpleLight(int index
, float bright
, float x
, float y
, float z
);
79 extern void stbgl_GlobalAmbient(float r
, float g
, float b
);
81 extern int stbgl_LoadTexture(char *filename
, char *props
); // only if stb_image is available
83 extern int stbgl_TestTexture(int w
);
84 extern int stbgl_TestTextureEx(int w
, char *scale_table
, int checks_log2
, int r1
,int g1
,int b1
, int r2
, int b2
, int g2
);
85 extern unsigned int stbgl_rand(void); // internal, but exposed just in case; LCG, so use middle bits
87 extern int stbgl_TexImage2D(int texid
, int w
, int h
, void *data
, char *props
);
88 extern int stbgl_TexImage2D_Extra(int texid
, int w
, int h
, void *data
, int chan
, char *props
, int preserve_data
);
89 // "props" is a series of characters (and blocks of characters), a la fopen()'s mode,
91 // GLuint texid = stbgl_LoadTexture("myfile.jpg", "mbc")
92 // means: load the image "myfile.jpg", and do the following:
94 // use bilinear filtering (not trilinear)
95 // use clamp-to-edge on both channels
97 // input descriptor: AT MOST ONE
99 // 1 1 channel of input (intensity/alpha)
100 // 2 2 channels of input (luminance, alpha)
101 // 3 3 channels of input (RGB)
102 // 4 4 channels of input (RGBA)
103 // l 1 channel of input (luminance)
104 // a 1 channel of input (alpha)
105 // la 2 channels of input (lum/alpha)
106 // rgb 3 channels of input (RGB)
107 // ycocg 3 channels of input (YCoCg - forces YCoCg output)
108 // ycocgj 4 channels of input (YCoCgJunk - forces YCoCg output)
109 // rgba 4 channels of input (RGBA)
111 // output descriptor: AT MOST ONE
113 // A 1 channel of output (alpha)
114 // I 1 channel of output (intensity)
115 // LA 2 channels of output (lum/alpha)
116 // RGB 3 channels of output (RGB)
117 // RGBA 4 channels of output (RGBA)
118 // DXT1 encode as a DXT1 texture (RGB unless input has RGBA)
119 // DXT3 encode as a DXT3 texture
120 // DXT5 encode as a DXT5 texture
121 // YCoCg encode as a DXT5 texture with Y in alpha, CoCg in RG
122 // D GL_DEPTH_COMPONENT
123 // NONE no input/output, don't call TexImage2D at all
125 // when reading from a file or using another interface with an explicit
126 // channel count, the input descriptor is ignored and instead the channel
127 // count is used as the input descriptor. if the file read is a DXT DDS,
128 // then it is passed directly to OpenGL in the file format.
130 // if an input descriptor is supplied but no output descriptor, the output
131 // is assumed to be the same as the input. if an output descriptor is supplied
132 // but no input descriptor, the input is assumed to be the same as the
133 // output. if neither is supplied, the input is assumed to be 4-channel.
134 // If DXT1 or YCoCG output is requested with no input, the input is assumed
135 // to be 4-channel but the alpha channel is ignored.
137 // filtering descriptor (default is no mipmaps)
139 // m generate mipmaps
140 // M mipmaps are provided, concatenated at end of data (from largest to smallest)
141 // t use trilinear filtering (default if mipmapped)
142 // b use bilinear filtering (default if not-mipmapped)
143 // n use nearest-neighbor sampling
145 // wrapping descriptor
149 // C GL_CLAMP (uses border color)
151 // If only one wrapping descriptor is supplied, it is applied to both channels.
155 // f input data is floats (default unsigned bytes)
156 // F input&output data is floats (default unsigned bytes)
157 // p explicitly pre-multiply the alpha
158 // P pad to power-of-two (default stretches)
159 // NP2 non-power-of-two
160 // + can overwrite the texture data with temp data
161 // ! free the texture data with "free"
163 // the properties string can also include spaces
170 #ifdef STB_GL_IMPLEMENTATION
176 int stbgl_hasExtension(char *ext
)
178 const char *s
= glGetString(GL_EXTENSIONS
);
183 if (*s
== 0 || *s
== ' ') return 1;
190 while (*s
&& *s
!= ' ') ++s
;
196 void stbgl_drawRect(float x0
, float y0
, float x1
, float y1
)
199 glTexCoord2f(0,0); glVertex2f(x0
,y0
);
200 glTexCoord2f(1,0); glVertex2f(x1
,y0
);
201 glTexCoord2f(1,1); glVertex2f(x1
,y1
);
202 glTexCoord2f(0,1); glVertex2f(x0
,y1
);
206 void stbgl_drawRectTC(float x0
, float y0
, float x1
, float y1
, float s0
, float t0
, float s1
, float t1
)
209 glTexCoord2f(s0
,t0
); glVertex2f(x0
,y0
);
210 glTexCoord2f(s1
,t0
); glVertex2f(x1
,y0
);
211 glTexCoord2f(s1
,t1
); glVertex2f(x1
,y1
);
212 glTexCoord2f(s0
,t1
); glVertex2f(x0
,y1
);
216 void stbgl_drawBox(float x
, float y
, float z
, float sx
, float sy
, float sz
, int cw
)
218 float x0
,y0
,z0
,x1
,y1
,z1
;
219 sx
/=2, sy
/=2, sz
/=2;
220 x0
= x
-sx
; y0
= y
-sy
; z0
= z
-sz
;
221 x1
= x
+sx
; y1
= y
+sy
; z1
= z
+sz
;
226 glTexCoord2f(0,0); glVertex3f(x0
,y0
,z0
);
227 glTexCoord2f(1,0); glVertex3f(x1
,y0
,z0
);
228 glTexCoord2f(1,1); glVertex3f(x1
,y1
,z0
);
229 glTexCoord2f(0,1); glVertex3f(x0
,y1
,z0
);
232 glTexCoord2f(0,0); glVertex3f(x1
,y0
,z1
);
233 glTexCoord2f(1,0); glVertex3f(x0
,y0
,z1
);
234 glTexCoord2f(1,1); glVertex3f(x0
,y1
,z1
);
235 glTexCoord2f(0,1); glVertex3f(x1
,y1
,z1
);
238 glTexCoord2f(0,0); glVertex3f(x0
,y1
,z1
);
239 glTexCoord2f(1,0); glVertex3f(x0
,y0
,z1
);
240 glTexCoord2f(1,1); glVertex3f(x0
,y0
,z0
);
241 glTexCoord2f(0,1); glVertex3f(x0
,y1
,z0
);
244 glTexCoord2f(0,0); glVertex3f(x1
,y0
,z1
);
245 glTexCoord2f(1,0); glVertex3f(x1
,y1
,z1
);
246 glTexCoord2f(1,1); glVertex3f(x1
,y1
,z0
);
247 glTexCoord2f(0,1); glVertex3f(x1
,y0
,z0
);
250 glTexCoord2f(0,0); glVertex3f(x0
,y0
,z1
);
251 glTexCoord2f(1,0); glVertex3f(x1
,y0
,z1
);
252 glTexCoord2f(1,1); glVertex3f(x1
,y0
,z0
);
253 glTexCoord2f(0,1); glVertex3f(x0
,y0
,z0
);
256 glTexCoord2f(0,0); glVertex3f(x1
,y1
,z1
);
257 glTexCoord2f(1,0); glVertex3f(x0
,y1
,z1
);
258 glTexCoord2f(1,1); glVertex3f(x0
,y1
,z0
);
259 glTexCoord2f(0,1); glVertex3f(x1
,y1
,z0
);
262 glTexCoord2f(0,0); glVertex3f(x0
,y0
,z0
);
263 glTexCoord2f(0,1); glVertex3f(x0
,y1
,z0
);
264 glTexCoord2f(1,1); glVertex3f(x1
,y1
,z0
);
265 glTexCoord2f(1,0); glVertex3f(x1
,y0
,z0
);
268 glTexCoord2f(0,0); glVertex3f(x1
,y0
,z1
);
269 glTexCoord2f(0,1); glVertex3f(x1
,y1
,z1
);
270 glTexCoord2f(1,1); glVertex3f(x0
,y1
,z1
);
271 glTexCoord2f(1,0); glVertex3f(x0
,y0
,z1
);
274 glTexCoord2f(0,0); glVertex3f(x0
,y1
,z1
);
275 glTexCoord2f(0,1); glVertex3f(x0
,y1
,z0
);
276 glTexCoord2f(1,1); glVertex3f(x0
,y0
,z0
);
277 glTexCoord2f(1,0); glVertex3f(x0
,y0
,z1
);
280 glTexCoord2f(0,0); glVertex3f(x1
,y0
,z1
);
281 glTexCoord2f(0,1); glVertex3f(x1
,y0
,z0
);
282 glTexCoord2f(1,1); glVertex3f(x1
,y1
,z0
);
283 glTexCoord2f(1,0); glVertex3f(x1
,y1
,z1
);
286 glTexCoord2f(0,0); glVertex3f(x0
,y0
,z1
);
287 glTexCoord2f(0,1); glVertex3f(x0
,y0
,z0
);
288 glTexCoord2f(1,1); glVertex3f(x1
,y0
,z0
);
289 glTexCoord2f(1,0); glVertex3f(x1
,y0
,z1
);
292 glTexCoord2f(0,0); glVertex3f(x1
,y1
,z1
);
293 glTexCoord2f(0,1); glVertex3f(x1
,y1
,z0
);
294 glTexCoord2f(1,1); glVertex3f(x0
,y1
,z0
);
295 glTexCoord2f(1,0); glVertex3f(x0
,y1
,z1
);
300 void stbgl_SimpleLight(int index
, float bright
, float x
, float y
, float z
)
302 float d
= (float) (1.0f
/sqrt(x
*x
+y
*y
+z
*z
));
303 float dir
[4] = { x
*d
,y
*d
,z
*d
,0 }, zero
[4] = { 0,0,0,0 };
304 float c
[4] = { bright
,bright
,bright
,0 };
305 GLuint light
= GL_LIGHT0
+ index
;
306 glLightfv(light
, GL_POSITION
, dir
);
307 glLightfv(light
, GL_DIFFUSE
, c
);
308 glLightfv(light
, GL_AMBIENT
, zero
);
309 glLightfv(light
, GL_SPECULAR
, zero
);
311 glColorMaterial(GL_FRONT
, GL_AMBIENT_AND_DIFFUSE
);
312 glEnable(GL_COLOR_MATERIAL
);
315 void stbgl_GlobalAmbient(float r
, float g
, float b
)
317 float v
[4] = { r
,g
,b
,0 };
318 glLightModelfv(GL_LIGHT_MODEL_AMBIENT
, v
);
322 #define stbgl_rad2deg(r) ((r)*180.0f / M_PI)
323 #define stbgl_deg2rad(r) ((r)/180.0f * M_PI)
325 void stbgl_Perspective(float zoom
, float max_hfov
, float max_vfov
, float znear
, float zfar
)
327 float unit_width
, unit_height
, aspect
, vfov
;
329 glGetIntegerv(GL_VIEWPORT
, data
);
332 aspect
= (float) w
/ h
;
334 if (max_hfov
<= 0) max_hfov
= 179;
335 if (max_vfov
<= 0) max_vfov
= 179;
337 // convert max_hfov, max_vfov to worldspace width at depth=1
338 unit_width
= (float) tan(stbgl_deg2rad(max_hfov
/2)) * 2;
339 unit_height
= (float) tan(stbgl_deg2rad(max_vfov
/2)) * 2;
340 // check if hfov = max_hfov is enough to satisfy it
341 if (unit_width
<= aspect
* unit_height
) {
342 float height
= unit_width
/ aspect
;
343 vfov
= (float) atan(( height
/2) / zoom
);
345 vfov
= (float) atan((unit_height
/2) / zoom
);
347 vfov
= (float) stbgl_rad2deg(vfov
* 2);
348 gluPerspective(vfov
, aspect
, znear
, zfar
);
351 void stbgl_PerspectiveViewport(int x
, int y
, int w
, int h
, float zoom
, float min_hfov
, float min_vfov
, float znear
, float zfar
)
353 if (znear
<= 0.0001f
) znear
= 0.0001f
;
356 glMatrixMode(GL_PROJECTION
);
358 stbgl_Perspective(zoom
, min_hfov
, min_vfov
, znear
, zfar
);
359 glMatrixMode(GL_MODELVIEW
);
362 // point the camera along the positive X axis, Z-up
363 void stbgl_initCamera_zup_facing_x(void)
365 glRotatef(-90, 1,0,0);
366 glRotatef( 90, 0,0,1);
369 // point the camera along the positive Y axis, Z-up
370 void stbgl_initCamera_zup_facing_y(void)
372 glRotatef(-90, 1,0,0);
375 // setup a camera using Euler angles
376 void stbgl_positionCameraWithEulerAngles(float *loc
, float *ang
)
378 glRotatef(-ang
[1], 0,1,0);
379 glRotatef(-ang
[0], 1,0,0);
380 glRotatef(-ang
[2], 0,0,1);
381 glTranslatef(-loc
[0], -loc
[1], -loc
[2]);
384 static int stbgl_m(char *a
, char *b
)
386 // skip first character
387 do { ++a
,++b
; } while (*b
&& *a
== *b
);
392 #ifndef STBI_NO_STDIO
393 int stbgl_LoadTexture(char *filename
, char *props
)
395 // @TODO: handle DDS files directly
400 if (stbi_is_hdr(filename
)) {
401 data
= stbi_loadf(filename
, &w
, &h
, &c
, 0);
403 res
= stbgl_TexImage2D_Extra(0, w
,h
,data
, -c
, props
, 0);
409 data
= stbi_load(filename
, &w
, &h
, &c
, 0);
411 res
= stbgl_TexImage2D_Extra(0, w
,h
,data
, c
, props
, 0);
416 #endif // STBI_VERSION
418 int stbgl_TexImage2D(int texid
, int w
, int h
, void *data
, char *props
)
420 return stbgl_TexImage2D_Extra(texid
, w
, h
, data
, 0, props
,1);
423 int stbgl_TestTexture(int w
)
425 char scale_table
[] = { 10,20,30,30,35,40,5,18,25,13,7,5,3,3,2,2,2,2,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0 };
426 return stbgl_TestTextureEx(w
, scale_table
, 2, 140,130,200, 180,200,170);
429 unsigned int stbgl_rand(void)
431 static unsigned int stbgl__rand_seed
= 3248980923; // random typing
432 return stbgl__rand_seed
= stbgl__rand_seed
* 2147001325 + 715136305; // BCPL generator
435 // wish this could be smaller, since it's so frivolous
436 int stbgl_TestTextureEx(int w
, char *scale_table
, int checks_log2
, int r1
,int g1
,int b1
, int r2
, int b2
, int g2
)
438 int rt
[2] = {r1
,r2
}, gt
[2] = {g1
,g2
}, bt
[2] = {b1
,b2
};
439 signed char modded
[256];
440 int i
,j
, m
= w
-1, s
,k
,scale
;
441 unsigned char *data
= (unsigned char *) malloc(w
*w
*3);
442 assert((m
& w
) == 0);
444 for (s
=0; s
< 16; ++s
) if ((1 << s
) == w
) break;
445 assert(w
== (1 << s
));
446 // plasma fractal noise
447 for (k
=s
-1; k
>= 0; --k
) {
449 // interpolate from "parents"
450 for (j
=0; j
< w
; j
+= step
*2) {
451 for (i
=0; i
< w
; i
+= step
*2) {
452 int i1
= i
+step
, j1
=j
+step
;
453 int i2
= (i
+step
*2)&m
, j2
= (j
+step
*2)&m
;
454 int p00
= data
[(j
*w
+i
)*3], p01
= data
[(j2
*w
+i
)*3];
455 int p10
= data
[(j
*w
+i2
)*3], p11
= data
[(j2
*w
+i2
)*3];
456 data
[(j
*w
+i1
)*3] = (p00
+p10
)>>1;
457 data
[(j1
*w
+i
)*3] = (p00
+p01
)>>1;
458 data
[(j1
*w
+i1
)*3]= (p00
+p01
+p10
+p11
)>>2;
461 scale
= scale_table
[s
-k
+1];
462 if (!scale
) continue; // just interpolate down the remaining data
463 for (j
=0,i
=0; i
< 256; i
+= 2, j
== scale
? j
=0 : ++j
)
464 modded
[i
] = j
, modded
[i
+1] = -j
; // precompute i%scale (plus sign)
465 for (j
=0; j
< w
; j
+= step
)
466 for (i
=0; i
< w
; i
+= step
) {
467 int x
= data
[(j
*w
+i
)*3] + modded
[(stbgl_rand() >> 12) & 255];
468 data
[(j
*w
+i
)*3] = x
< 0 ? 0 : x
> 255 ? 255 : x
;
471 for (j
=0; j
< w
; ++j
)
472 for (i
=0; i
< w
; ++i
) {
473 int check
= ((i
^j
) & (1 << (s
-checks_log2
))) == 0;
474 int v
= data
[(j
*w
+i
)*3] >> 2;
475 data
[(j
*w
+i
)*3+0] = rt
[check
]-v
;
476 data
[(j
*w
+i
)*3+1] = gt
[check
]-v
;
477 data
[(j
*w
+i
)*3+2] = bt
[check
]-v
;
479 return stbgl_TexImage2D(0, w
, w
, data
, "3m!"); // 3 channels, mipmap, free
484 typedef int (__stdcall
*stbgl__voidfunc
)(void);
485 __declspec(dllimport
) stbgl__voidfunc
wglGetProcAddress(char *);
487 #define STB__HAS_WGLPROC
488 static void (__stdcall
*stbgl__CompressedTexImage2DARB
)(int target
, int level
,
489 int internalformat
, int width
,
490 int height
, int border
,
491 int imageSize
, void *data
);
492 static void stbgl__initCompTex(void)
494 *((void **) &stbgl__CompressedTexImage2DARB
) = (void *) wglGetProcAddress("glCompressedTexImage2DARB");
497 static void (*stbgl__CompressedTexImage2DARB
)(int target
, int level
,
498 int internalformat
, int width
,
499 int height
, int border
,
500 int imageSize
, void *data
);
501 static void stbgl__initCompTex(void)
506 #define STBGL_COMPRESSED_RGB_S3TC_DXT1 0x83F0
507 #define STBGL_COMPRESSED_RGBA_S3TC_DXT1 0x83F1
508 #define STBGL_COMPRESSED_RGBA_S3TC_DXT3 0x83F2
509 #define STBGL_COMPRESSED_RGBA_S3TC_DXT5 0x83F3
511 #ifdef STB_COMPRESS_DXT_BLOCK
512 static void stbgl__convert(uint8
*p
, uint8
*q
, int n
, int input_desc
, uint8
*end
)
515 switch (input_desc
) {
517 case GL_LUMINANCE
: for (i
=0; i
< n
; ++i
,p
+=4) p
[0] = p
[1] = p
[2] = q
[0], p
[3]=255, q
+=1; break;
518 case GL_ALPHA
: for (i
=0; i
< n
; ++i
,p
+=4) p
[0] = p
[1] = p
[2] = 0, p
[3] = q
[0], q
+=1; break;
519 case GL_LUMINANCE_ALPHA
: for (i
=0; i
< n
; ++i
,p
+=4) p
[0] = p
[1] = p
[2] = q
[0], p
[3]=q
[1], q
+=2; break;
520 case GL_RGB
: for (i
=0; i
< n
; ++i
,p
+=4) p
[0]=q
[0],p
[1]=q
[1],p
[2]=q
[2],p
[3]=255,q
+=3; break;
521 case GL_RGBA
: memcpy(p
, q
, n
*4); break;
522 case GL_INTENSITY
: for (i
=0; i
< n
; ++i
,p
+=4) p
[0] = p
[1] = p
[2] = p
[3] = q
[0], q
+=1; break;
527 static void stbgl__compress(uint8
*p
, uint8
*rgba
, int w
, int h
, int output_desc
, uint8
*end
)
530 int alpha
= (output_desc
== STBGL_COMPRESSED_RGBA_S3TC_DXT5
);
531 for (j
=0; j
< w
; j
+= 4) {
533 for (i
=0; i
< h
; i
+= 4) {
535 if (i
+3 >= w
) x
= w
-i
;
536 for (y
=0; y
< 4; ++y
) {
538 memcpy(block
+y
*16, rgba
+ w
*4*(j
+y
) + i
*4, x
*4);
544 for (y2
=0; y2
< y
; ++y2
) {
545 memcpy(block
+y2
*16+1*4, block
+y2
*16+0*4, 4);
546 memcpy(block
+y2
*16+2*4, block
+y2
*16+0*4, 8);
550 for (y2
=0; y2
< y
; ++y2
)
551 memcpy(block
+y2
*16+2*4, block
+y2
*16+0*4, 8);
554 for (y2
=0; y2
< y
; ++y2
)
555 memcpy(block
+y2
*16+3*4, block
+y2
*16+1*4, 4);
561 memcpy(block
+y
*16, block
+y2
*16, 4*4);
562 stb_compress_dxt_block(p
, block
, alpha
, 10);
568 #endif // STB_COMPRESS_DXT_BLOCK
570 // use the reserved temporary-use enumerant range, since no
571 // OpenGL enumerants should fall in that range
574 STBGL_UNDEFINED
= 0x6000,
582 #define STBGL_CLAMP_TO_EDGE 0x812F
583 #define STBGL_CLAMP_TO_BORDER 0x812D
585 #define STBGL_DEPTH_COMPONENT16 0x81A5
586 #define STBGL_DEPTH_COMPONENT24 0x81A6
587 #define STBGL_DEPTH_COMPONENT32 0x81A7
589 int stbgl_TexImage2D_Extra(int texid
, int w
, int h
, void *data
, int chan
, char *props
, int preserve_data
)
591 static int has_s3tc
= -1; // haven't checked yet
592 int free_data
= 0, is_compressed
= 0;
593 int pad_to_power_of_two
= 0, non_power_of_two
= 0;
594 int premultiply_alpha
= 0; // @TODO
595 int float_tex
= 0; // @TODO
596 int input_type
= GL_UNSIGNED_BYTE
;
597 int input_desc
= STBGL_UNDEFINED
;
598 int output_desc
= STBGL_UNDEFINED
;
599 int mipmaps
= STBGL_UNDEFINED
;
600 int filter
= STBGL_UNDEFINED
, mag_filter
;
601 int wrap_s
= STBGL_UNDEFINED
, wrap_t
= STBGL_UNDEFINED
;
603 // parse out the properties
604 if (props
== NULL
) props
= "";
607 case '1' : input_desc
= GL_LUMINANCE
; break;
608 case '2' : input_desc
= GL_LUMINANCE_ALPHA
; break;
609 case '3' : input_desc
= GL_RGB
; break;
610 case '4' : input_desc
= GL_RGBA
; break;
611 case 'l' : if (props
[1] == 'a') { input_desc
= GL_LUMINANCE_ALPHA
; ++props
; }
612 else input_desc
= GL_LUMINANCE
;
614 case 'a' : input_desc
= GL_ALPHA
; break;
615 case 'r' : if (stbgl_m(props
, "rgba")) { input_desc
= GL_RGBA
; props
+= 3; break; }
616 if (stbgl_m(props
, "rgb")) { input_desc
= GL_RGB
; props
+= 2; break; }
619 case 'y' : if (stbgl_m(props
, "ycocg")) {
620 if (props
[5] == 'j') { props
+= 5; input_desc
= STBGL_YCOCGJ
; }
621 else { props
+= 4; input_desc
= STBGL_YCOCG
; }
625 case 'L' : if (props
[1] == 'A') { output_desc
= GL_LUMINANCE_ALPHA
; ++props
; }
626 else output_desc
= GL_LUMINANCE
;
628 case 'I' : output_desc
= GL_INTENSITY
; break;
629 case 'A' : output_desc
= GL_ALPHA
; break;
630 case 'R' : if (stbgl_m(props
, "RGBA")) { output_desc
= GL_RGBA
; props
+= 3; break; }
631 if (stbgl_m(props
, "RGB")) { output_desc
= GL_RGB
; props
+= 2; break; }
632 output_desc
= GL_RED
;
634 case 'Y' : if (stbgl_m(props
, "YCoCg") || stbgl_m(props
, "YCOCG")) {
636 output_desc
= STBGL_YCOCG
;
640 case 'D' : if (stbgl_m(props
, "DXT")) {
642 case '1': output_desc
= STBGL_COMPRESSED_RGB_S3TC_DXT1
; break;
643 case '3': output_desc
= STBGL_COMPRESSED_RGBA_S3TC_DXT3
; break;
644 case '5': output_desc
= STBGL_COMPRESSED_RGBA_S3TC_DXT5
; break;
648 } else if (stbgl_m(props
, "D16")) {
649 output_desc
= STBGL_DEPTH_COMPONENT16
;
650 input_desc
= GL_DEPTH_COMPONENT
;
652 } else if (stbgl_m(props
, "D24")) {
653 output_desc
= STBGL_DEPTH_COMPONENT24
;
654 input_desc
= GL_DEPTH_COMPONENT
;
656 } else if (stbgl_m(props
, "D32")) {
657 output_desc
= STBGL_DEPTH_COMPONENT32
;
658 input_desc
= GL_DEPTH_COMPONENT
;
661 output_desc
= GL_DEPTH_COMPONENT
;
662 input_desc
= GL_DEPTH_COMPONENT
;
665 case 'N' : if (stbgl_m(props
, "NONE")) {
667 input_desc
= STBGL_NO_DOWNLOAD
;
668 output_desc
= STBGL_NO_DOWNLOAD
;
671 if (stbgl_m(props
, "NP2")) {
672 non_power_of_two
= 1;
677 case 'm' : mipmaps
= STBGL_GEN_MIPMAPS
; break;
678 case 'M' : mipmaps
= STBGL_MIPMAPS
; break;
679 case 't' : filter
= GL_LINEAR_MIPMAP_LINEAR
; break;
680 case 'b' : filter
= GL_LINEAR
; break;
681 case 'n' : filter
= GL_NEAREST
; break;
682 case 'w' : if (wrap_s
== STBGL_UNDEFINED
) wrap_s
= GL_REPEAT
; else wrap_t
= GL_REPEAT
; break;
683 case 'C' : if (wrap_s
== STBGL_UNDEFINED
) wrap_s
= STBGL_CLAMP_TO_BORDER
; else wrap_t
= STBGL_CLAMP_TO_BORDER
; break;
684 case 'c' : if (wrap_s
== STBGL_UNDEFINED
) wrap_s
= STBGL_CLAMP_TO_EDGE
; else wrap_t
= STBGL_CLAMP_TO_EDGE
; break;
685 case 'f' : input_type
= GL_FLOAT
; break;
686 case 'F' : input_type
= GL_FLOAT
; float_tex
= 1; break;
687 case 'p' : premultiply_alpha
= 1; break;
688 case 'P' : pad_to_power_of_two
= 1; break;
689 case '+' : preserve_data
= 0; break;
690 case '!' : preserve_data
= 0; free_data
= 1; break;
693 default : if (free_data
) free(data
);
699 // override input_desc based on channel count
700 if (output_desc
!= STBGL_NO_DOWNLOAD
) {
702 case 1: input_desc
= GL_LUMINANCE
; break;
703 case 2: input_desc
= GL_LUMINANCE_ALPHA
; break;
704 case 3: input_desc
= GL_RGB
; break;
705 case 4: input_desc
= GL_RGBA
; break;
711 // override input_desc based on channel info
712 if (chan
> 0) { input_type
= GL_UNSIGNED_BYTE
; }
713 if (chan
< 0) { input_type
= GL_FLOAT
; }
715 if (output_desc
== GL_ALPHA
) {
716 if (input_desc
== GL_LUMINANCE
)
717 input_desc
= GL_ALPHA
;
718 if (input_desc
== GL_RGB
) {
719 // force a presumably-mono image to alpha
720 // @TODO handle 'preserve_data' case?
721 if (data
&& !preserve_data
&& input_type
== GL_UNSIGNED_BYTE
) {
723 unsigned char *p
= (unsigned char *) data
, *q
= p
;
724 for (i
=0; i
< w
*h
; ++i
) {
725 *q
= (p
[0] + 2*p
[1] + p
[2]) >> 2;
729 input_desc
= GL_ALPHA
;
734 // set undefined input/output based on the other
735 if (input_desc
== STBGL_UNDEFINED
&& output_desc
== STBGL_UNDEFINED
) {
736 input_desc
= output_desc
= GL_RGBA
;
737 } else if (output_desc
== STBGL_UNDEFINED
) {
738 switch (input_desc
) {
741 case GL_LUMINANCE_ALPHA
:
744 output_desc
= input_desc
;
747 output_desc
= GL_INTENSITY
;
751 output_desc
= STBGL_YCOCG
;
753 default: assert(0); return 0;
755 } else if (input_desc
== STBGL_UNDEFINED
) {
756 switch (output_desc
) {
759 case GL_LUMINANCE_ALPHA
:
762 input_desc
= output_desc
;
768 case STBGL_COMPRESSED_RGB_S3TC_DXT1
:
769 case STBGL_COMPRESSED_RGBA_S3TC_DXT3
:
770 case STBGL_COMPRESSED_RGBA_S3TC_DXT5
:
771 input_desc
= GL_RGBA
;
775 if (output_desc
== STBGL_COMPRESSED_RGB_S3TC_DXT1
) {
776 // if input has alpha, force output alpha
777 switch (input_desc
) {
779 case GL_LUMINANCE_ALPHA
:
781 output_desc
= STBGL_COMPRESSED_RGBA_S3TC_DXT5
;
793 case GL_LUMINANCE_ALPHA
:
804 if (pad_to_power_of_two
&& ((w
& (w
-1)) || (h
& (h
-1)))) {
805 if (output_desc
!= STBGL_NO_DOWNLOAD
&& input_type
== GL_UNSIGNED_BYTE
&& chan
> 0) {
806 unsigned char *new_data
;
807 int w2
= w
, h2
= h
, j
;
812 new_data
= malloc(w
* h
* chan
);
813 for (j
=0; j
< h2
; ++j
) {
814 memcpy(new_data
+ j
* w
* chan
, (char *) data
+j
*w2
*chan
, w2
*chan
);
815 memset(new_data
+ (j
* w
+w2
) * chan
, 0, (w
-w2
)*chan
);
818 memset(new_data
+ j
*w
*chan
, 0, w
*chan
);
826 switch (output_desc
) {
827 case STBGL_COMPRESSED_RGB_S3TC_DXT1
:
828 case STBGL_COMPRESSED_RGBA_S3TC_DXT1
:
829 case STBGL_COMPRESSED_RGBA_S3TC_DXT3
:
830 case STBGL_COMPRESSED_RGBA_S3TC_DXT5
:
832 if (has_s3tc
== -1) {
833 has_s3tc
= stbgl_hasExtension("GL_EXT_texture_compression_s3tc");
834 if (has_s3tc
) stbgl__initCompTex();
838 if (output_desc
== STBGL_COMPRESSED_RGB_S3TC_DXT1
)
839 output_desc
= GL_RGB
;
841 output_desc
= GL_RGBA
;
845 if (output_desc
== STBGL_YCOCG
) {
847 output_desc
= GL_RGB
; // @TODO!
848 if (free_data
) free(data
);
853 if (mipmaps
!= STBGL_UNDEFINED
) {
855 case STBGL_UNDEFINED
: filter
= GL_LINEAR_MIPMAP_LINEAR
; break;
856 case GL_NEAREST
: mag_filter
= GL_NEAREST
; filter
= GL_LINEAR_MIPMAP_LINEAR
; break;
857 case GL_LINEAR
: filter
= GL_LINEAR_MIPMAP_NEAREST
; break;
860 if (filter
== STBGL_UNDEFINED
)
866 if (filter
== GL_NEAREST
)
867 mag_filter
= GL_NEAREST
;
869 mag_filter
= GL_LINEAR
;
873 if (wrap_s
== STBGL_UNDEFINED
) wrap_s
= GL_REPEAT
;
874 if (wrap_t
== STBGL_UNDEFINED
) wrap_t
= wrap_s
;
876 // if no texture id, generate one
879 glGenTextures(1, &tex
);
880 if (tex
== 0) { if (free_data
) free(data
); return 0; }
884 if (data
== NULL
&& mipmaps
== STBGL_GEN_MIPMAPS
)
885 mipmaps
= STBGL_MIPMAPS
;
887 if (output_desc
== STBGL_NO_DOWNLOAD
)
888 mipmaps
= STBGL_NO_DOWNLOAD
;
890 glBindTexture(GL_TEXTURE_2D
, texid
);
892 #ifdef STB_COMPRESS_DXT_BLOCK
893 if (!is_compressed
|| !stbgl__CompressedTexImage2DARB
|| output_desc
== STBGL_COMPRESSED_RGBA_S3TC_DXT3
|| data
== NULL
)
897 case STBGL_NO_DOWNLOAD
:
900 case STBGL_UNDEFINED
:
901 // check if actually power-of-two
902 if (non_power_of_two
|| ((w
& (w
-1)) == 0 && (h
& (h
-1)) == 0))
903 glTexImage2D(GL_TEXTURE_2D
, 0, output_desc
, w
, h
, 0, input_desc
, input_type
, data
);
905 gluBuild2DMipmaps(GL_TEXTURE_2D
, output_desc
, w
, h
, input_desc
, input_type
, data
);
906 // not power of two, so use glu to resize (generates mipmaps needlessly)
909 case STBGL_MIPMAPS
: {
911 int size
= input_type
== GL_FLOAT
? sizeof(float) : 1;
912 if (data
== NULL
) size
= 0; // reuse same block of memory for all mipmaps
913 assert((w
& (w
-1)) == 0 && (h
& (h
-1)) == 0); // verify power-of-two
914 while (w
> 1 && h
> 1) {
915 glTexImage2D(GL_TEXTURE_2D
, level
, output_desc
, w
, h
, 0, input_desc
, input_type
, data
);
916 data
= (void *) ((char *) data
+ w
* h
* size
* chan
);
923 case STBGL_GEN_MIPMAPS
:
924 gluBuild2DMipmaps(GL_TEXTURE_2D
, output_desc
, w
, h
, input_desc
, input_type
, data
);
929 if (free_data
) free(data
);
932 #ifdef STB_COMPRESS_DXT_BLOCK
934 uint8
*out
, *rgba
=0, *end_out
, *end_rgba
;
935 int level
= 0, alpha
= (output_desc
!= STBGL_COMPRESSED_RGB_S3TC_DXT1
);
936 int size
= input_type
== GL_FLOAT
? sizeof(float) : 1;
937 int osize
= alpha
? 16 : 8;
938 if (!free_data
&& mipmaps
== STBGL_GEN_MIPMAPS
) {
939 uint8
*temp
= malloc(w
*h
*chan
);
940 if (!temp
) { if (free_data
) free(data
); return 0; }
941 memcpy(temp
, data
, w
*h
*chan
);
942 if (free_data
) free(data
);
946 if (chan
!= 4 || size
!= 1) {
947 rgba
= malloc(w
*h
*4);
949 end_rgba
= rgba
+w
*h
*4;
951 out
= malloc((w
+3)*(h
+3)/16*osize
); // enough storage for the s3tc data
953 end_out
= out
+ ((w
+3)*(h
+3))/16*osize
;
957 stbgl__convert(rgba
, data
, w
*h
, input_desc
, end_rgba
);
958 stbgl__compress(out
, rgba
? rgba
: data
, w
, h
, output_desc
, end_out
);
959 stbgl__CompressedTexImage2DARB(GL_TEXTURE_2D
, level
, output_desc
, w
, h
, 0, ((w
+3)&~3)*((h
+3)&~3)/16*osize
, out
);
960 //glTexImage2D(GL_TEXTURE_2D, level, alpha?GL_RGBA:GL_RGB, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, rgba ? rgba : data);
962 if (mipmaps
== STBGL_UNDEFINED
) break;
963 if (w
<= 1 && h
<= 1) break;
964 if (mipmaps
== STBGL_MIPMAPS
) data
= (void *) ((char *) data
+ w
* h
* size
* chan
);
965 if (mipmaps
== STBGL_GEN_MIPMAPS
) {
966 int w2
= w
>>1, h2
=h
>>1, i
,j
,k
, s
=w
*chan
;
967 uint8
*p
= data
, *q
=data
;
969 for (j
=0; j
< h2
; ++j
) {
970 for (k
=0; k
< chan
; ++k
)
971 *p
++ = (q
[k
] + q
[s
+k
] + 1) >> 1;
975 for (i
=0; i
< w2
; ++i
) {
976 for (k
=0; k
< chan
; ++k
)
977 *p
++ = (q
[k
] + q
[k
+chan
] + 1) >> 1;
981 for (j
=0; j
< h2
; ++j
) {
982 for (i
=0; i
< w2
; ++i
) {
983 for (k
=0; k
< chan
; ++k
)
984 *p
++ = (q
[k
] + q
[k
+chan
] + q
[s
+k
] + q
[s
+k
+chan
] + 2) >> 2;
996 if (rgba
) free(rgba
);
997 #endif // STB_COMPRESS_DXT_BLOCK
1000 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_S
, wrap_s
);
1001 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_T
, wrap_t
);
1002 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, mag_filter
);
1003 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, filter
);
1005 if (free_data
) free(data
);
1009 #endif // STB_DEFINE
1012 #endif //INCLUDE_STB_GL_H
1014 // Extension handling... must be outside the INCLUDE_ brackets
1016 #if defined(STB_GLEXT_DEFINE) || defined(STB_GLEXT_DECLARE)
1018 #ifndef STB_GLEXT_SKIP_DURING_RECURSION
1020 #ifndef GL_GLEXT_VERSION
1022 // First check if glext.h is concatenated on the end of this file
1023 // (if it's concatenated on the beginning, we'll have GL_GLEXT_VERSION)
1025 #define STB_GLEXT_SKIP_DURING_RECURSION
1027 #undef STB_GLEXT_SKIP_DURING_RECURSION
1029 // now check if it's still undefined; if so, try going for it by name;
1030 // if this errors, that's fine, since we can't compile without it
1032 #ifndef GL_GLEXT_VERSION
1037 #define GLARB(a,b) GLE(a##ARB,b##ARB)
1038 #define GLEXT(a,b) GLE(a##EXT,b##EXT)
1039 #define GLNV(a,b) GLE(a##NV ,b##NV)
1040 #define GLATI(a,b) GLE(a##ATI,b##ATI)
1041 #define GLCORE(a,b) GLE(a,b)
1043 #ifdef STB_GLEXT_DEFINE_DECLARE
1044 #define STB_GLEXT_DEFINE STB_GLEXT_DECLARE
1047 #if defined(STB_GLEXT_DECLARE) && defined(STB_GLEXT_DEFINE)
1048 #undef STB_GLEXT_DECLARE
1051 #if defined(STB_GLEXT_DECLARE) && !defined(STB_GLEXT_DEFINE)
1052 #define GLE(a,b) extern PFNGL##b##PROC gl##a;
1058 extern void stbgl_initExtensions(void);
1060 #include STB_GLEXT_DECLARE
1068 #ifndef STB_GLEXT_DEFINE
1069 #error "Header file is screwed up somehow"
1074 #ifndef STB__HAS_WGLPROC
1075 typedef int (__stdcall
*stbgl__voidfunc
)(void);
1076 __declspec(dllimport
) stbgl__voidfunc
wglGetProcAddress(char *);
1079 #define STBGL__GET_FUNC(x) wglGetProcAddress(x)
1086 #define GLE(a,b) PFNGL##b##PROC gl##a;
1087 #include STB_GLEXT_DEFINE
1090 #define GLE(a,b) gl##a = (PFNGL##b##PROC) STBGL__GET_FUNC("gl" #a );
1092 void stbgl_initExtensions(void)
1094 #include STB_GLEXT_DEFINE
1099 #endif // STB_GLEXT_DECLARE
1101 #endif // STB_GLEXT_SKIP
1103 #endif // STB_GLEXT_DEFINE || STB_GLEXT_DECLARE