1 //----------------------------------------------------------------------------
2 // EDGE OpenGL Rendering (Unit batching)
3 //----------------------------------------------------------------------------
4 //
5 // Copyright (c) 1999-2009 The EDGE Team.
6 //
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License
9 // as published by the Free Software Foundation; either version 2
10 // of the License, or (at your option) any later version.
11 //
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU General Public License for more details.
16 //
17 //----------------------------------------------------------------------------
18 //
19 // -AJA- 2000/10/09: Began work on this new unit system.
20 //
21
22 #include "i_defs.h"
23 #include "i_defs_gl.h"
24
25 #include <vector>
26 #include <algorithm>
27
28 #include "epi/image_data.h"
29
30 #include "m_argv.h"
31 #include "r_gldefs.h"
32 #include "r_units.h"
33 #include "z_zone.h"
34
35 #include "r_misc.h"
36 #include "r_image.h"
37 #include "r_texgl.h"
38 #include "r_shader.h"
39
40
41 cvar_c r_colorlighting;
42 cvar_c r_colormaterial;
43
44 cvar_c r_dumbsky;
45 cvar_c r_dumbmulti;
46 cvar_c r_dumbcombine;
47 cvar_c r_dumbclamp;
48
49
50 #define MAX_L_VERT 4096
51 #define MAX_L_UNIT (MAX_L_VERT / 4)
52
53 #define DUMMY_CLAMP 789
54
55
56 // a single unit (polygon, quad, etc) to pass to the GL
57 typedef struct local_gl_unit_s
58 {
59 // unit mode (e.g. GL_POLYGON)
60 GLuint shape;
61
62 // environment modes (GL_REPLACE, GL_MODULATE, GL_DECAL, GL_ADD)
63 GLuint env[2];
64
65 // texture(s) used
66 GLuint tex[2];
67
68 // pass number (multiple pass rendering)
69 int pass;
70
71 // blending flags
72 int blending;
73
74 // range of local vertices
75 int first, count;
76 }
77 local_gl_unit_t;
78
79
80 static local_gl_vert_t local_verts[MAX_L_VERT];
81 static local_gl_unit_t local_units[MAX_L_UNIT];
82
83 static std::vector<local_gl_unit_t *> local_unit_map;
84
85 static int cur_vert;
86 static int cur_unit;
87
88 static bool batch_sort;
89
90
91 //
92 // RGL_InitUnits
93 //
94 // Initialise the unit system. Once-only call.
95 //
RGL_InitUnits(void)96 void RGL_InitUnits(void)
97 {
98 // Run the soft init code
99 RGL_SoftInitUnits();
100 }
101
102 //
103 // RGL_SoftInitUnits
104 //
105 // -ACB- 2004/02/15 Quickly-hacked routine to reinit stuff lost on res change
106 //
RGL_SoftInitUnits()107 void RGL_SoftInitUnits()
108 {
109 }
110
111
112 //
113 // RGL_StartUnits
114 //
115 // Starts a fresh batch of units.
116 //
117 // When 'sort_em' is true, the units will be sorted to keep
118 // texture changes to a minimum. Otherwise, the batch is
119 // drawn in the same order as given.
120 //
RGL_StartUnits(bool sort_em)121 void RGL_StartUnits(bool sort_em)
122 {
123 cur_vert = cur_unit = 0;
124
125 batch_sort = sort_em;
126
127 local_unit_map.resize(MAX_L_UNIT);
128 }
129
130 //
131 // RGL_FinishUnits
132 //
133 // Finishes a batch of units, drawing any that haven't been drawn yet.
134 //
RGL_FinishUnits(void)135 void RGL_FinishUnits(void)
136 {
137 RGL_DrawUnits();
138 }
139
140
myActiveTexture(GLuint id)141 static inline void myActiveTexture(GLuint id)
142 {
143 if (GLEW_VERSION_1_3)
144 glActiveTexture(id);
145 else /* GLEW_ARB_multitexture */
146 glActiveTextureARB(id);
147 }
148
myMultiTexCoord2f(GLuint id,GLfloat s,GLfloat t)149 static inline void myMultiTexCoord2f(GLuint id, GLfloat s, GLfloat t)
150 {
151 if (GLEW_VERSION_1_3)
152 glMultiTexCoord2f(id, s, t);
153 else /* GLEW_ARB_multitexture */
154 glMultiTexCoord2fARB(id, s, t);
155 }
156
157 //
158 // RGL_BeginUnit
159 //
160 // Begin a new unit, with the given parameters (mode and texture ID).
161 // `max_vert' is the maximum expected vertices of the quad/poly (the
162 // actual number can be less, but never more). Returns a pointer to
163 // the first vertex structure. `masked' should be true if the texture
164 // contains "holes" (like sprites). `blended' should be true if the
165 // texture should be blended (like for translucent water or sprites).
166 //
RGL_BeginUnit(GLuint shape,int max_vert,GLuint env1,GLuint tex1,GLuint env2,GLuint tex2,int pass,int blending)167 local_gl_vert_t *RGL_BeginUnit(GLuint shape, int max_vert,
168 GLuint env1, GLuint tex1,
169 GLuint env2, GLuint tex2,
170 int pass, int blending)
171 {
172 local_gl_unit_t *unit;
173
174 SYS_ASSERT(max_vert > 0);
175 SYS_ASSERT(pass >= 0);
176
177 SYS_ASSERT((blending & BL_CULL_BOTH) != BL_CULL_BOTH);
178
179 // check we have enough space left
180 if (cur_vert + max_vert > MAX_L_VERT || cur_unit >= MAX_L_UNIT)
181 {
182 RGL_DrawUnits();
183 }
184
185 unit = local_units + cur_unit;
186
187 if (env1 == ENV_NONE) tex1 = 0;
188 if (env2 == ENV_NONE) tex2 = 0;
189
190 unit->shape = shape;
191 unit->env[0] = env1;
192 unit->env[1] = env2;
193 unit->tex[0] = tex1;
194 unit->tex[1] = tex2;
195
196 unit->pass = pass;
197 unit->blending = blending;
198 unit->first = cur_vert; // count set later
199
200 return local_verts + cur_vert;
201 }
202
203 //
204 // RGL_EndUnit
205 //
RGL_EndUnit(int actual_vert)206 void RGL_EndUnit(int actual_vert)
207 {
208 local_gl_unit_t *unit;
209
210 SYS_ASSERT(actual_vert > 0);
211
212 unit = local_units + cur_unit;
213
214 unit->count = actual_vert;
215
216 // adjust colors (for special effects)
217 for (int i = 0; i < actual_vert; i++)
218 {
219 local_gl_vert_t *v = &local_verts[cur_vert + i];
220
221 v->rgba[0] *= ren_red_mul;
222 v->rgba[1] *= ren_grn_mul;
223 v->rgba[2] *= ren_blu_mul;
224 }
225
226 cur_vert += actual_vert;
227 cur_unit++;
228
229 SYS_ASSERT(cur_vert <= MAX_L_VERT);
230 SYS_ASSERT(cur_unit <= MAX_L_UNIT);
231 }
232
233
234 struct Compare_Unit_pred
235 {
operator ()Compare_Unit_pred236 inline bool operator() (const local_gl_unit_t *A, const local_gl_unit_t *B) const
237 {
238 if (A->pass != B->pass)
239 return A->pass < B->pass;
240
241 if (A->tex[0] != B->tex[0])
242 return A->tex[0] < B->tex[0];
243
244 if (A->tex[1] != B->tex[1])
245 return A->tex[1] < B->tex[1];
246
247 if (A->env[0] != B->env[0])
248 return A->env[0] < B->env[0];
249
250 if (A->env[1] != B->env[1])
251 return A->env[1] < B->env[1];
252
253 return A->blending < B->blending;
254 }
255 };
256
EnableCustomEnv(GLuint env,bool enable)257 static void EnableCustomEnv(GLuint env, bool enable)
258 {
259 switch (env)
260 {
261 case ENV_SKIP_RGB:
262 if (enable)
263 {
264 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
265 glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE);
266 glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS);
267 }
268 else
269 {
270 /* no need to modify TEXTURE_ENV_MODE */
271 glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);
272 glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE);
273 }
274 break;
275
276 default:
277 I_Error("INTERNAL ERROR: no such custom env: %08x\n", env);
278 }
279 }
280
RGL_SendRawVector(const local_gl_vert_t * V)281 static inline void RGL_SendRawVector(const local_gl_vert_t *V)
282 {
283 if (r_colormaterial.d || ! r_colorlighting.d)
284 glColor4fv(V->rgba);
285 else
286 {
287 glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, V->rgba);
288 glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, V->rgba);
289 }
290
291 myMultiTexCoord2f(GL_TEXTURE0, V->texc[0].x, V->texc[0].y);
292 myMultiTexCoord2f(GL_TEXTURE1, V->texc[1].x, V->texc[1].y);
293
294 glNormal3f(V->normal.x, V->normal.y, V->normal.z);
295 glEdgeFlag(V->edge);
296
297 // vertex must be last
298 glVertex3f(V->pos.x, V->pos.y, V->pos.z);
299 }
300
301 //
302 // RGL_DrawUnits
303 //
304 // Forces the set of current units to be drawn. This call is
305 // optional (it never _needs_ to be called by client code).
306 //
RGL_DrawUnits(void)307 void RGL_DrawUnits(void)
308 {
309 if (cur_unit == 0)
310 return;
311
312 GLuint active_tex[2] = { 0, 0 };
313 GLuint active_env[2] = { 0, 0 };
314
315 int active_pass = 0;
316 int active_blending = 0;
317
318 for (int i=0; i < cur_unit; i++)
319 local_unit_map[i] = & local_units[i];
320
321 if (batch_sort)
322 {
323 std::sort(local_unit_map.begin(),
324 local_unit_map.begin() + cur_unit,
325 Compare_Unit_pred());
326 }
327
328 glDisable(GL_TEXTURE_2D);
329 glDisable(GL_ALPHA_TEST);
330 glDisable(GL_BLEND);
331
332 glAlphaFunc(GL_GREATER, 0);
333
334 glPolygonOffset(0, 0);
335
336
337 for (int j=0; j < cur_unit; j++)
338 {
339 local_gl_unit_t *unit = local_unit_map[j];
340
341 SYS_ASSERT(unit->count > 0);
342
343 // detect changes in texture/alpha/blending state
344
345 if (active_pass != unit->pass)
346 {
347 active_pass = unit->pass;
348
349 glPolygonOffset(0, -active_pass);
350 }
351
352 if ((active_blending ^ unit->blending) & (BL_Masked | BL_Less))
353 {
354 if (unit->blending & BL_Less)
355 {
356 // glAlphaFunc is updated below, because the alpha
357 // value can change from unit to unit while the
358 // BL_Less flag remains set.
359 glEnable(GL_ALPHA_TEST);
360 }
361 else if (unit->blending & BL_Masked)
362 {
363 glEnable(GL_ALPHA_TEST);
364 glAlphaFunc(GL_GREATER, 0);
365 }
366 else
367 glDisable(GL_ALPHA_TEST);
368 }
369
370 if ((active_blending ^ unit->blending) & (BL_Alpha | BL_Add))
371 {
372 if (unit->blending & BL_Add)
373 {
374 glEnable(GL_BLEND);
375 glBlendFunc(GL_SRC_ALPHA, GL_ONE);
376 }
377 else if (unit->blending & BL_Alpha)
378 {
379 glEnable(GL_BLEND);
380 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
381 }
382 else
383 glDisable(GL_BLEND);
384 }
385
386 if ((active_blending ^ unit->blending) & BL_CULL_BOTH)
387 {
388 if (unit->blending & BL_CULL_BOTH)
389 {
390 glEnable(GL_CULL_FACE);
391 glCullFace((unit->blending & BL_CullFront) ? GL_FRONT : GL_BACK);
392 }
393 else
394 glDisable(GL_CULL_FACE);
395 }
396
397 if ((active_blending ^ unit->blending) & BL_NoZBuf)
398 {
399 glDepthMask((unit->blending & BL_NoZBuf) ? GL_FALSE : GL_TRUE);
400 }
401
402 active_blending = unit->blending;
403
404 if (active_blending & BL_Less)
405 {
406 // NOTE: assumes alpha is constant over whole polygon
407 float a = local_verts[unit->first].rgba[3];
408
409 glAlphaFunc(GL_GREATER, a * 0.66f);
410 }
411
412 for (int t=1; t >= 0; t--)
413 {
414 myActiveTexture(GL_TEXTURE0 + t);
415
416 if (active_tex[t] != unit->tex[t])
417 {
418 if (unit->tex[t] == 0)
419 glDisable(GL_TEXTURE_2D);
420 else if (active_tex[t] == 0)
421 glEnable(GL_TEXTURE_2D);
422
423 if (unit->tex[t] != 0)
424 glBindTexture(GL_TEXTURE_2D, unit->tex[t]);
425
426 active_tex[t] = unit->tex[t];
427 }
428
429 if (active_env[t] != unit->env[t])
430 {
431 if (active_env[t] >= CUSTOM_ENV_BEGIN &&
432 active_env[t] <= CUSTOM_ENV_END)
433 {
434 EnableCustomEnv(active_env[t], false);
435 }
436
437 if (unit->env[t] >= CUSTOM_ENV_BEGIN &&
438 unit->env[t] <= CUSTOM_ENV_END)
439 {
440 EnableCustomEnv(unit->env[t], true);
441 }
442 else if (unit->env[t] != ENV_NONE)
443 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, unit->env[t]);
444
445 active_env[t] = unit->env[t];
446 }
447 }
448
449 GLint old_clamp = DUMMY_CLAMP;
450
451 if ((active_blending & BL_ClampY) && active_tex[0] != 0)
452 {
453 glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, &old_clamp);
454
455 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,
456 r_dumbclamp.d ? GL_CLAMP : GL_CLAMP_TO_EDGE);
457 }
458
459 glBegin(unit->shape);
460
461 for (int v_idx=0; v_idx < unit->count; v_idx++)
462 {
463 RGL_SendRawVector(local_verts + unit->first + v_idx);
464 }
465
466 glEnd();
467
468 // restore the clamping mode
469 if (old_clamp != DUMMY_CLAMP)
470 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, old_clamp);
471 }
472
473 // all done
474 cur_vert = cur_unit = 0;
475
476 glPolygonOffset(0, 0);
477
478 for (int t=1; t >=0; t--)
479 {
480 myActiveTexture(GL_TEXTURE0 + t);
481
482 if (active_env[t] >= CUSTOM_ENV_BEGIN &&
483 active_env[t] <= CUSTOM_ENV_END)
484 {
485 EnableCustomEnv(active_env[t], false);
486 }
487 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
488 glDisable(GL_TEXTURE_2D);
489 }
490
491 glDepthMask(GL_TRUE);
492 glCullFace(GL_BACK);
493
494 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
495 glAlphaFunc(GL_GREATER, 0);
496
497 glDisable(GL_ALPHA_TEST);
498 glDisable(GL_BLEND);
499 glDisable(GL_CULL_FACE);
500 }
501
502
503 //--- editor settings ---
504 // vi:ts=4:sw=4:noexpandtab
505