// rendercubes.cpp: sits in between worldrender.cpp and rendergl.cpp and fills the vertex array for different cube surfaces. #include "cube.h" vector verts; void finishstrips(); void setupstrips() { finishstrips(); glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_COLOR_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); vertex *buf = verts.getbuf(); glVertexPointer(3, GL_FLOAT, sizeof(vertex), &buf->x); glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(vertex), &buf->r); glTexCoordPointer(2, GL_FLOAT, sizeof(vertex), &buf->u); } struct strips { vector first; vector count; }; struct stripbatch { int tex; strips tris, tristrips, quads; }; stripbatch skystrips = { DEFAULT_SKY }; stripbatch stripbatches[256]; uchar renderedtex[256]; int renderedtexs = 0; #define RENDERSTRIPS(strips, type) \ if(strips.first.length()) \ { \ if(hasMDA) glMultiDrawArrays_(type, strips.first.getbuf(), strips.count.getbuf(), strips.first.length()); \ else loopv(strips.first) glDrawArrays(type, strips.first[i], strips.count[i]); \ strips.first.setsizenodelete(0); \ strips.count.setsizenodelete(0); \ } void renderstripssky() { if(skystrips.tris.first.empty() && skystrips.tristrips.first.empty() && skystrips.quads.first.empty()) return; int xs, ys; glBindTexture(GL_TEXTURE_2D, lookuptexture(DEFAULT_SKY, xs, ys)); RENDERSTRIPS(skystrips.tris, GL_TRIANGLES); RENDERSTRIPS(skystrips.tristrips, GL_TRIANGLE_STRIP); RENDERSTRIPS(skystrips.quads, GL_QUADS); } void renderstrips() { int xs, ys; loopj(renderedtexs) { stripbatch &sb = stripbatches[j]; glBindTexture(GL_TEXTURE_2D, lookuptexture(sb.tex, xs, ys)); RENDERSTRIPS(sb.tris, GL_TRIANGLES); RENDERSTRIPS(sb.tristrips, GL_TRIANGLE_STRIP); RENDERSTRIPS(sb.quads, GL_QUADS); } renderedtexs = 0; glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_COLOR_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); } void addstrip(int type, int tex, int start, int n) { stripbatch *sb = NULL; if(tex==DEFAULT_SKY) { if(minimap) return; sb = &skystrips; } else { sb = &stripbatches[renderedtex[tex]]; if(sb->tex!=tex || sb>=&stripbatches[renderedtexs]) { sb = &stripbatches[renderedtex[tex] = renderedtexs++]; sb->tex = tex; } } strips &s = (type==GL_QUADS ? sb->quads : (type==GL_TRIANGLES ? sb->tris : sb->tristrips)); if(type!=GL_TRIANGLE_STRIP && s.first.length() && s.first.last()+s.count.last() == start) { s.count.last() += n; return; } s.first.add(start); s.count.add(n); } // generating the actual vertices is done dynamically every frame and sits at the // leaves of all these functions, and are part of the cpu bottleneck on really slow // machines, hence the macros. #define vert(v1, v2, v3, ls, t1, t2) { \ vertex &v = verts.add(); \ v.u = t1; v.v = t2; \ v.x = (float)(v1); v.y = (float)(v2); v.z = (float)(v3); \ v.r = ls->r; v.g = ls->g; v.b = ls->b; v.a = 255; \ } int nquads; const float TEXTURESCALE = 32.0f; bool floorstrip = false, deltastrip = false; int oh, oy, ox, striptex; // the o* vars are used by the stripification int ol3r, ol3g, ol3b, ol4r, ol4g, ol4b; int firstindex; bool showm = false; void showmip() { showm = !showm; } void mipstats(int a, int b, int c) { if(showm) conoutf("1x1/2x2/4x4: %d / %d / %d", a, b, c); } COMMAND(showmip, ARG_NONE); VAR(mergestrips, 0, 1, 1); #define stripend() \ if(floorstrip || deltastrip) { \ int type = GL_TRIANGLE_STRIP, len = verts.length()-firstindex; \ if(mergestrips) switch(len) { \ case 3: type = GL_TRIANGLES; break; \ case 4: type = GL_QUADS; swap(vertex, verts.last(), verts[verts.length()-2]); break; \ } \ addstrip(type, striptex, firstindex, len); \ floorstrip = deltastrip = false; \ } void finishstrips() { stripend(); } sqr sbright, sdark; VARP(lighterror,1,8,100); void render_flat(int wtex, int x, int y, int size, int h, sqr *l1, sqr *l2, sqr *l3, sqr *l4, bool isceil) // floor/ceil quads { if(showm) { l3 = l1 = &sbright; l4 = l2 = &sdark; } int sx, sy; lookuptexture(wtex, sx, sy); float xf = TEXTURESCALE/sx; float yf = TEXTURESCALE/sy; float xs = size*xf; float ys = size*yf; float xo = xf*x; float yo = yf*y; bool first = !floorstrip || y!=oy+size || striptex!=wtex || h!=oh || x!=ox; if(first) // start strip here { stripend(); firstindex = verts.length(); striptex = wtex; oh = h; ox = x; floorstrip = true; if(isceil) { vert(x+size, y, h, l2, xo+xs, yo); vert(x, y, h, l1, xo, yo); } else { vert(x, y, h, l1, xo, yo); vert(x+size, y, h, l2, xo+xs, yo); } ol3r = l1->r; ol3g = l1->g; ol3b = l1->b; ol4r = l2->r; ol4g = l2->g; ol4b = l2->b; } else // continue strip { int lighterr = lighterror*2; if((abs(ol3r-l3->r)r)g)g)b)b)r; ol3g = l1->g; ol3b = l1->b; ol4r = l2->r; ol4g = l2->g; ol4b = l2->b; } if(isceil) { vert(x+size, y+size, h3, l3, xo+xs, yo+ys); vert(x, y+size, h4, l4, xo, yo+ys); } else { vert(x, y+size, h4, l4, xo, yo+ys); vert(x+size, y+size, h3, l3, xo+xs, yo+ys); } oy = y; nquads++; } 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 { stripend(); int sx, sy; lookuptexture(h->ftex, sx, sy); float xf = TEXTURESCALE/sx; float yf = TEXTURESCALE/sy; vert(x1, y1, h->floor, l1, xf*x1, yf*y1); vert(x2, y2, h->floor, l2, xf*x2, yf*y2); vert(x3, y3, h->floor, l3, xf*x3, yf*y3); addstrip(mergestrips ? GL_TRIANGLES : GL_TRIANGLE_STRIP, h->ftex, verts.length()-3, 3); lookuptexture(h->ctex, sx, sy); xf = TEXTURESCALE/sx; yf = TEXTURESCALE/sy; vert(x3, y3, h->ceil, l3, xf*x3, yf*y3); vert(x2, y2, h->ceil, l2, xf*x2, yf*y2); vert(x1, y1, h->ceil, l1, xf*x1, yf*y1); addstrip(mergestrips ? GL_TRIANGLES : GL_TRIANGLE_STRIP, h->ctex, verts.length()-3, 3); nquads++; } void render_tris(int x, int y, int size, bool topleft, sqr *h1, sqr *h2, sqr *s, sqr *t, sqr *u, sqr *v) { if(topleft) { if(h1) render_2tris(h1, s, x+size, y+size, x, y+size, x, y, u, v, s); if(h2) render_2tris(h2, s, x, y, x+size, y, x+size, y+size, s, t, v); } else { if(h1) render_2tris(h1, s, x, y, x+size, y, x, y+size, s, t, u); if(h2) render_2tris(h2, s, x+size, y, x+size, y+size, x, y+size, t, u, v); } } 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) // wall quads { stripend(); if(showm) { l1 = &sbright; l2 = &sdark; } int sx, sy; lookuptexture(wtex, sx, sy); float xf = TEXTURESCALE/sx; float yf = TEXTURESCALE/sy; float xs = size*xf; float xo = xf*(x1==x2 ? min(y1,y2) : min(x1,x2)); if(!flip) { vert(x2, y2, ceil2, l2, xo+xs, -yf*ceil2); vert(x1, y1, ceil1, l1, xo, -yf*ceil1); if(mergestrips) vert(x1, y1, floor1, l1, xo, -floor1*yf); vert(x2, y2, floor2, l2, xo+xs, -floor2*yf); if(!mergestrips) vert(x1, y1, floor1, l1, xo, -floor1*yf); } else { vert(x1, y1, ceil1, l1, xo, -yf*ceil1); vert(x2, y2, ceil2, l2, xo+xs, -yf*ceil2); if(mergestrips) vert(x2, y2, floor2, l2, xo+xs, -floor2*yf); vert(x1, y1, floor1, l1, xo, -floor1*yf); if(!mergestrips) vert(x2, y2, floor2, l2, xo+xs, -floor2*yf); } addstrip(mergestrips ? GL_QUADS : GL_TRIANGLE_STRIP, wtex, verts.length()-4, 4); nquads++; } int wx1, wy1, wx2, wy2; VARP(watersubdiv, 1, 4, 64); VARF(waterlevel, -128, -128, 127, if(!noteditmode()) hdr.waterlevel = waterlevel); void setwatercolor(char *r, char *g, char *b, char *a) { if(r[0]) { hdr.watercolor[0] = ATOI(r); hdr.watercolor[1] = ATOI(g); hdr.watercolor[2] = ATOI(b); hdr.watercolor[3] = a[0] ? ATOI(a) : 178; } else { hdr.watercolor[0] = 25; hdr.watercolor[1] = 76; hdr.watercolor[2] = 102; hdr.watercolor[3] = 178; } } COMMANDN(watercolour, setwatercolor, ARG_4STR); // renders water for bounding rect area that contains water... simple but very inefficient #define VERTW(vertw, body) \ inline void vertw(float v1, float v2, float v3, float t) \ { \ float angle = v1*v2*0.1f + t; \ float h = 0.3f*sinf(angle); \ body; \ glVertex3f(v1, v2, v3+h); \ } #define VERTWT(vertwt, body) VERTW(vertwt, { float v = cosf(angle); float duv = 0.2f*v; body; }) VERTW(vertw, {}) VERTW(vertwc, { float v = cosf(angle); glColor4ub(hdr.watercolor[0], hdr.watercolor[1], hdr.watercolor[2], (uchar)(hdr.watercolor[3] + (max(v, 0) - 0.5f)*51.0f)); }) VERTWT(vertwt, { glTexCoord3f(v1+duv, v2+duv, v3+h); }) VERTWT(vertwtc, { glColor4f(1, 1, 1, 0.15f + max(v, 0)*0.15f); glTexCoord3f(v1+duv, v2+duv, v3+h); }) VERTWT(vertwmtc, { glColor4f(1, 1, 1, 0.15f + max(v, 0)*0.15f); glMultiTexCoord3f_(GL_TEXTURE0_ARB, v1-duv, v2+duv, v3+h); glMultiTexCoord3f_(GL_TEXTURE1_ARB, v1+duv, v2+duv, v3+h); }) #define renderwaterstrips(vertw, hf, t) \ for(int x = wx1; x=2 && reflecttex) { if(refracttex) { setupmultitexrefract(reflecttex, refracttex); renderwaterstrips(vertwmtc, hf, t); } else { setupmultitexreflect(reflecttex); glDepthMask(GL_FALSE); glEnable(GL_BLEND); glBlendFunc(GL_ONE, GL_SRC_ALPHA); renderwaterstrips(vertwtc, hf, t); glDisable(GL_BLEND); glDepthMask(GL_TRUE); } cleanupmultitex(reflecttex, refracttex); return nquads; } if(!refracttex) { glDisable(GL_TEXTURE_2D); glEnable(GL_BLEND); } glDepthMask(GL_FALSE); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); if(reflecttex) { if(!refracttex) { glColor4ubv(hdr.watercolor); renderwaterstrips(vertw, hf, t); glEnable(GL_TEXTURE_2D); } setprojtexmatrix(); glBindTexture(GL_TEXTURE_2D, refracttex ? refracttex : reflecttex); } if(refracttex) { glColor3f(1, 1, 1); renderwaterstrips(vertwt, hf, t); glEnable(GL_BLEND); glBindTexture(GL_TEXTURE_2D, reflecttex); glDepthMask(GL_TRUE); } if(reflecttex) { renderwaterstrips(vertwtc, hf, t); } else { renderwaterstrips(vertwc, hf, t); } if(reflecttex) { glLoadIdentity(); glMatrixMode(GL_MODELVIEW); } else glEnable(GL_TEXTURE_2D); glDisable(GL_BLEND); if(!refracttex) glDepthMask(GL_TRUE); return nquads; } void addwaterquad(int x, int y, int size) // update bounding rect that contains water { int x2 = x+size; int y2 = y+size; if(wx1<0) { wx1 = x; wy1 = y; wx2 = x2; wy2 = y2; } else { if(xwx2) wx2 = x2; if(y2>wy2) wy2 = y2; } } void resetcubes() { verts.setsizenodelete(0); floorstrip = deltastrip = false; if(!reflecting) wx1 = -1; nquads = 0; sbright.r = sbright.g = sbright.b = 255; sdark.r = sdark.g = sdark.b = 0; }