1 // rendercubes.cpp: sits in between worldrender.cpp and rendergl.cpp and fills the vertex array for different cube surfaces.
2
3 #include "cube.h"
4
5 vector<vertex> verts;
6
7 void finishstrips();
8
setupstrips()9 void setupstrips()
10 {
11 finishstrips();
12
13 glEnableClientState(GL_VERTEX_ARRAY);
14 glEnableClientState(GL_COLOR_ARRAY);
15 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
16
17 vertex *buf = verts.getbuf();
18 glVertexPointer(3, GL_FLOAT, sizeof(vertex), &buf->x);
19 glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(vertex), &buf->r);
20 glTexCoordPointer(2, GL_FLOAT, sizeof(vertex), &buf->u);
21 }
22
23 struct strips { vector<GLint> first; vector<GLsizei> count; };
24
25 struct stripbatch
26 {
27 int tex;
28 strips tris, tristrips, quads;
29 };
30
31 stripbatch skystrips = { DEFAULT_SKY };
32 stripbatch stripbatches[256];
33 uchar renderedtex[256];
34 int renderedtexs = 0;
35
36 extern int ati_mda_bug;
37
38 #define RENDERSTRIPS(strips, type) \
39 if(strips.first.length()) \
40 { \
41 if(hasMDA && !ati_mda_bug) glMultiDrawArrays_(type, strips.first.getbuf(), strips.count.getbuf(), strips.first.length()); \
42 else loopv(strips.first) glDrawArrays(type, strips.first[i], strips.count[i]); \
43 strips.first.setsize(0); \
44 strips.count.setsize(0); \
45 }
46
renderstripssky()47 void renderstripssky()
48 {
49 if(skystrips.tris.first.empty() && skystrips.tristrips.first.empty() && skystrips.quads.first.empty()) return;
50 glDisable(GL_TEXTURE_2D);
51 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
52 RENDERSTRIPS(skystrips.tris, GL_TRIANGLES);
53 RENDERSTRIPS(skystrips.tristrips, GL_TRIANGLE_STRIP);
54 RENDERSTRIPS(skystrips.quads, GL_QUADS);
55 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
56 glEnable(GL_TEXTURE_2D);
57 }
58
renderstrips()59 void renderstrips()
60 {
61 loopj(renderedtexs)
62 {
63 stripbatch &sb = stripbatches[j];
64 glBindTexture(GL_TEXTURE_2D, lookupworldtexture(sb.tex)->id);
65 RENDERSTRIPS(sb.tris, GL_TRIANGLES);
66 RENDERSTRIPS(sb.tristrips, GL_TRIANGLE_STRIP);
67 RENDERSTRIPS(sb.quads, GL_QUADS);
68 }
69 renderedtexs = 0;
70
71 glDisableClientState(GL_VERTEX_ARRAY);
72 glDisableClientState(GL_COLOR_ARRAY);
73 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
74 }
75
addstrip(int type,int tex,int start,int n)76 void addstrip(int type, int tex, int start, int n)
77 {
78 stripbatch *sb = NULL;
79 if(tex==DEFAULT_SKY)
80 {
81 if(minimap) return;
82 sb = &skystrips;
83 loopi(n) skyfloor = min(skyfloor, verts[start + i].z);
84 }
85 else
86 {
87 sb = &stripbatches[renderedtex[tex]];
88 if(sb->tex!=tex || sb>=&stripbatches[renderedtexs])
89 {
90 sb = &stripbatches[renderedtex[tex] = renderedtexs++];
91 sb->tex = tex;
92 }
93 }
94 strips &s = (type==GL_QUADS ? sb->quads : (type==GL_TRIANGLES ? sb->tris : sb->tristrips));
95 if(type!=GL_TRIANGLE_STRIP && s.first.length() && s.first.last()+s.count.last() == start)
96 {
97 s.count.last() += n;
98 return;
99 }
100 s.first.add(start);
101 s.count.add(n);
102 }
103
104 // generating the actual vertices is done dynamically every frame and sits at the
105 // leaves of all these functions, and are part of the cpu bottleneck on really slow
106 // machines, hence the macros.
107
108 #define vert(v1, v2, v3, ls, t1, t2) \
109 { \
110 vertex &v = verts.add(); \
111 v.u = t1; v.v = t2; \
112 v.x = (float)(v1); v.y = (float)(v2); v.z = (float)(v3); \
113 v.r = ls->r; v.g = ls->g; v.b = ls->b; v.a = 255; \
114 }
115
116 enum
117 {
118 STRIP_FLOOR = 1,
119 STRIP_DELTA,
120 STRIP_WALL
121 };
122
123 int nquads;
124
125 // for testing purpose. UNDOME on release.
126 const float texturescale = 32.0f;
127 //VARP(texturescale, 16, 32, 64);
128
129 #define TEXTURESCALE (float(texturescale) * ((uniformtexres && t->scale>1.0f) ? 1.0f : t->scale))
130
131 int striptype = 0, striptex, oh, oy, ox, odir; // the o* vars are used by the stripification
132 int ol1r, ol1g, ol1b, ol2r, ol2g, ol2b;
133 float ofloor, oceil;
134 bool ohf;
135 int firstindex;
136 bool showm = false;
137
showmip()138 void showmip() { showm = !showm; }
mipstats(int a,int b,int c)139 void mipstats(int a, int b, int c) { if(showm) conoutf("1x1/2x2/4x4: %d / %d / %d", a, b, c); }
140
141 COMMAND(showmip, "");
142
143 VAR(mergestrips, 0, 1, 1);
144
145 #define stripend(verts) \
146 if(striptype) \
147 { \
148 int type = GL_TRIANGLE_STRIP, len = verts.length()-firstindex; \
149 if(mergestrips) switch(len) \
150 { \
151 case 3: type = GL_TRIANGLES; break; \
152 case 4: type = GL_QUADS; swap(verts.last(), verts[verts.length()-2]); break; \
153 } \
154 addstrip(type, striptex, firstindex, len); \
155 striptype = 0; \
156 }
157
finishstrips()158 void finishstrips() { stripend(verts); }
159
160 sqr sbright, sdark;
161 VARP(lighterror, 1, 4, 100);
162
render_flat(int wtex,int x,int y,int size,int h,sqr * l1,sqr * l4,sqr * l3,sqr * l2,bool isceil)163 void render_flat(int wtex, int x, int y, int size, int h, sqr *l1, sqr *l4, sqr *l3, sqr *l2, bool isceil) // floor/ceil quads
164 {
165 if(showm) { l3 = l1 = &sbright; l4 = l2 = &sdark; }
166
167 Texture *t = lookupworldtexture(wtex);
168 float xf = TEXTURESCALE/t->xs;
169 float yf = TEXTURESCALE/t->ys;
170 float xs = size*xf;
171 float ys = size*yf;
172 float xo = xf*x;
173 float yo = yf*y;
174
175 bool first = striptype!=STRIP_FLOOR || x!=ox+size || striptex!=wtex || h!=oh || y!=oy;
176
177 if(first) // start strip here
178 {
179 stripend(verts);
180 firstindex = verts.length();
181 striptex = wtex;
182 oh = h;
183 oy = y;
184 striptype = STRIP_FLOOR;
185 if(isceil)
186 {
187 vert(x, y, h, l1, xo, yo);
188 vert(x, y+size, h, l2, xo, yo+ys);
189 }
190 else
191 {
192 vert(x, y+size, h, l2, xo, yo+ys);
193 vert(x, y, h, l1, xo, yo);
194 }
195 ol1r = l1->r;
196 ol1g = l1->g;
197 ol1b = l1->b;
198 ol2r = l2->r;
199 ol2g = l2->g;
200 ol2b = l2->b;
201 }
202 else // continue strip
203 {
204 int lighterr = lighterror*2;
205 if((iabs(ol1r-l3->r)<lighterr && iabs(ol2r-l4->r)<lighterr // skip vertices if light values are close enough
206 && iabs(ol1g-l3->g)<lighterr && iabs(ol2g-l4->g)<lighterr
207 && iabs(ol1b-l3->b)<lighterr && iabs(ol2b-l4->b)<lighterr) || !wtex)
208 {
209 verts.setsize(verts.length()-2);
210 nquads--;
211 }
212 else
213 {
214 uchar *p1 = (uchar *)(&verts[verts.length()-1].r);
215 ol1r = p1[0];
216 ol1g = p1[1];
217 ol1b = p1[2];
218 uchar *p2 = (uchar *)(&verts[verts.length()-2].r);
219 ol2r = p2[0];
220 ol2g = p2[1];
221 ol2b = p2[2];
222 }
223 }
224
225 if(isceil)
226 {
227 vert(x+size, y, h, l4, xo+xs, yo);
228 vert(x+size, y+size, h, l3, xo+xs, yo+ys);
229 }
230 else
231 {
232 vert(x+size, y+size, h, l3, xo+xs, yo+ys);
233 vert(x+size, y, h, l4, xo+xs, yo);
234 }
235
236 ox = x;
237 nquads++;
238 }
239
render_flatdelta(int wtex,int x,int y,int size,float h1,float h4,float h3,float h2,sqr * l1,sqr * l4,sqr * l3,sqr * l2,bool isceil)240 void render_flatdelta(int wtex, int x, int y, int size, float h1, float h4, float h3, float h2, sqr *l1, sqr *l4, sqr *l3, sqr *l2, bool isceil) // floor/ceil quads on a slope
241 {
242 if(showm) { l3 = l1 = &sbright; l4 = l2 = &sdark; }
243
244 Texture *t = lookupworldtexture(wtex);
245 float xf = TEXTURESCALE/t->xs;
246 float yf = TEXTURESCALE/t->ys;
247 float xs = size*xf;
248 float ys = size*yf;
249 float xo = xf*x;
250 float yo = yf*y;
251
252 bool first = striptype!=STRIP_DELTA || x!=ox+size || striptex!=wtex || y!=oy;
253
254 if(first)
255 {
256 stripend(verts);
257 firstindex = verts.length();
258 striptex = wtex;
259 oy = y;
260 striptype = STRIP_DELTA;
261 if(isceil)
262 {
263 vert(x, y, h1, l1, xo, yo);
264 vert(x, y+size, h2, l2, xo, yo+ys);
265 }
266 else
267 {
268 vert(x, y+size, h2, l2, xo, yo+ys);
269 vert(x, y, h1, l1, xo, yo);
270 }
271 }
272
273 if(isceil)
274 {
275 vert(x+size, y, h4, l4, xo+xs, yo);
276 vert(x+size, y+size, h3, l3, xo+xs, yo+ys);
277 }
278 else
279 {
280 vert(x+size, y+size, h3, l3, xo+xs, yo+ys);
281 vert(x+size, y, h4, l4, xo+xs, yo);
282 }
283
284 ox = x;
285 nquads++;
286 }
287
render_2tris(sqr * h,sqr * s,int x1,int y1,int x2,int y2,int x3,int y3,sqr * l1,sqr * l2,sqr * l3)288 void render_2tris(sqr *h, sqr *s, int x1, int y1, int x2, int y2, int x3, int y3, sqr *l1, sqr *l2, sqr *l3) // floor/ceil tris on a corner cube
289 {
290 stripend(verts);
291
292 Texture *t = lookupworldtexture(h->ftex);
293 float xf = TEXTURESCALE/t->xs;
294 float yf = TEXTURESCALE/t->ys;
295 vert(x1, y1, h->floor, l1, xf*x1, yf*y1);
296 vert(x2, y2, h->floor, l2, xf*x2, yf*y2);
297 vert(x3, y3, h->floor, l3, xf*x3, yf*y3);
298 addstrip(mergestrips ? GL_TRIANGLES : GL_TRIANGLE_STRIP, h->ftex, verts.length()-3, 3);
299
300 t = lookupworldtexture(h->ctex);
301 xf = TEXTURESCALE/t->xs;
302 yf = TEXTURESCALE/t->ys;
303
304 vert(x3, y3, h->ceil, l3, xf*x3, yf*y3);
305 vert(x2, y2, h->ceil, l2, xf*x2, yf*y2);
306 vert(x1, y1, h->ceil, l1, xf*x1, yf*y1);
307 addstrip(mergestrips ? GL_TRIANGLES : GL_TRIANGLE_STRIP, h->ctex, verts.length()-3, 3);
308 nquads++;
309 }
310
render_tris(int x,int y,int size,bool topleft,sqr * h1,sqr * h2,sqr * s,sqr * t,sqr * u,sqr * v)311 void render_tris(int x, int y, int size, bool topleft,
312 sqr *h1, sqr *h2, sqr *s, sqr *t, sqr *u, sqr *v)
313 {
314 if(topleft)
315 {
316 if(h1) render_2tris(h1, s, x+size, y+size, x, y+size, x, y, u, v, s);
317 if(h2) render_2tris(h2, s, x, y, x+size, y, x+size, y+size, s, t, v);
318 }
319 else
320 {
321 if(h1) render_2tris(h1, s, x, y, x+size, y, x, y+size, s, t, u);
322 if(h2) render_2tris(h2, s, x+size, y, x+size, y+size, x, y+size, t, u, v);
323 }
324 }
325
render_square(int wtex,float floor1,float floor2,float ceil1,float ceil2,int x1,int y1,int x2,int y2,int size,sqr * l1,sqr * l2,bool flip,int dir)326 void render_square(int wtex, float floor1, float floor2, float ceil1, float ceil2, int x1, int y1, int x2, int y2, int size, sqr *l1, sqr *l2, bool flip, int dir) // wall quads
327 {
328 if(showm) { l1 = &sbright; l2 = &sdark; }
329
330 Texture *t = lookupworldtexture(wtex);
331 float xf = TEXTURESCALE/t->xs;
332 float yf = -TEXTURESCALE/t->ys;
333 float xs = size*xf;
334 float xo = xf*(x1==x2 ? min(y1,y2) : min(x1,x2));
335
336 bool first = striptype!=STRIP_WALL || striptex!=wtex || ox!=x1 || oy!=y1 || ofloor!=floor1 || oceil!=ceil1 || odir!=dir,
337 hf = floor1!=floor2 || ceil1!=ceil2;
338
339 if(first)
340 {
341 stripend(verts);
342 firstindex = verts.length();
343 striptex = wtex;
344 striptype = STRIP_WALL;
345
346 if(!flip)
347 {
348 vert(x1, y1, ceil1, l1, xo, yf*ceil1);
349 vert(x1, y1, floor1, l1, xo, yf*floor1);
350 }
351 else
352 {
353 vert(x1, y1, floor1, l1, xo, yf*floor1);
354 vert(x1, y1, ceil1, l1, xo, yf*ceil1);
355 }
356 ol1r = l1->r;
357 ol1g = l1->g;
358 ol1b = l1->b;
359 }
360 else // continue strip
361 {
362 int lighterr = lighterror*2;
363 if((!hf && !ohf)
364 && ((iabs(ol1r-l2->r)<lighterr && iabs(ol1g-l2->g)<lighterr && iabs(ol1b-l2->b)<lighterr) || !wtex)) // skip vertices if light values are close enough
365 {
366 verts.setsize(verts.length()-2);
367 nquads--;
368 }
369 else
370 {
371 uchar *p1 = (uchar *)(&verts[verts.length()-1].r);
372 ol1r = p1[0];
373 ol1g = p1[1];
374 ol1b = p1[2];
375 }
376 }
377
378 if(!flip)
379 {
380 vert(x2, y2, ceil2, l2, xo+xs, yf*ceil2);
381 vert(x2, y2, floor2, l2, xo+xs, yf*floor2);
382 }
383 else
384 {
385 vert(x2, y2, floor2, l2, xo+xs, yf*floor2);
386 vert(x2, y2, ceil2, l2, xo+xs, yf*ceil2);
387 }
388
389 ox = x2;
390 oy = y2;
391 ofloor = floor2;
392 oceil = ceil2;
393 odir = dir;
394 ohf = hf;
395 nquads++;
396 }
397
resetcubes()398 void resetcubes()
399 {
400 verts.setsize(0);
401
402 striptype = 0;
403 nquads = 0;
404
405 sbright.r = sbright.g = sbright.b = 255;
406 sdark.r = sdark.g = sdark.b = 0;
407
408 resetwater();
409 }
410
411 struct shadowvertex { float u, v, x, y, z; };
412 vector<shadowvertex> shadowverts;
413
resetshadowverts()414 static void resetshadowverts()
415 {
416 shadowverts.setsize(0);
417
418 striptype = 0;
419 }
420
rendershadowstrips()421 static void rendershadowstrips()
422 {
423 glEnableClientState(GL_VERTEX_ARRAY);
424 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
425
426 shadowvertex *buf = shadowverts.getbuf();
427 glVertexPointer(3, GL_FLOAT, sizeof(shadowvertex), &buf->x);
428 glTexCoordPointer(2, GL_FLOAT, sizeof(shadowvertex), &buf->u);
429
430 loopj(renderedtexs)
431 {
432 stripbatch &sb = stripbatches[j];
433 RENDERSTRIPS(sb.tris, GL_TRIANGLES);
434 RENDERSTRIPS(sb.tristrips, GL_TRIANGLE_STRIP);
435 RENDERSTRIPS(sb.quads, GL_QUADS);
436 }
437 renderedtexs = 0;
438
439 glDisableClientState(GL_VERTEX_ARRAY);
440 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
441
442 xtraverts += shadowverts.length();
443 }
444
445 #define shadowvert(v1, v2, v3) \
446 { \
447 shadowvertex &v = shadowverts.add(); \
448 v.x = (float)(v1); v.y = (float)(v2); v.z = (float)(v3); \
449 }
450
rendershadow_tri(sqr * h,int x1,int y1,int x2,int y2,int x3,int y3)451 void rendershadow_tri(sqr *h, int x1, int y1, int x2, int y2, int x3, int y3) // floor tris on a corner cube
452 {
453 stripend(shadowverts);
454
455 shadowvert(x1, y1, h->floor);
456 shadowvert(x2, y2, h->floor);
457 shadowvert(x3, y3, h->floor);
458 addstrip(mergestrips ? GL_TRIANGLES : GL_TRIANGLE_STRIP, DEFAULT_FLOOR, shadowverts.length()-3, 3);
459 }
460
rendershadow_tris(int x,int y,bool topleft,sqr * h1,sqr * h2)461 void rendershadow_tris(int x, int y, bool topleft, sqr *h1, sqr *h2)
462 {
463 if(topleft)
464 {
465 if(h1) rendershadow_tri(h1, x+1, y+1, x, y+1, x, y);
466 if(h2) rendershadow_tri(h2, x, y, x+1, y, x+1, y+1);
467 }
468 else
469 {
470 if(h1) rendershadow_tri(h1, x, y, x+1, y, x, y+1);
471 if(h2) rendershadow_tri(h2, x+1, y, x+1, y+1, x, y+1);
472 }
473 }
474
rendershadow_flat(int x,int y,int h)475 static void rendershadow_flat(int x, int y, int h) // floor quads
476 {
477 bool first = striptype!=STRIP_FLOOR || x!=ox+1 || h!=oh || y!=oy;
478
479 if(first) // start strip here
480 {
481 stripend(shadowverts);
482 firstindex = shadowverts.length();
483 striptex = DEFAULT_FLOOR;
484 oh = h;
485 oy = y;
486 striptype = STRIP_FLOOR;
487 shadowvert(x, y+1, h);
488 shadowvert(x, y, h);
489 }
490 else // continue strip
491 {
492 shadowverts.setsize(shadowverts.length()-2);
493 }
494
495 shadowvert(x+1, y+1, h);
496 shadowvert(x+1, y, h);
497
498 ox = x;
499 }
500
rendershadow_flatdelta(int x,int y,float h1,float h4,float h3,float h2)501 static void rendershadow_flatdelta(int x, int y, float h1, float h4, float h3, float h2) // floor quads on a slope
502 {
503 bool first = striptype!=STRIP_DELTA || x!=ox+1 || y!=oy;
504
505 if(first)
506 {
507 stripend(shadowverts);
508 firstindex = shadowverts.length();
509 striptex = DEFAULT_FLOOR;
510 oy = y;
511 striptype = STRIP_DELTA;
512 shadowvert(x, y+1, h2);
513 shadowvert(x, y, h1);
514 }
515
516 shadowvert(x+1, y+1, h3);
517 shadowvert(x+1, y, h4);
518
519 ox = x;
520 }
521
rendershadow(int x,int y,int xs,int ys,const vec & texgenS,const vec & texgenT)522 void rendershadow(int x, int y, int xs, int ys, const vec &texgenS, const vec &texgenT)
523 {
524 x = max(x, 1);
525 y = max(y, 1);
526 xs = min(xs, ssize-1);
527 ys = min(ys, ssize-1);
528
529 resetshadowverts();
530
531 #define df(x) s->floor-(x->vdelta/4.0f)
532
533 sqr *w = wmip[0];
534 for(int yy = y; yy<ys; yy++) for(int xx = x; xx<xs; xx++)
535 {
536 sqr *s = SW(w,xx,yy);
537 if(s->type==SPACE || s->type==CHF)
538 {
539 rendershadow_flat(xx, yy, s->floor);
540 }
541 else if(s->type==FHF)
542 {
543 sqr *t = SW(s,1,0), *u = SW(s,1,1), *v = SW(s,0,1);
544 rendershadow_flatdelta(xx, yy, df(s), df(t), df(u), df(v));
545 }
546 else if(s->type==CORNER)
547 {
548 sqr *t = SW(s,1,0), *v = SW(s,0,1), *w = SW(s,0,-1), *z = SW(s,-1,0);
549 bool topleft = true;
550 sqr *h1 = NULL, *h2 = NULL;
551 if(SOLID(z))
552 {
553 if(SOLID(w)) { h2 = s; topleft = false; }
554 else if(SOLID(v)) { h2 = s; }
555 }
556 else if(SOLID(t))
557 {
558 if(SOLID(w)) { h1 = s; }
559 else if(SOLID(v)) { h1 = s; topleft = false; }
560 }
561 else
562 {
563 bool wv = w->ceil-w->floor < v->ceil-v->floor;
564 if(z->ceil-z->floor < t->ceil-t->floor)
565 {
566 if(wv) { h1 = s; h2 = v; topleft = false; }
567 else { h1 = s; h2 = w; }
568 }
569 else
570 {
571 if(wv) { h2 = s; h1 = v; }
572 else { h2 = s; h1 = w; topleft = false; }
573 }
574 }
575 rendershadow_tris(xx, yy, topleft, h1, h2);
576 }
577 }
578
579 stripend(shadowverts);
580
581 for(shadowvertex *v = shadowverts.getbuf(), *end = &v[shadowverts.length()]; v < end; v++)
582 {
583 float vx = v->x, vy = v->y;
584 v->u = vx*texgenS.x + vy*texgenS.y + texgenS.z;
585 v->v = vx*texgenT.x + vy*texgenT.y + texgenT.z;
586 }
587
588 rendershadowstrips();
589 }
590
591