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