Files
assaultcube-/source/src/rendercubes.cpp
T
ac-stef 41fa9e1b34 add command for raw scripted editing
selectionwalk action beginsel endsel

  The command iterates over all cubes in all selections and executes
  "action" for each cube. The cubes of every selection are framed by
  executions of "beginsel" and "endsel". The action script is allowed
  to make changes to cube attributes, and if it does, an undo point is
  automatically added. "beginsel" and "endsel" are optional.

  While the scripts are executed, several aliases provide further info:

  sw_cursel
    Position and size of the current selection. Valid in all three
    scripts. Holds four integer values for x, y, xs, ys. See command
    "selection" for details.

  (all following aliases are only valid during the execution of the action
   script and provide the attributes of one cube of the map)

  sw_abs_x
  sw_abs_y
  sw_rel_x
  sw_rel_y
    Absolute and relative (within the current selection) coordinates of
    the current cube. Read-only values.

  sw_type
    Type of the current cube. Can be 0..4 for SOLID, CORNER, FHF, CHF and
    SPACE. May be changed to other numerical value or keyword.

  sw_floor
  sw_ceil
    Floor and ceiling heights of the current cube. Range -128..127

  sw_wtex
  sw_ftex
  sw_ctex
  sw_utex
    Texture slot indices for wall, floor, ceiling and upper wall of the
    current cube.

  sw_vdelta
  sw_tag
    Vdelta and tag values of current cube.

  sw_r
  sw_g
  sw_b
    Current light values of current cube. Can be edited, but only with
    temporary effect. Will be reset with the next light recalc.

The command is mostly useful for statistics scripts (like counting cubes
with certain attributes), but is can also be used for scripted editing,
as the following (useless) example shows:

  selectionwalk [ += sw_floor (rnd 2) ]

Further changes:

  The command listoptions now also can return the names of cube types:

    echo (listoptions cubetypes)

  The command countwalls is now implemented as a script instead of a
  builtin command. The script works better, because it doesn't create
  unnecessary undo points.

DOCREF selectionwalk listoptions countwalls
2016-08-27 21:58:02 +02:00

605 lines
17 KiB
C++

// rendercubes.cpp: sits in between worldrender.cpp and rendergl.cpp and fills the vertex array for different cube surfaces.
#include "cube.h"
vector<vertex> 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<GLint> first; vector<GLsizei> count; };
struct stripbatch
{
int tex;
strips tris, tristrips, quads;
};
stripbatch skystrips = { DEFAULT_SKY };
stripbatch stripbatches[256];
uchar renderedtex[256];
int renderedtexs = 0;
extern int ati_mda_bug;
#define RENDERSTRIPS(strips, type) \
if(strips.first.length()) \
{ \
if(hasMDA && !ati_mda_bug) 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.setsize(0); \
strips.count.setsize(0); \
}
void renderstripssky()
{
if(skystrips.tris.first.empty() && skystrips.tristrips.first.empty() && skystrips.quads.first.empty()) return;
glDisable(GL_TEXTURE_2D);
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
RENDERSTRIPS(skystrips.tris, GL_TRIANGLES);
RENDERSTRIPS(skystrips.tristrips, GL_TRIANGLE_STRIP);
RENDERSTRIPS(skystrips.quads, GL_QUADS);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glEnable(GL_TEXTURE_2D);
}
void renderstrips()
{
loopj(renderedtexs)
{
stripbatch &sb = stripbatches[j];
glBindTexture(GL_TEXTURE_2D, lookupworldtexture(sb.tex)->id);
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;
loopi(n) skyfloor = min(skyfloor, verts[start + i].z);
}
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; \
}
enum
{
STRIP_FLOOR = 1,
STRIP_DELTA,
STRIP_WALL
};
int nquads;
// for testing purpose. UNDOME on release.
const float texturescale = 32.0f;
//VARP(texturescale, 16, 32, 64);
#define TEXTURESCALE (float(texturescale) * ((uniformtexres && t->scale>1.0f) ? 1.0f : t->scale))
int striptype = 0, striptex, oh, oy, ox, odir; // the o* vars are used by the stripification
int ol1r, ol1g, ol1b, ol2r, ol2g, ol2b;
float ofloor, oceil;
bool ohf;
int firstindex;
bool showm = false, showef = false;
COMMANDF(showmip, "",() { showm = !showm; });
COMMANDF(showfocuscubedetails, "",() { showef = !showef; });
const char *cubetypenames[] = {"SOLID", "CORNER", "FHF", "CHF", "SPACE", ""};
const char *cubetypename(int t) { return t >= 0 && t < SEMISOLID ? cubetypenames[t] : "unknown"; }
void mipstats(const int a[]) { if(showm && !showef) hudeditf(HUDMSG_MIPSTATS, "1x1/2x2/4x4/8x8: %d / %d / %d / %d", a[0], a[1], a[2], a[3]); }
bool editfocusdetails(sqr *s)
{
if(showef)
{
const char *g = SOLID(s) ? "\f4" : "";
hudeditf(HUDMSG_EDITFOCUS, "%s: \fs%s%d..%d\fr W:%d \fs%sU:%d F:%d C:%d v:%d t:0x%X (%02X%02X%02X)\fr", cubetypename(s->type), g, s->floor, s->ceil, s->wtex, g, s->utex, s->ftex, s->ctex, s->vdelta, s->tag, s->r, s->g, s->b);
return true;
}
return false;
}
VAR(mergestrips, 0, 1, 1);
#define stripend(verts) \
if(striptype) \
{ \
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(verts.last(), verts[verts.length()-2]); break; \
} \
addstrip(type, striptex, firstindex, len); \
striptype = 0; \
}
void finishstrips() { stripend(verts); }
sqr sbright, sdark;
VARP(lighterror, 1, 4, 100);
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
{
if(showm) { l3 = l1 = &sbright; l4 = l2 = &sdark; }
Texture *t = lookupworldtexture(wtex);
float xf = TEXTURESCALE/t->xs;
float yf = TEXTURESCALE/t->ys;
float xs = size*xf;
float ys = size*yf;
float xo = xf*x;
float yo = yf*y;
bool first = striptype!=STRIP_FLOOR || x!=ox+size || striptex!=wtex || h!=oh || y!=oy;
if(first) // start strip here
{
stripend(verts);
firstindex = verts.length();
striptex = wtex;
oh = h;
oy = y;
striptype = STRIP_FLOOR;
if(isceil)
{
vert(x, y, h, l1, xo, yo);
vert(x, y+size, h, l2, xo, yo+ys);
}
else
{
vert(x, y+size, h, l2, xo, yo+ys);
vert(x, y, h, l1, xo, yo);
}
ol1r = l1->r;
ol1g = l1->g;
ol1b = l1->b;
ol2r = l2->r;
ol2g = l2->g;
ol2b = l2->b;
}
else // continue strip
{
int lighterr = lighterror*2;
if((iabs(ol1r-l3->r)<lighterr && iabs(ol2r-l4->r)<lighterr // skip vertices if light values are close enough
&& iabs(ol1g-l3->g)<lighterr && iabs(ol2g-l4->g)<lighterr
&& iabs(ol1b-l3->b)<lighterr && iabs(ol2b-l4->b)<lighterr) || !wtex)
{
verts.setsize(verts.length()-2);
nquads--;
}
else
{
uchar *p1 = (uchar *)(&verts[verts.length()-1].r);
ol1r = p1[0];
ol1g = p1[1];
ol1b = p1[2];
uchar *p2 = (uchar *)(&verts[verts.length()-2].r);
ol2r = p2[0];
ol2g = p2[1];
ol2b = p2[2];
}
}
if(isceil)
{
vert(x+size, y, h, l4, xo+xs, yo);
vert(x+size, y+size, h, l3, xo+xs, yo+ys);
}
else
{
vert(x+size, y+size, h, l3, xo+xs, yo+ys);
vert(x+size, y, h, l4, xo+xs, yo);
}
ox = x;
nquads++;
}
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
{
if(showm) { l3 = l1 = &sbright; l4 = l2 = &sdark; }
Texture *t = lookupworldtexture(wtex);
float xf = TEXTURESCALE/t->xs;
float yf = TEXTURESCALE/t->ys;
float xs = size*xf;
float ys = size*yf;
float xo = xf*x;
float yo = yf*y;
bool first = striptype!=STRIP_DELTA || x!=ox+size || striptex!=wtex || y!=oy;
if(first)
{
stripend(verts);
firstindex = verts.length();
striptex = wtex;
oy = y;
striptype = STRIP_DELTA;
if(isceil)
{
vert(x, y, h1, l1, xo, yo);
vert(x, y+size, h2, l2, xo, yo+ys);
}
else
{
vert(x, y+size, h2, l2, xo, yo+ys);
vert(x, y, h1, l1, xo, yo);
}
}
if(isceil)
{
vert(x+size, y, h4, l4, xo+xs, yo);
vert(x+size, y+size, h3, l3, xo+xs, yo+ys);
}
else
{
vert(x+size, y+size, h3, l3, xo+xs, yo+ys);
vert(x+size, y, h4, l4, xo+xs, yo);
}
ox = x;
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(verts);
Texture *t = lookupworldtexture(h->ftex);
float xf = TEXTURESCALE/t->xs;
float yf = TEXTURESCALE/t->ys;
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);
t = lookupworldtexture(h->ctex);
xf = TEXTURESCALE/t->xs;
yf = TEXTURESCALE/t->ys;
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, int dir) // wall quads
{
if(showm) { l1 = &sbright; l2 = &sdark; }
Texture *t = lookupworldtexture(wtex);
float xf = TEXTURESCALE/t->xs;
float yf = -TEXTURESCALE/t->ys;
float xs = size*xf;
float xo = xf*(x1==x2 ? min(y1,y2) : min(x1,x2));
bool first = striptype!=STRIP_WALL || striptex!=wtex || ox!=x1 || oy!=y1 || ofloor!=floor1 || oceil!=ceil1 || odir!=dir,
hf = floor1!=floor2 || ceil1!=ceil2;
if(first)
{
stripend(verts);
firstindex = verts.length();
striptex = wtex;
striptype = STRIP_WALL;
if(!flip)
{
vert(x1, y1, ceil1, l1, xo, yf*ceil1);
vert(x1, y1, floor1, l1, xo, yf*floor1);
}
else
{
vert(x1, y1, floor1, l1, xo, yf*floor1);
vert(x1, y1, ceil1, l1, xo, yf*ceil1);
}
ol1r = l1->r;
ol1g = l1->g;
ol1b = l1->b;
}
else // continue strip
{
int lighterr = lighterror*2;
if((!hf && !ohf)
&& ((iabs(ol1r-l2->r)<lighterr && iabs(ol1g-l2->g)<lighterr && iabs(ol1b-l2->b)<lighterr) || !wtex)) // skip vertices if light values are close enough
{
verts.setsize(verts.length()-2);
nquads--;
}
else
{
uchar *p1 = (uchar *)(&verts[verts.length()-1].r);
ol1r = p1[0];
ol1g = p1[1];
ol1b = p1[2];
}
}
if(!flip)
{
vert(x2, y2, ceil2, l2, xo+xs, yf*ceil2);
vert(x2, y2, floor2, l2, xo+xs, yf*floor2);
}
else
{
vert(x2, y2, floor2, l2, xo+xs, yf*floor2);
vert(x2, y2, ceil2, l2, xo+xs, yf*ceil2);
}
ox = x2;
oy = y2;
ofloor = floor2;
oceil = ceil2;
odir = dir;
ohf = hf;
nquads++;
}
void resetcubes()
{
verts.setsize(0);
striptype = 0;
nquads = 0;
sbright.r = sbright.g = sbright.b = 255;
sdark.r = sdark.g = sdark.b = 0;
resetwater();
}
struct shadowvertex { float u, v, x, y, z; };
vector<shadowvertex> shadowverts;
static void resetshadowverts()
{
shadowverts.setsize(0);
striptype = 0;
}
static void rendershadowstrips()
{
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
shadowvertex *buf = shadowverts.getbuf();
glVertexPointer(3, GL_FLOAT, sizeof(shadowvertex), &buf->x);
glTexCoordPointer(2, GL_FLOAT, sizeof(shadowvertex), &buf->u);
loopj(renderedtexs)
{
stripbatch &sb = stripbatches[j];
RENDERSTRIPS(sb.tris, GL_TRIANGLES);
RENDERSTRIPS(sb.tristrips, GL_TRIANGLE_STRIP);
RENDERSTRIPS(sb.quads, GL_QUADS);
}
renderedtexs = 0;
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
xtraverts += shadowverts.length();
}
#define shadowvert(v1, v2, v3) \
{ \
shadowvertex &v = shadowverts.add(); \
v.x = (float)(v1); v.y = (float)(v2); v.z = (float)(v3); \
}
void rendershadow_tri(sqr *h, int x1, int y1, int x2, int y2, int x3, int y3) // floor tris on a corner cube
{
stripend(shadowverts);
shadowvert(x1, y1, h->floor);
shadowvert(x2, y2, h->floor);
shadowvert(x3, y3, h->floor);
addstrip(mergestrips ? GL_TRIANGLES : GL_TRIANGLE_STRIP, DEFAULT_FLOOR, shadowverts.length()-3, 3);
}
void rendershadow_tris(int x, int y, bool topleft, sqr *h1, sqr *h2)
{
if(topleft)
{
if(h1) rendershadow_tri(h1, x+1, y+1, x, y+1, x, y);
if(h2) rendershadow_tri(h2, x, y, x+1, y, x+1, y+1);
}
else
{
if(h1) rendershadow_tri(h1, x, y, x+1, y, x, y+1);
if(h2) rendershadow_tri(h2, x+1, y, x+1, y+1, x, y+1);
}
}
static void rendershadow_flat(int x, int y, int h) // floor quads
{
bool first = striptype!=STRIP_FLOOR || x!=ox+1 || h!=oh || y!=oy;
if(first) // start strip here
{
stripend(shadowverts);
firstindex = shadowverts.length();
striptex = DEFAULT_FLOOR;
oh = h;
oy = y;
striptype = STRIP_FLOOR;
shadowvert(x, y+1, h);
shadowvert(x, y, h);
}
else // continue strip
{
shadowverts.setsize(shadowverts.length()-2);
}
shadowvert(x+1, y+1, h);
shadowvert(x+1, y, h);
ox = x;
}
static void rendershadow_flatdelta(int x, int y, float h1, float h4, float h3, float h2) // floor quads on a slope
{
bool first = striptype!=STRIP_DELTA || x!=ox+1 || y!=oy;
if(first)
{
stripend(shadowverts);
firstindex = shadowverts.length();
striptex = DEFAULT_FLOOR;
oy = y;
striptype = STRIP_DELTA;
shadowvert(x, y+1, h2);
shadowvert(x, y, h1);
}
shadowvert(x+1, y+1, h3);
shadowvert(x+1, y, h4);
ox = x;
}
void rendershadow(int x, int y, int xs, int ys, const vec &texgenS, const vec &texgenT)
{
x = max(x, 1);
y = max(y, 1);
xs = min(xs, ssize-1);
ys = min(ys, ssize-1);
resetshadowverts();
#define df(x) s->floor-(x->vdelta/4.0f)
sqr *w = wmip[0];
for(int yy = y; yy<ys; yy++) for(int xx = x; xx<xs; xx++)
{
sqr *s = SW(w,xx,yy);
if(s->type==SPACE || s->type==CHF)
{
rendershadow_flat(xx, yy, s->floor);
}
else if(s->type==FHF)
{
sqr *t = SW(s,1,0), *u = SW(s,1,1), *v = SW(s,0,1);
rendershadow_flatdelta(xx, yy, df(s), df(t), df(u), df(v));
}
else if(s->type==CORNER)
{
sqr *t = SW(s,1,0), *v = SW(s,0,1), *w = SW(s,0,-1), *z = SW(s,-1,0);
bool topleft = true;
sqr *h1 = NULL, *h2 = NULL;
if(SOLID(z))
{
if(SOLID(w)) { h2 = s; topleft = false; }
else if(SOLID(v)) { h2 = s; }
}
else if(SOLID(t))
{
if(SOLID(w)) { h1 = s; }
else if(SOLID(v)) { h1 = s; topleft = false; }
}
else
{
bool wv = w->ceil-w->floor < v->ceil-v->floor;
if(z->ceil-z->floor < t->ceil-t->floor)
{
if(wv) { h1 = s; h2 = v; topleft = false; }
else { h1 = s; h2 = w; }
}
else
{
if(wv) { h2 = s; h1 = v; }
else { h2 = s; h1 = w; topleft = false; }
}
}
rendershadow_tris(xx, yy, topleft, h1, h2);
}
}
stripend(shadowverts);
for(shadowvertex *v = shadowverts.getbuf(), *end = &v[shadowverts.length()]; v < end; v++)
{
float vx = v->x, vy = v->y;
v->u = vx*texgenS.x + vy*texgenS.y + texgenS.z;
v->v = vx*texgenT.x + vy*texgenT.y + texgenT.z;
}
rendershadowstrips();
}